feat(lmxproxy): phase 1 — v2 protocol types and domain model

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-03-21 23:41:56 -04:00
parent 08d2a07d8b
commit 0d63fb1105
87 changed files with 3389 additions and 956 deletions

View File

@@ -0,0 +1,241 @@
using System;
using System.IO;
using Microsoft.Extensions.Logging;
namespace ZB.MOM.WW.LmxProxy.Client
{
/// <summary>
/// Builder for creating configured instances of LmxProxyClient
/// </summary>
public class LmxProxyClientBuilder
{
private string? _host;
private int _port = 5050;
private string? _apiKey;
private ILogger<LmxProxyClient>? _logger;
private TimeSpan _defaultTimeout = TimeSpan.FromSeconds(30);
private int _maxRetryAttempts = 3;
private TimeSpan _retryDelay = TimeSpan.FromSeconds(1);
private bool _enableMetrics;
private string? _correlationIdHeader;
private ClientTlsConfiguration? _tlsConfiguration;
/// <summary>
/// Sets the host address for the LmxProxy service
/// </summary>
/// <param name="host">The host address</param>
/// <returns>The builder instance for method chaining</returns>
public LmxProxyClientBuilder WithHost(string host)
{
if (string.IsNullOrWhiteSpace(host))
throw new ArgumentException("Host cannot be null or empty", nameof(host));
_host = host;
return this;
}
/// <summary>
/// Sets the port for the LmxProxy service
/// </summary>
/// <param name="port">The port number</param>
/// <returns>The builder instance for method chaining</returns>
public LmxProxyClientBuilder WithPort(int port)
{
if (port < 1 || port > 65535)
throw new ArgumentOutOfRangeException(nameof(port), "Port must be between 1 and 65535");
_port = port;
return this;
}
/// <summary>
/// Sets the API key for authentication
/// </summary>
/// <param name="apiKey">The API key</param>
/// <returns>The builder instance for method chaining</returns>
public LmxProxyClientBuilder WithApiKey(string apiKey)
{
_apiKey = apiKey;
return this;
}
/// <summary>
/// Sets the logger instance
/// </summary>
/// <param name="logger">The logger</param>
/// <returns>The builder instance for method chaining</returns>
public LmxProxyClientBuilder WithLogger(ILogger<LmxProxyClient> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
return this;
}
/// <summary>
/// Sets the default timeout for operations
/// </summary>
/// <param name="timeout">The timeout duration</param>
/// <returns>The builder instance for method chaining</returns>
public LmxProxyClientBuilder WithTimeout(TimeSpan timeout)
{
if (timeout <= TimeSpan.Zero)
throw new ArgumentOutOfRangeException(nameof(timeout), "Timeout must be positive");
if (timeout > TimeSpan.FromMinutes(10))
throw new ArgumentOutOfRangeException(nameof(timeout), "Timeout cannot exceed 10 minutes");
_defaultTimeout = timeout;
return this;
}
/// <summary>
/// Enables SSL/TLS with the specified certificate
/// </summary>
/// <param name="certificatePath">Path to the certificate file</param>
/// <returns>The builder instance for method chaining</returns>
public LmxProxyClientBuilder WithSslCredentials(string? certificatePath = null)
{
_tlsConfiguration ??= new ClientTlsConfiguration();
_tlsConfiguration.UseTls = true;
_tlsConfiguration.ServerCaCertificatePath = string.IsNullOrWhiteSpace(certificatePath) ? null : certificatePath;
return this;
}
/// <summary>
/// Applies a full TLS configuration to the client.
/// </summary>
/// <param name="configuration">The TLS configuration to apply.</param>
/// <returns>The builder instance for method chaining.</returns>
public LmxProxyClientBuilder WithTlsConfiguration(ClientTlsConfiguration configuration)
{
_tlsConfiguration = configuration ?? throw new ArgumentNullException(nameof(configuration));
return this;
}
/// <summary>
/// Sets the retry configuration
/// </summary>
/// <param name="maxAttempts">Maximum number of retry attempts</param>
/// <param name="retryDelay">Delay between retries</param>
/// <returns>The builder instance for method chaining</returns>
public LmxProxyClientBuilder WithRetryPolicy(int maxAttempts, TimeSpan retryDelay)
{
if (maxAttempts <= 0)
throw new ArgumentOutOfRangeException(nameof(maxAttempts), "Max attempts must be positive");
if (retryDelay <= TimeSpan.Zero)
throw new ArgumentOutOfRangeException(nameof(retryDelay), "Retry delay must be positive");
_maxRetryAttempts = maxAttempts;
_retryDelay = retryDelay;
return this;
}
/// <summary>
/// Enables metrics collection
/// </summary>
/// <returns>The builder instance for method chaining</returns>
public LmxProxyClientBuilder WithMetrics()
{
_enableMetrics = true;
return this;
}
/// <summary>
/// Sets the correlation ID header name for request tracing
/// </summary>
/// <param name="headerName">The header name for correlation ID</param>
/// <returns>The builder instance for method chaining</returns>
public LmxProxyClientBuilder WithCorrelationIdHeader(string headerName)
{
if (string.IsNullOrEmpty(headerName))
throw new ArgumentException("Header name cannot be null or empty", nameof(headerName));
_correlationIdHeader = headerName;
return this;
}
/// <summary>
/// Builds the configured LmxProxyClient instance
/// </summary>
/// <returns>A configured LmxProxyClient instance</returns>
public LmxProxyClient Build()
{
if (string.IsNullOrWhiteSpace(_host))
throw new InvalidOperationException("Host must be specified");
ValidateTlsConfiguration();
var client = new LmxProxyClient(_host, _port, _apiKey, _tlsConfiguration, _logger)
{
DefaultTimeout = _defaultTimeout
};
// Store additional configuration for future use
client.SetBuilderConfiguration(new ClientConfiguration
{
MaxRetryAttempts = _maxRetryAttempts,
RetryDelay = _retryDelay,
EnableMetrics = _enableMetrics,
CorrelationIdHeader = _correlationIdHeader
});
return client;
}
private void ValidateTlsConfiguration()
{
if (_tlsConfiguration?.UseTls != true)
{
return;
}
if (!string.IsNullOrWhiteSpace(_tlsConfiguration.ServerCaCertificatePath) &&
!File.Exists(_tlsConfiguration.ServerCaCertificatePath))
{
throw new FileNotFoundException(
$"Certificate file not found: {_tlsConfiguration.ServerCaCertificatePath}",
_tlsConfiguration.ServerCaCertificatePath);
}
if (!string.IsNullOrWhiteSpace(_tlsConfiguration.ClientCertificatePath) &&
!File.Exists(_tlsConfiguration.ClientCertificatePath))
{
throw new FileNotFoundException(
$"Client certificate file not found: {_tlsConfiguration.ClientCertificatePath}",
_tlsConfiguration.ClientCertificatePath);
}
if (!string.IsNullOrWhiteSpace(_tlsConfiguration.ClientKeyPath) &&
!File.Exists(_tlsConfiguration.ClientKeyPath))
{
throw new FileNotFoundException(
$"Client key file not found: {_tlsConfiguration.ClientKeyPath}",
_tlsConfiguration.ClientKeyPath);
}
}
}
/// <summary>
/// Internal configuration class for storing builder settings
/// </summary>
internal class ClientConfiguration
{
/// <summary>
/// Gets or sets the maximum number of retry attempts.
/// </summary>
public int MaxRetryAttempts { get; set; }
/// <summary>
/// Gets or sets the retry delay.
/// </summary>
public TimeSpan RetryDelay { get; set; }
/// <summary>
/// Gets or sets a value indicating whether metrics are enabled.
/// </summary>
public bool EnableMetrics { get; set; }
/// <summary>
/// Gets or sets the correlation ID header name.
/// </summary>
public string? CorrelationIdHeader { get; set; }
}
}