using System.Text.Json; using System.Text.Json.Serialization; using SmartB2B.Enova.Contracts; namespace SmartB2B.Enova.Service.Configuration; public sealed class ServiceSettings { public string Portal { get; init; } = string.Empty; public WampSettings Wamp { get; init; } = new(); public EnovaSettings Enova { get; init; } = new(); public SqlSettings Sql { get; init; } = new(); public SentrySettings Sentry { get; init; } = new(); public DiagnosticsSettings Diagnostics { get; init; } = new(); public static ServiceSettings Load(string path) { if (!File.Exists(path)) { throw new ConfigurationException($"Nie znaleziono pliku konfiguracji: {path}"); } var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true, Converters = { new JsonStringEnumConverter() } }; ServiceSettings settings; try { settings = JsonSerializer.Deserialize(File.ReadAllText(path), options) ?? throw new ConfigurationException("Plik konfiguracji jest pusty."); } catch (JsonException exception) { throw new ConfigurationException($"Niepoprawny JSON konfiguracji: {exception.Message}", exception); } settings.Validate(); return settings; } public RuntimeSettings ResolveRuntimeSettings(bool verboseLogging = false) { var installationPath = Path.GetFullPath( Environment.ExpandEnvironmentVariables(Enova.InstallationPath)); EnovaAdapterConfiguration? enovaConfiguration = null; if (!string.IsNullOrWhiteSpace(Enova.Operator) && Enova.Password is not null) { enovaConfiguration = new EnovaAdapterConfiguration( new EnovaConnectionOptions( Enova.Database, Enova.Operator, Enova.Password, Sql.ConnectionString), Enova.DocumentDefinition, Enova.DefaultWarehouseCode, Enova.SaveMode, verboseLogging); } return new RuntimeSettings( installationPath, enovaConfiguration, Sql.ConnectionString, Sql.CommandTimeoutSeconds, ResolvePath(Diagnostics.LogDirectory), Diagnostics.LogPrefix); } private void Validate() { var errors = new List(); Require(Portal, "portal", errors); Require(Wamp.ServerUrl, "wamp.serverUrl", errors); if (Wamp.ReconnectDelaySeconds <= 0) { errors.Add("wamp.reconnectDelaySeconds musi być większe od zera."); } Require(Enova.InstallationPath, "enova.installationPath", errors); Require(Enova.Database, "enova.database", errors); Require(Enova.DocumentDefinition, "enova.documentDefinition", errors); Require(Sql.ConnectionString, "sql.connectionString", errors); if (Sql.CommandTimeoutSeconds <= 0) { errors.Add("sql.commandTimeoutSeconds musi być większe od zera."); } if (errors.Count > 0) { throw new ConfigurationException(string.Join(Environment.NewLine, errors)); } } private static void Require(string? value, string name, ICollection errors) { if (string.IsNullOrWhiteSpace(value)) { errors.Add($"Parametr '{name}' jest wymagany."); } } private static string ResolvePath(string path) => Path.IsPathRooted(path) ? Path.GetFullPath(path) : Path.GetFullPath(path, AppContext.BaseDirectory); } public sealed class WampSettings { public string ServerUrl { get; init; } = string.Empty; public int ReconnectDelaySeconds { get; init; } = 10; } public sealed class EnovaSettings { public string InstallationPath { get; init; } = string.Empty; public string Database { get; init; } = string.Empty; public string? Operator { get; init; } public string? Password { get; init; } public string DocumentDefinition { get; init; } = "ZO"; public string? DefaultWarehouseCode { get; init; } public OrderSaveMode SaveMode { get; init; } = OrderSaveMode.Buffer; } public sealed class SqlSettings { public string ConnectionString { get; init; } = string.Empty; public int CommandTimeoutSeconds { get; init; } = 30; } public sealed class SentrySettings { public string? Dsn { get; init; } } public sealed class DiagnosticsSettings { public string LogDirectory { get; init; } = "daemon"; public string LogPrefix { get; init; } = "smartb2bsync-enova"; } public sealed record RuntimeSettings( string EnovaInstallationPath, EnovaAdapterConfiguration? EnovaConfiguration, string SqlConnectionString, int SqlCommandTimeoutSeconds, string LogDirectory, string LogPrefix); public sealed class ConfigurationException : Exception { public ConfigurationException(string message, Exception? innerException = null) : base(message, innerException) { } }