diff --git a/README.md b/README.md index 5bb691a..6b83847 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ Dedykowana usługa Windows zastępująca proces Node na stanowiskach z enova365. ## Konfiguracja -Plik `config/enova.json` zawiera portal WAMP, ścieżkę instalacji, alias bazy, operatora wraz z hasłem, connection string SQL, definicję dokumentu, magazyn domyślny, tryb zapisu oraz DSN Sentry. Repozytorium oraz paczka zawierają tylko `config/enova.json.sample`. Skrypt instalacyjny kopiuje wzorzec do `config/enova.json` wyłącznie wtedy, gdy lokalna konfiguracja jeszcze nie istnieje. +Plik `config/enova.json` zawiera portal WAMP, ścieżkę instalacji, alias bazy używany wewnątrz procesu, operatora wraz z hasłem, connection string SQL, definicję dokumentu, magazyn domyślny, tryb zapisu oraz DSN Sentry. Repozytorium oraz paczka zawierają tylko `config/enova.json.sample`. Skrypt instalacyjny kopiuje wzorzec do `config/enova.json` wyłącznie wtedy, gdy lokalna konfiguracja jeszcze nie istnieje. -Uzupełnij pola `enova.operator`, `enova.password` i `sql.connectionString` bezpośrednio w lokalnym pliku `config/enova.json`. Parametr `sentry.dsn` wskazuje projekt Sentry; pusty lub pominięty DSN wyłącza wysyłanie błędów. Jeżeli operator lub hasło Enovy nie są podane, usługa nie loguje się do bazy Enovy i nie rejestruje endpointu `eu.smartb2b.place_order`; endpoint SQL oraz diagnostyka pozostają dostępne. Po zmianie konfiguracji trzeba zrestartować usługę. Connection string powinien używać konta SQL o możliwie najmniejszych uprawnieniach; `sql_raw` celowo dopuszcza również polecenia modyfikujące dane dla zgodności z obecną usługą. +Uzupełnij pola `enova.operator`, `enova.password` i `sql.connectionString` bezpośrednio w lokalnym pliku `config/enova.json`. Adapter rejestruje bazę Enovy tylko w pamięci procesu na podstawie `sql.connectionString`; baza nie musi być wcześniej dodana do listy baz w profilu użytkownika Windows, a dane połączenia nie są przez usługę zapisywane w plikach Enovy. Pole `enova.database` jest nazwą tego wpisu w procesie i może odpowiadać nazwie bazy z connection stringa. Parametr `sentry.dsn` wskazuje projekt Sentry; pusty lub pominięty DSN wyłącza wysyłanie błędów. Jeżeli operator lub hasło Enovy nie są podane, usługa nie loguje się do bazy Enovy i nie rejestruje endpointu `eu.smartb2b.place_order`; endpoint SQL oraz diagnostyka pozostają dostępne. Po zmianie konfiguracji trzeba zrestartować usługę. Konto z `sql.connectionString` musi mieć uprawnienia wymagane do pracy Enovy oraz do zapytań `sql_raw`; `sql_raw` celowo dopuszcza również polecenia modyfikujące dane dla zgodności z obecną usługą. ## Budowanie i testy @@ -46,7 +46,7 @@ Skrypt odtwarza katalog `release` z hostem, adapterem, wzorcem konfiguracji i Wi 1. Skopiuj katalog `release` na komputer produkcyjny. Możesz skopiować `release/config/enova.json.sample` do `release/config/enova.json` i uzupełnić go przed instalacją; jeżeli tego nie zrobisz, `install.cmd` utworzy plik automatycznie. 2. Uzupełnij w `release/config/enova.json` hasło operatora i connection string SQL. 3. Zatrzymaj starą usługę Node dla tego samego portalu WAMP. -4. Skonfiguruj usługę do pracy pod dedykowanym kontem Windows, które potrafi uruchomić Enovę, widzi jej bazę i licencję. +4. Skonfiguruj usługę do pracy pod dedykowanym kontem Windows, które potrafi uruchomić Enovę i ma dostęp do jej licencji. Lista baz zapisana w profilu tego konta nie jest wymagana. 5. Jako administrator uruchom `release/daemon/install.cmd`. Jeżeli usługa ma działać pod innym kontem niż `LocalSystem`, po instalacji ustaw je w `services.msc` albo poleceniem administracyjnym `sc.exe config smartb2bsync-enova obj= "DOMENA\\Użytkownik" password= "..."`, a następnie uruchom usługę ponownie. diff --git a/release/SmartB2B.Enova.Contracts.dll b/release/SmartB2B.Enova.Contracts.dll index 07bae83..47d11f4 100644 Binary files a/release/SmartB2B.Enova.Contracts.dll and b/release/SmartB2B.Enova.Contracts.dll differ diff --git a/release/SmartB2B.Enova.Contracts.pdb b/release/SmartB2B.Enova.Contracts.pdb index 50a8c41..01b0636 100644 Binary files a/release/SmartB2B.Enova.Contracts.pdb and b/release/SmartB2B.Enova.Contracts.pdb differ diff --git a/release/SmartB2B.Enova.Service.dll b/release/SmartB2B.Enova.Service.dll index b725bd9..df5e434 100644 Binary files a/release/SmartB2B.Enova.Service.dll and b/release/SmartB2B.Enova.Service.dll differ diff --git a/release/SmartB2B.Enova.Service.exe b/release/SmartB2B.Enova.Service.exe index 17b3c97..7414d8c 100644 Binary files a/release/SmartB2B.Enova.Service.exe and b/release/SmartB2B.Enova.Service.exe differ diff --git a/release/SmartB2B.Enova.Service.pdb b/release/SmartB2B.Enova.Service.pdb index 0021d45..df3ac21 100644 Binary files a/release/SmartB2B.Enova.Service.pdb and b/release/SmartB2B.Enova.Service.pdb differ diff --git a/release/adapters/SmartB2B.Enova.Adapter.dll b/release/adapters/SmartB2B.Enova.Adapter.dll index 2feabea..e8d75d9 100644 Binary files a/release/adapters/SmartB2B.Enova.Adapter.dll and b/release/adapters/SmartB2B.Enova.Adapter.dll differ diff --git a/src/SmartB2B.Enova.Adapter/EnovaOrderAdapter.cs b/src/SmartB2B.Enova.Adapter/EnovaOrderAdapter.cs index a20e78c..d6cd4d9 100644 --- a/src/SmartB2B.Enova.Adapter/EnovaOrderAdapter.cs +++ b/src/SmartB2B.Enova.Adapter/EnovaOrderAdapter.cs @@ -18,18 +18,14 @@ public sealed class EnovaOrderAdapter : IEnovaOrderAdapter private static readonly object RuntimeLock = new(); private static bool _runtimeLoaded; private EnovaAdapterConfiguration? _configuration; + private Database? _database; public EnovaAdapterInfo Initialize(EnovaAdapterConfiguration configuration) { ValidateConfiguration(configuration); EnsureRuntimeLoaded(); - _ = BusApplication.Instance[configuration.Connection.DatabaseName] - ?? throw new EnovaOperationException( - "eu.smartb2b.erp.database_not_found", - $"Nie znaleziono zarejestrowanej bazy enova365 '{configuration.Connection.DatabaseName}'.", - new Dictionary { ["database"] = configuration.Connection.DatabaseName }); - + _database = RegisterDatabase(configuration.Connection); _configuration = configuration; return new EnovaAdapterInfo( @@ -43,6 +39,8 @@ public sealed class EnovaOrderAdapter : IEnovaOrderAdapter { var configuration = _configuration ?? throw new InvalidOperationException("Adapter Enovy nie został zainicjalizowany."); + var database = _database + ?? throw new InvalidOperationException("Połączenie z bazą Enovy nie zostało zainicjalizowane."); var errors = OrderValidator.Validate(request); if (errors.Count > 0) @@ -58,11 +56,6 @@ public sealed class EnovaOrderAdapter : IEnovaOrderAdapter var sessionState = SessionState.Create(); using var attachedSessionState = sessionState.Attach(); - var database = BusApplication.Instance[configuration.Connection.DatabaseName] - ?? throw new EnovaOperationException( - "eu.smartb2b.erp.database_not_found", - $"Nie znaleziono zarejestrowanej bazy enova365 '{configuration.Connection.DatabaseName}'."); - using var login = database.Login( winAuth: false, user: configuration.Connection.OperatorName, @@ -225,6 +218,26 @@ public sealed class EnovaOrderAdapter : IEnovaOrderAdapter } } + private static Database RegisterDatabase(EnovaConnectionOptions connection) + { + var application = BusApplication.Instance; + var existing = application[connection.DatabaseName, throwError: false]; + if (existing is not null) + { + application.Remove(existing, save: false); + } + + var database = new MsSqlDatabase + { + Name = connection.DatabaseName, + UseConnectionString = true, + ConnectionString = connection.SqlConnectionString + }; + + application.Add(database, save: false); + return database; + } + private static void ValidateConfiguration(EnovaAdapterConfiguration configuration) { var errors = new List(); @@ -238,6 +251,11 @@ public sealed class EnovaOrderAdapter : IEnovaOrderAdapter errors.Add("Nazwa operatora enova365 jest wymagana."); } + if (string.IsNullOrWhiteSpace(configuration.Connection.SqlConnectionString)) + { + errors.Add("Connection string SQL dla bazy Enovy jest wymagany."); + } + if (string.IsNullOrWhiteSpace(configuration.DocumentDefinition)) { errors.Add("Definicja dokumentu jest wymagana."); diff --git a/src/SmartB2B.Enova.Contracts/EnovaContracts.cs b/src/SmartB2B.Enova.Contracts/EnovaContracts.cs index ffcbefa..39b8c1c 100644 --- a/src/SmartB2B.Enova.Contracts/EnovaContracts.cs +++ b/src/SmartB2B.Enova.Contracts/EnovaContracts.cs @@ -9,7 +9,8 @@ public enum OrderSaveMode public sealed record EnovaConnectionOptions( string DatabaseName, string OperatorName, - string Password); + string Password, + string SqlConnectionString); public sealed record EnovaAdapterConfiguration( EnovaConnectionOptions Connection, diff --git a/src/SmartB2B.Enova.Service/Configuration/ServiceSettings.cs b/src/SmartB2B.Enova.Service/Configuration/ServiceSettings.cs index 28471ed..af3940c 100644 --- a/src/SmartB2B.Enova.Service/Configuration/ServiceSettings.cs +++ b/src/SmartB2B.Enova.Service/Configuration/ServiceSettings.cs @@ -57,7 +57,11 @@ public sealed class ServiceSettings if (!string.IsNullOrWhiteSpace(Enova.Operator) && Enova.Password is not null) { enovaConfiguration = new EnovaAdapterConfiguration( - new EnovaConnectionOptions(Enova.Database, Enova.Operator, Enova.Password), + new EnovaConnectionOptions( + Enova.Database, + Enova.Operator, + Enova.Password, + Sql.ConnectionString), Enova.DocumentDefinition, Enova.DefaultWarehouseCode, Enova.SaveMode); diff --git a/tests/SmartB2B.Enova.Tests/Program.cs b/tests/SmartB2B.Enova.Tests/Program.cs index 1f999bc..82da1a3 100644 --- a/tests/SmartB2B.Enova.Tests/Program.cs +++ b/tests/SmartB2B.Enova.Tests/Program.cs @@ -255,6 +255,10 @@ static Task SecretsComeFromConfigurationFile() "Server=db;Database=enova;User ID=user;Password=sql-z-pliku", runtime.SqlConnectionString, "connection string SQL"); + AssertEqual( + runtime.SqlConnectionString, + runtime.EnovaConfiguration?.Connection.SqlConnectionString, + "connection string rejestracji bazy Enovy"); return Task.CompletedTask; } finally @@ -321,7 +325,11 @@ static RpcInvocation CreateInvocation() => new( }); static EnovaAdapterConfiguration CreateConfiguration() => new( - new EnovaConnectionOptions("Firma demo", "Administrator", ""), + new EnovaConnectionOptions( + "Firma demo", + "Administrator", + "", + "Server=localhost;Database=enova;Integrated Security=true;TrustServerCertificate=true"), "ZO", null, OrderSaveMode.Buffer);