using Dapper; using FastEndpoints; using Npgsql; using Kbx.Shared.ExternalData.Generated; using Kbx.Shared.Runtime; namespace Kbx.Modules.Common.ExternalData.Status; public sealed record Row(string DatasetId,string ProviderId,string SourceLabel,string State,long CacheEntries,DateTimeOffset? LastReceivedAt,DateTimeOffset? OldestFreshUntil,long StaleEntries,long UnavailableEntries); public sealed record Response(IReadOnlyList Items); file sealed record DbRow(string DatasetId,string ProviderId,string State,long CacheEntries,DateTimeOffset? LastReceivedAt,DateTimeOffset? OldestFreshUntil,long StaleEntries,long UnavailableEntries); public sealed class Endpoint(NpgsqlDataSource dataSource):EndpointWithoutRequest { public override void Configure(){Get("/api/kbx/external-data/status");Permissions("common.external-data.read");} public override async Task HandleAsync(CancellationToken ct) { const string sql=""" select dataset_id as DatasetId, provider_id as ProviderId, case when bool_or(fresh_until is not null and now() <= fresh_until) then 'fresh' when bool_or(usable_until is not null and now() <= usable_until) then 'stale' when bool_or(state='unavailable') then 'unavailable' else 'expired' end as State, count(*) as CacheEntries, max(received_at) as LastReceivedAt, min(fresh_until) as OldestFreshUntil, count(*) filter(where fresh_until is not null and now()>fresh_until and usable_until is not null and now()<=usable_until) as StaleEntries, count(*) filter(where state='unavailable') as UnavailableEntries from kbx.external_data_cache where tenant_id=@TenantId group by dataset_id,provider_id """; await using var connection=await dataSource.OpenConnectionAsync(ct); var db=(await connection.QueryAsync(new CommandDefinition(sql,new{TenantId=RuntimeIdentity.TenantId(User)},cancellationToken:ct))).ToDictionary(x=>x.DatasetId,StringComparer.Ordinal); var rows=KbxExternalDataCatalog.All.Values.OrderBy(x=>x.Id,StringComparer.Ordinal).Select(dataset=>{ if(db.TryGetValue(dataset.Id,out var current))return new Row(dataset.Id,dataset.ProviderId,dataset.SourceLabel,current.State,current.CacheEntries,current.LastReceivedAt,current.OldestFreshUntil,current.StaleEntries,current.UnavailableEntries); var state=dataset.FreshnessMode=="provider-defined" && dataset.FreshForSeconds is null ? "unavailable" : "expired"; return new Row(dataset.Id,dataset.ProviderId,dataset.SourceLabel,state,0,null,null,0,state=="unavailable"?1:0); }).ToArray(); await Send.OkAsync(new Response(rows),ct); } }