using ZB.MOM.WW.OtOpcUa.Commons.Interfaces;
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Admin;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Clients;
///
/// Thin AdminUI-side wrapper for the Test Connect operation. Dispatches a
/// through IAdminOperationsClient, applies a
/// 65-second outer wall (the actor itself clamps to [1,60]s; this guards against the
/// Ask never replying), and surfaces a friendly result for the Razor button to render.
///
public sealed class AdminProbeService
{
private readonly IAdminOperationsClient _client;
private readonly ZB.MOM.WW.Audit.IAuditWriter? _audit;
private readonly Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider? _auth;
private readonly Microsoft.Extensions.Logging.ILogger _logger;
/// Initializes a new instance of the .
/// The admin operations client used to dispatch probe requests.
/// Credential-use audit sink (#835 review I-1); null ⇒ the structured log line only.
/// The circuit's user (for the audit record); null ⇒ "unknown".
/// Logger; null ⇒ none.
public AdminProbeService(
IAdminOperationsClient client,
ZB.MOM.WW.Audit.IAuditWriter? audit = null,
Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider? auth = null,
Microsoft.Extensions.Logging.ILogger? logger = null)
{
_client = client;
_audit = audit;
_auth = auth;
_logger = (Microsoft.Extensions.Logging.ILogger?)logger ?? Microsoft.Extensions.Logging.Abstractions.NullLogger.Instance;
}
///
/// Dispatches a Test Connect probe for the supplied driver type and config JSON,
/// waiting up to 65 seconds for a reply before surfacing a timeout failure.
///
/// Driver type key (must match an installed IDriverProbe.DriverType).
/// Driver config as JSON (same shape as DriverInstance.DriverConfig).
/// Per-probe timeout; actor clamps to [1, 60].
/// Optional cancellation token from the caller.
/// The owning driver instance (lmxopcua#835); null ⇒ a not-yet-created one.
/// The probe result, or a timeout failure if no reply arrives in time.
public async Task TestAsync(
string driverType,
string configJson,
int timeoutSeconds,
CancellationToken ct = default,
string? driverInstanceId = null)
{
var correlationId = Guid.NewGuid();
var msg = new TestDriverConnect(driverType, configJson, timeoutSeconds, correlationId, driverInstanceId);
// 65s outer guard — the actor's CTS clamps to 60s; if the Ask never returns we still want
// a deterministic failure surface for the UI.
using var outerCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
outerCts.CancelAfter(TimeSpan.FromSeconds(65));
TestDriverConnectResult result;
try
{
result = await _client.AskAsync(msg, outerCts.Token);
}
catch (OperationCanceledException)
{
result = new TestDriverConnectResult(false, "Probe request did not return within 65s.", null, correlationId);
}
catch (Exception ex)
{
result = new TestDriverConnectResult(false, $"Probe dispatch failed: {ex.Message}", null, correlationId);
}
// #835 review I-1: who Test-Connected which endpoint with which driver's credential reference, and how it went.
await Audit.CredentialUseAudit.RecordAsync(_audit, _auth, _logger, Audit.CredentialUseAudit.TestConnectAction,
driverType, configJson, driverInstanceId, result.Ok, result.Message).ConfigureAwait(false);
return result;
}
}