Register Enova database from SQL connection string
This commit is contained in:
@@ -13,9 +13,9 @@ Dedykowana usługa Windows zastępująca proces Node na stanowiskach z enova365.
|
|||||||
|
|
||||||
## Konfiguracja
|
## 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
|
## 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.
|
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.
|
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.
|
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`.
|
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.
|
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.
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -18,18 +18,14 @@ public sealed class EnovaOrderAdapter : IEnovaOrderAdapter
|
|||||||
private static readonly object RuntimeLock = new();
|
private static readonly object RuntimeLock = new();
|
||||||
private static bool _runtimeLoaded;
|
private static bool _runtimeLoaded;
|
||||||
private EnovaAdapterConfiguration? _configuration;
|
private EnovaAdapterConfiguration? _configuration;
|
||||||
|
private Database? _database;
|
||||||
|
|
||||||
public EnovaAdapterInfo Initialize(EnovaAdapterConfiguration configuration)
|
public EnovaAdapterInfo Initialize(EnovaAdapterConfiguration configuration)
|
||||||
{
|
{
|
||||||
ValidateConfiguration(configuration);
|
ValidateConfiguration(configuration);
|
||||||
EnsureRuntimeLoaded();
|
EnsureRuntimeLoaded();
|
||||||
|
|
||||||
_ = BusApplication.Instance[configuration.Connection.DatabaseName]
|
_database = RegisterDatabase(configuration.Connection);
|
||||||
?? throw new EnovaOperationException(
|
|
||||||
"eu.smartb2b.erp.database_not_found",
|
|
||||||
$"Nie znaleziono zarejestrowanej bazy enova365 '{configuration.Connection.DatabaseName}'.",
|
|
||||||
new Dictionary<string, object?> { ["database"] = configuration.Connection.DatabaseName });
|
|
||||||
|
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
|
|
||||||
return new EnovaAdapterInfo(
|
return new EnovaAdapterInfo(
|
||||||
@@ -43,6 +39,8 @@ public sealed class EnovaOrderAdapter : IEnovaOrderAdapter
|
|||||||
{
|
{
|
||||||
var configuration = _configuration
|
var configuration = _configuration
|
||||||
?? throw new InvalidOperationException("Adapter Enovy nie został zainicjalizowany.");
|
?? 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);
|
var errors = OrderValidator.Validate(request);
|
||||||
if (errors.Count > 0)
|
if (errors.Count > 0)
|
||||||
@@ -58,11 +56,6 @@ public sealed class EnovaOrderAdapter : IEnovaOrderAdapter
|
|||||||
var sessionState = SessionState.Create();
|
var sessionState = SessionState.Create();
|
||||||
using var attachedSessionState = sessionState.Attach();
|
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(
|
using var login = database.Login(
|
||||||
winAuth: false,
|
winAuth: false,
|
||||||
user: configuration.Connection.OperatorName,
|
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)
|
private static void ValidateConfiguration(EnovaAdapterConfiguration configuration)
|
||||||
{
|
{
|
||||||
var errors = new List<string>();
|
var errors = new List<string>();
|
||||||
@@ -238,6 +251,11 @@ public sealed class EnovaOrderAdapter : IEnovaOrderAdapter
|
|||||||
errors.Add("Nazwa operatora enova365 jest wymagana.");
|
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))
|
if (string.IsNullOrWhiteSpace(configuration.DocumentDefinition))
|
||||||
{
|
{
|
||||||
errors.Add("Definicja dokumentu jest wymagana.");
|
errors.Add("Definicja dokumentu jest wymagana.");
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ public enum OrderSaveMode
|
|||||||
public sealed record EnovaConnectionOptions(
|
public sealed record EnovaConnectionOptions(
|
||||||
string DatabaseName,
|
string DatabaseName,
|
||||||
string OperatorName,
|
string OperatorName,
|
||||||
string Password);
|
string Password,
|
||||||
|
string SqlConnectionString);
|
||||||
|
|
||||||
public sealed record EnovaAdapterConfiguration(
|
public sealed record EnovaAdapterConfiguration(
|
||||||
EnovaConnectionOptions Connection,
|
EnovaConnectionOptions Connection,
|
||||||
|
|||||||
@@ -57,7 +57,11 @@ public sealed class ServiceSettings
|
|||||||
if (!string.IsNullOrWhiteSpace(Enova.Operator) && Enova.Password is not null)
|
if (!string.IsNullOrWhiteSpace(Enova.Operator) && Enova.Password is not null)
|
||||||
{
|
{
|
||||||
enovaConfiguration = new EnovaAdapterConfiguration(
|
enovaConfiguration = new EnovaAdapterConfiguration(
|
||||||
new EnovaConnectionOptions(Enova.Database, Enova.Operator, Enova.Password),
|
new EnovaConnectionOptions(
|
||||||
|
Enova.Database,
|
||||||
|
Enova.Operator,
|
||||||
|
Enova.Password,
|
||||||
|
Sql.ConnectionString),
|
||||||
Enova.DocumentDefinition,
|
Enova.DocumentDefinition,
|
||||||
Enova.DefaultWarehouseCode,
|
Enova.DefaultWarehouseCode,
|
||||||
Enova.SaveMode);
|
Enova.SaveMode);
|
||||||
|
|||||||
@@ -255,6 +255,10 @@ static Task SecretsComeFromConfigurationFile()
|
|||||||
"Server=db;Database=enova;User ID=user;Password=sql-z-pliku",
|
"Server=db;Database=enova;User ID=user;Password=sql-z-pliku",
|
||||||
runtime.SqlConnectionString,
|
runtime.SqlConnectionString,
|
||||||
"connection string SQL");
|
"connection string SQL");
|
||||||
|
AssertEqual(
|
||||||
|
runtime.SqlConnectionString,
|
||||||
|
runtime.EnovaConfiguration?.Connection.SqlConnectionString,
|
||||||
|
"connection string rejestracji bazy Enovy");
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -321,7 +325,11 @@ static RpcInvocation CreateInvocation() => new(
|
|||||||
});
|
});
|
||||||
|
|
||||||
static EnovaAdapterConfiguration CreateConfiguration() => 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",
|
"ZO",
|
||||||
null,
|
null,
|
||||||
OrderSaveMode.Buffer);
|
OrderSaveMode.Buffer);
|
||||||
|
|||||||
Reference in New Issue
Block a user