using System; using System.Collections.Generic; using System.Linq; using Serilog; namespace ZB.MOM.WW.LmxProxy.Host.Configuration { /// /// Validates LmxProxy configuration settings on startup. /// public static class ConfigurationValidator { private static readonly ILogger Logger = Log.ForContext(typeof(ConfigurationValidator)); /// /// Validates the provided configuration and returns a list of validation errors. /// /// The configuration to validate. /// A list of validation error messages. Empty if configuration is valid. public static List Validate(LmxProxyConfiguration configuration) { var errors = new List(); if (configuration == null) { errors.Add("Configuration is null"); return errors; } // Validate gRPC port if (configuration.GrpcPort <= 0 || configuration.GrpcPort > 65535) { errors.Add($"Invalid gRPC port: {configuration.GrpcPort}. Must be between 1 and 65535."); } // Validate API key configuration file if (string.IsNullOrWhiteSpace(configuration.ApiKeyConfigFile)) { errors.Add("API key configuration file path is not specified."); } // Validate Connection settings if (configuration.Connection != null) { ValidateConnectionConfiguration(configuration.Connection, errors); } else { errors.Add("Connection configuration is missing."); } // Validate Subscription settings if (configuration.Subscription != null) { ValidateSubscriptionConfiguration(configuration.Subscription, errors); } // Validate Service Recovery settings if (configuration.ServiceRecovery != null) { ValidateServiceRecoveryConfiguration(configuration.ServiceRecovery, errors); } // Validate TLS settings if (configuration.Tls != null) { if (!configuration.Tls.Validate()) { errors.Add("TLS configuration validation failed. Check the logs for details."); } } return errors; } private static void ValidateConnectionConfiguration(ConnectionConfiguration config, List errors) { if (config.MonitorIntervalSeconds <= 0) { errors.Add( $"Invalid monitor interval: {config.MonitorIntervalSeconds} seconds. Must be greater than 0."); } if (config.ConnectionTimeoutSeconds <= 0) { errors.Add( $"Invalid connection timeout: {config.ConnectionTimeoutSeconds} seconds. Must be greater than 0."); } if (config.ReadTimeoutSeconds <= 0) { errors.Add($"Invalid read timeout: {config.ReadTimeoutSeconds} seconds. Must be greater than 0."); } if (config.WriteTimeoutSeconds <= 0) { errors.Add($"Invalid write timeout: {config.WriteTimeoutSeconds} seconds. Must be greater than 0."); } if (config.MaxConcurrentOperations.HasValue && config.MaxConcurrentOperations.Value <= 0) { errors.Add( $"Invalid max concurrent operations: {config.MaxConcurrentOperations}. Must be greater than 0."); } // Validate node and galaxy names if provided if (!string.IsNullOrWhiteSpace(config.NodeName) && config.NodeName?.Length > 255) { errors.Add($"Node name is too long: {config.NodeName.Length} characters. Maximum is 255."); } if (!string.IsNullOrWhiteSpace(config.GalaxyName) && config.GalaxyName?.Length > 255) { errors.Add($"Galaxy name is too long: {config.GalaxyName.Length} characters. Maximum is 255."); } } private static void ValidateSubscriptionConfiguration(SubscriptionConfiguration config, List errors) { if (config.ChannelCapacity <= 0) { errors.Add($"Invalid channel capacity: {config.ChannelCapacity}. Must be greater than 0."); } if (config.ChannelCapacity > 100000) { errors.Add($"Channel capacity too large: {config.ChannelCapacity}. Maximum recommended is 100000."); } string[] validChannelModes = { "DropOldest", "DropNewest", "Wait" }; if (!validChannelModes.Contains(config.ChannelFullMode)) { errors.Add( $"Invalid channel full mode: {config.ChannelFullMode}. Valid values are: {string.Join(", ", validChannelModes)}"); } } private static void ValidateServiceRecoveryConfiguration(ServiceRecoveryConfiguration config, List errors) { if (config.FirstFailureDelayMinutes < 0) { errors.Add( $"Invalid first failure delay: {config.FirstFailureDelayMinutes} minutes. Must be 0 or greater."); } if (config.SecondFailureDelayMinutes < 0) { errors.Add( $"Invalid second failure delay: {config.SecondFailureDelayMinutes} minutes. Must be 0 or greater."); } if (config.SubsequentFailureDelayMinutes < 0) { errors.Add( $"Invalid subsequent failure delay: {config.SubsequentFailureDelayMinutes} minutes. Must be 0 or greater."); } if (config.ResetPeriodDays <= 0) { errors.Add($"Invalid reset period: {config.ResetPeriodDays} days. Must be greater than 0."); } } /// /// Logs validation results and returns whether the configuration is valid. /// /// The configuration to validate. /// True if configuration is valid; otherwise, false. public static bool ValidateAndLog(LmxProxyConfiguration configuration) { List errors = Validate(configuration); if (errors.Any()) { Logger.Error("Configuration validation failed with {ErrorCount} errors:", errors.Count); foreach (string? error in errors) { Logger.Error(" - {ValidationError}", error); } return false; } Logger.Information("Configuration validation successful"); return true; } /// /// Throws an exception if the configuration is invalid. /// /// The configuration to validate. /// Thrown when configuration is invalid. public static void ValidateOrThrow(LmxProxyConfiguration configuration) { List errors = Validate(configuration); if (errors.Any()) { string message = $"Configuration validation failed with {errors.Count} error(s):\n" + string.Join("\n", errors.Select(e => $" - {e}")); throw new InvalidOperationException(message); } } } }