feat(health): surface pending-updates counts on the Health dashboard and CLI (ScadaBridge#63)
Health page gains PendingUpdatesHealthTiles (stale instances / sites with stale artifacts, Deployer+Administrator only, site-scope narrowed, shared cache). New GetPendingUpdates management command (Deployer) and 'health pending-updates' CLI leaf. Flatten/cache computation untouched; the command reads IPendingUpdatesEvaluator directly because the UI cache is not reachable from ManagementService. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -32,6 +32,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.InboundApi;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Transport;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Validation;
|
||||
using ZB.MOM.WW.ScadaBridge.DeploymentManager;
|
||||
using ZB.MOM.WW.ScadaBridge.DeploymentManager.PendingUpdates;
|
||||
using ZB.MOM.WW.ScadaBridge.HealthMonitoring;
|
||||
using ZB.MOM.WW.ScadaBridge.Communication;
|
||||
using ZB.MOM.WW.ScadaBridge.Security;
|
||||
@@ -354,6 +355,7 @@ public class ManagementActor : ReceiveActor
|
||||
or GetDeploymentDiffCommand
|
||||
or MgmtDeployArtifactsCommand
|
||||
or QueryDeploymentsCommand
|
||||
or GetPendingUpdatesCommand
|
||||
or RetryParkedMessageCommand or DiscardParkedMessageCommand
|
||||
or DebugSnapshotCommand => DeployerOnly,
|
||||
|
||||
@@ -550,6 +552,7 @@ public class ManagementActor : ReceiveActor
|
||||
// Health
|
||||
GetHealthSummaryCommand => HandleGetHealthSummary(sp),
|
||||
GetSiteHealthCommand cmd => HandleGetSiteHealth(sp, cmd),
|
||||
GetPendingUpdatesCommand => await HandleGetPendingUpdates(sp, user),
|
||||
|
||||
// Remote Queries
|
||||
QueryEventLogsCommand cmd => await HandleQueryEventLogs(sp, cmd, user),
|
||||
@@ -3174,6 +3177,45 @@ public class ManagementActor : ReceiveActor
|
||||
return aggregator.GetAllSiteStates();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pending-updates counts (Gitea #63). Reads the evaluator directly — the Central UI's
|
||||
/// process-wide TTL cache lives in the CentralUI project, which this project cannot
|
||||
/// reference — so each call is one fleet flatten; fine for a CLI/scripting cadence.
|
||||
/// A site-scoped, non-Administrator caller sees only their permitted sites, so the
|
||||
/// totals never include a site they cannot see.
|
||||
/// </summary>
|
||||
private static async Task<object?> HandleGetPendingUpdates(IServiceProvider sp, AuthenticatedUser user)
|
||||
{
|
||||
var evaluator = sp.GetRequiredService<IPendingUpdatesEvaluator>();
|
||||
var snapshot = await evaluator.EvaluateAsync();
|
||||
if (user.PermittedSiteIds.Length > 0 && !user.Roles.Contains(Roles.Administrator, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
var permitted = new HashSet<string>(user.PermittedSiteIds);
|
||||
snapshot = snapshot.ForSites(snapshot.Sites
|
||||
.Where(s => permitted.Contains(s.SiteId.ToString()))
|
||||
.Select(s => s.SiteId).ToHashSet());
|
||||
}
|
||||
|
||||
return new
|
||||
{
|
||||
computedAt = snapshot.ComputedAt,
|
||||
hasPendingUpdates = snapshot.HasPendingUpdates,
|
||||
staleInstanceCount = snapshot.StaleInstanceCount,
|
||||
staleArtifactSiteCount = snapshot.StaleArtifactSiteCount,
|
||||
sites = snapshot.Sites.Select(s => new
|
||||
{
|
||||
siteId = s.SiteId,
|
||||
siteIdentifier = s.SiteIdentifier,
|
||||
siteName = s.SiteName,
|
||||
artifactState = s.ArtifactState.ToString(),
|
||||
artifactsDeployedAt = s.ArtifactsDeployedAt,
|
||||
deployedInstanceCount = s.DeployedInstanceCount,
|
||||
staleInstanceCount = s.StaleInstanceCount,
|
||||
unknownInstanceCount = s.UnknownInstanceCount,
|
||||
}).ToList(),
|
||||
};
|
||||
}
|
||||
|
||||
private static object? HandleGetSiteHealth(IServiceProvider sp, GetSiteHealthCommand cmd)
|
||||
{
|
||||
var aggregator = sp.GetRequiredService<ICentralHealthAggregator>();
|
||||
|
||||
Reference in New Issue
Block a user