feat(audit): production gRPC IPullAuditEventsClient for site reconciliation
This commit is contained in:
@@ -362,4 +362,69 @@ public static class ServiceCollectionExtensions
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Audit Log (#23) M6 — central-only registration of the production
|
||||
/// <see cref="IPullAuditEventsClient"/> (<see cref="GrpcPullAuditEventsClient"/>)
|
||||
/// and its unary-call invoker (<see cref="GrpcPullAuditEventsInvoker"/>) used
|
||||
/// by <see cref="SiteAuditReconciliationActor"/> to pull reconciliation
|
||||
/// batches from each site over the <c>PullAuditEvents</c> gRPC RPC.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Kept out of <see cref="AddAuditLog"/> — which also runs on site
|
||||
/// composition roots — because the client dials sites and resolves
|
||||
/// <see cref="ISiteEnumerator"/> (a central-only collaborator wired
|
||||
/// alongside the reconciliation singleton). Folding it into
|
||||
/// <see cref="AddAuditLog"/> would register a site-dialing client on every
|
||||
/// site host, violating the "every <c>Add*</c> call is safe from any
|
||||
/// composition root" invariant. This helper is the central analogue of
|
||||
/// <see cref="AddAuditLogCentralMaintenance"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The <see cref="GrpcPullAuditEventsInvoker"/> binds with default
|
||||
/// <see cref="ZB.MOM.WW.ScadaBridge.Communication.CommunicationOptions"/>
|
||||
/// keepalive unless an <c>IOptions<CommunicationOptions></c> is
|
||||
/// already registered, in which case the configured timings flow through —
|
||||
/// matching how <c>SiteStreamGrpcClientFactory</c> takes its keepalive from
|
||||
/// the same options.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <see cref="ISiteEnumerator"/> is NOT registered here: its production
|
||||
/// implementation (wrapping <c>ISiteRepository</c>) ships with the
|
||||
/// reconciliation-singleton wiring in the Host. The client resolves the
|
||||
/// enumerator lazily at actor-construction time, so this binding is safe to
|
||||
/// issue before the enumerator binding lands.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="services">The service collection to register into.</param>
|
||||
/// <returns>The same <see cref="IServiceCollection"/> for chaining.</returns>
|
||||
public static IServiceCollection AddAuditLogCentralReconciliationClient(
|
||||
this IServiceCollection services)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
|
||||
// The invoker owns the per-endpoint GrpcChannel cache, so it must be a
|
||||
// singleton — a fresh invoker per resolution would leak channels.
|
||||
// Resolve CommunicationOptions if present (the central Host binds it),
|
||||
// otherwise fall back to defaults so this helper stays standalone.
|
||||
services.TryAddSingleton<GrpcPullAuditEventsInvoker>(sp =>
|
||||
{
|
||||
var options = sp
|
||||
.GetService<Microsoft.Extensions.Options.IOptions<
|
||||
ZB.MOM.WW.ScadaBridge.Communication.CommunicationOptions>>();
|
||||
return options is null
|
||||
? new GrpcPullAuditEventsInvoker()
|
||||
: new GrpcPullAuditEventsInvoker(options.Value);
|
||||
});
|
||||
services.TryAddSingleton<GrpcPullAuditEventsClient.IPullAuditEventsInvoker>(
|
||||
sp => sp.GetRequiredService<GrpcPullAuditEventsInvoker>());
|
||||
|
||||
services.TryAddSingleton<IPullAuditEventsClient>(sp => new GrpcPullAuditEventsClient(
|
||||
sp.GetRequiredService<ISiteEnumerator>(),
|
||||
sp.GetRequiredService<GrpcPullAuditEventsClient.IPullAuditEventsInvoker>(),
|
||||
sp.GetRequiredService<ILogger<GrpcPullAuditEventsClient>>()));
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user