544a6ddb77
Resolves the 35 findings from the 2026-06-01 baseline (commit 26ba1c7),
test-first for every behavioral change. +51 tests (331 -> 382 passing, 0 failed).
- Telemetry-001 (HIGH): RedactionEnricher now honours property removal, so a
redactor that drops a key actually scrubs the secret from the event.
- Auth: LDAP validator ValidateOnStart; API-key verify no longer fails on a
best-effort MarkUsed write or a corrupt scopes column (fail-closed); LDAP cert
validation hook; KeyPrefix persistence aligned; README algorithm corrected.
- Health: Akka checks return Degraded (not throw) when the cluster isn't up yet;
GrpcDependencyHealthCheck catch-all; null 'description' rendered; composite
endpoint builder; XML docs shipped.
- Audit: CompositeAuditWriter no longer re-throws OperationCanceledException;
TruncatingAuditRedactor over-redact scrubs Target + safe negative max; options
record; XML docs shipped.
- Configuration: TryAddEnumerable idempotent registration; consistent port
quoting; strict invariant port parsing; XML docs + README packaged.
- Theme: mobile toggle is now CSS-only (no Bootstrap JS); token/CSS hygiene;
XML docs on the public parameter surface.
Shared-contract/spec docs updated where the code was the source of truth
(observability service.instance.id, MapZbMetrics, redactor reach). All changes
additive/back-compatible at v0.1.0. code-reviews bookkeeping follows separately.
115 lines
5.0 KiB
C#
115 lines
5.0 KiB
C#
using Microsoft.AspNetCore.Builder;
|
|
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
|
using Microsoft.AspNetCore.Routing;
|
|
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
|
|
|
namespace ZB.MOM.WW.Health;
|
|
|
|
/// <summary>
|
|
/// Maps the canonical ZB.MOM.WW three-tier health endpoints in one call.
|
|
/// </summary>
|
|
public static class ZbHealthEndpointExtensions
|
|
{
|
|
/// <summary>
|
|
/// Maps the three health tiers:
|
|
/// <list type="bullet">
|
|
/// <item><description><c>/health/ready</c> — runs only checks tagged <see cref="ZbHealthTags.Ready"/>.</description></item>
|
|
/// <item><description><c>/health/active</c> — runs only checks tagged <see cref="ZbHealthTags.Active"/>.</description></item>
|
|
/// <item><description><c>/healthz</c> — bare process liveness; runs no checks (always 200 while the process is up).</description></item>
|
|
/// </list>
|
|
/// All three are anonymous. Status mapping is the ASP.NET Core default:
|
|
/// Healthy/Degraded → 200, Unhealthy → 503.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Does NOT call <c>services.AddHealthChecks()</c> — the caller registers probes and their tags.
|
|
/// The readiness and active tiers use the canonical JSON writer
|
|
/// (<see cref="ZbHealthWriter.WriteJsonAsync"/>) unless overridden via
|
|
/// <see cref="ZbHealthEndpointOptions.ResponseWriter"/>. The liveness tier runs no checks and
|
|
/// emits a minimal <c>200 OK</c> body.
|
|
/// </remarks>
|
|
/// <returns>
|
|
/// A composite <see cref="IEndpointConventionBuilder"/> that fans every chained convention out to
|
|
/// <em>all three</em> health endpoints (readiness, active, and liveness). For example,
|
|
/// <c>endpoints.MapZbHealth().RequireHost("…")</c> gates all three endpoints, as a caller expects.
|
|
/// </returns>
|
|
public static IEndpointConventionBuilder MapZbHealth(
|
|
this IEndpointRouteBuilder endpoints,
|
|
ZbHealthEndpointOptions? options = null)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(endpoints);
|
|
options ??= new ZbHealthEndpointOptions();
|
|
|
|
var responseWriter = options.ResponseWriter ?? ZbHealthWriter.WriteJsonAsync;
|
|
|
|
var ready = endpoints.MapHealthChecks(options.ReadyPath, new HealthCheckOptions
|
|
{
|
|
Predicate = static c => c.Tags.Contains(ZbHealthTags.Ready),
|
|
ResponseWriter = responseWriter,
|
|
}).AllowAnonymous();
|
|
|
|
var active = endpoints.MapHealthChecks(options.ActivePath, new HealthCheckOptions
|
|
{
|
|
Predicate = static c => c.Tags.Contains(ZbHealthTags.Active),
|
|
ResponseWriter = responseWriter,
|
|
}).AllowAnonymous();
|
|
|
|
// Liveness: run no checks. The endpoint returns 200 as long as the process can respond.
|
|
// No JSON writer — the empty report would carry no useful data, so the framework default
|
|
// (a minimal plain-text body) is sufficient.
|
|
var live = endpoints.MapHealthChecks(options.LivePath, new HealthCheckOptions
|
|
{
|
|
Predicate = static _ => false,
|
|
}).AllowAnonymous();
|
|
|
|
return new CompositeEndpointConventionBuilder(ready, active, live);
|
|
}
|
|
|
|
/// <summary>
|
|
/// An <see cref="IEndpointConventionBuilder"/> that forwards each convention to several
|
|
/// underlying builders, so conventions chained onto the result of
|
|
/// <see cref="MapZbHealth(IEndpointRouteBuilder, ZbHealthEndpointOptions?)"/> apply to all three
|
|
/// health endpoints rather than just one.
|
|
/// </summary>
|
|
private sealed class CompositeEndpointConventionBuilder : IEndpointConventionBuilder
|
|
{
|
|
private readonly IEndpointConventionBuilder[] _builders;
|
|
|
|
public CompositeEndpointConventionBuilder(params IEndpointConventionBuilder[] builders) =>
|
|
_builders = builders;
|
|
|
|
public void Add(Action<EndpointBuilder> convention)
|
|
{
|
|
foreach (var builder in _builders)
|
|
builder.Add(convention);
|
|
}
|
|
|
|
public void Finally(Action<EndpointBuilder> finalConvention)
|
|
{
|
|
foreach (var builder in _builders)
|
|
builder.Finally(finalConvention);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Maps the three health tiers, configuring options inline. See the other
|
|
/// <see cref="MapZbHealth(IEndpointRouteBuilder, ZbHealthEndpointOptions?)"/> overload for tier semantics.
|
|
/// </summary>
|
|
/// <param name="endpoints">The endpoint route builder to map onto.</param>
|
|
/// <param name="configure">Callback that mutates a fresh <see cref="ZbHealthEndpointOptions"/>.</param>
|
|
/// <returns>
|
|
/// A composite <see cref="IEndpointConventionBuilder"/> that fans chained conventions out to all
|
|
/// three health endpoints.
|
|
/// </returns>
|
|
public static IEndpointConventionBuilder MapZbHealth(
|
|
this IEndpointRouteBuilder endpoints,
|
|
Action<ZbHealthEndpointOptions> configure)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(endpoints);
|
|
ArgumentNullException.ThrowIfNull(configure);
|
|
|
|
var options = new ZbHealthEndpointOptions();
|
|
configure(options);
|
|
return endpoints.MapZbHealth(options);
|
|
}
|
|
}
|