feat(cli): add --primary-config, --backup-config, --failover-retry-count to data connection commands
Thread backup data connection fields through management command messages, ManagementActor handlers, SiteService, site-side SQLite storage, and deployment/replication actors. The old --configuration CLI flag is kept as a hidden alias for backwards compatibility.
This commit is contained in:
@@ -38,22 +38,28 @@ public static class DataConnectionCommands
|
||||
var idOption = new Option<int>("--id") { Description = "Data connection ID", Required = true };
|
||||
var nameOption = new Option<string>("--name") { Description = "Connection name", Required = true };
|
||||
var protocolOption = new Option<string>("--protocol") { Description = "Protocol", Required = true };
|
||||
var configOption = new Option<string?>("--configuration") { Description = "Primary configuration JSON" };
|
||||
var configOption = new Option<string?>("--primary-config", "--configuration") { Description = "Primary configuration JSON" };
|
||||
var backupConfigOption = new Option<string?>("--backup-config") { Description = "Backup configuration JSON" };
|
||||
var failoverRetryOption = new Option<int>("--failover-retry-count") { Description = "Number of retries before failover to backup", DefaultValueFactory = _ => 3 };
|
||||
|
||||
var cmd = new Command("update") { Description = "Update a data connection" };
|
||||
cmd.Add(idOption);
|
||||
cmd.Add(nameOption);
|
||||
cmd.Add(protocolOption);
|
||||
cmd.Add(configOption);
|
||||
cmd.Add(backupConfigOption);
|
||||
cmd.Add(failoverRetryOption);
|
||||
cmd.SetAction(async (ParseResult result) =>
|
||||
{
|
||||
var id = result.GetValue(idOption);
|
||||
var name = result.GetValue(nameOption)!;
|
||||
var protocol = result.GetValue(protocolOption)!;
|
||||
var config = result.GetValue(configOption);
|
||||
var backupConfig = result.GetValue(backupConfigOption);
|
||||
var failoverRetryCount = result.GetValue(failoverRetryOption);
|
||||
return await CommandHelpers.ExecuteCommandAsync(
|
||||
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||
new UpdateDataConnectionCommand(id, name, protocol, config));
|
||||
new UpdateDataConnectionCommand(id, name, protocol, config, backupConfig, failoverRetryCount));
|
||||
});
|
||||
return cmd;
|
||||
}
|
||||
@@ -77,22 +83,28 @@ public static class DataConnectionCommands
|
||||
var siteIdOption = new Option<int>("--site-id") { Description = "Site ID", Required = true };
|
||||
var nameOption = new Option<string>("--name") { Description = "Connection name", Required = true };
|
||||
var protocolOption = new Option<string>("--protocol") { Description = "Protocol (e.g. OpcUa)", Required = true };
|
||||
var configOption = new Option<string?>("--configuration") { Description = "Primary configuration JSON" };
|
||||
var configOption = new Option<string?>("--primary-config", "--configuration") { Description = "Primary configuration JSON" };
|
||||
var backupConfigOption = new Option<string?>("--backup-config") { Description = "Backup configuration JSON" };
|
||||
var failoverRetryOption = new Option<int>("--failover-retry-count") { Description = "Number of retries before failover to backup", DefaultValueFactory = _ => 3 };
|
||||
|
||||
var cmd = new Command("create") { Description = "Create a new data connection" };
|
||||
cmd.Add(siteIdOption);
|
||||
cmd.Add(nameOption);
|
||||
cmd.Add(protocolOption);
|
||||
cmd.Add(configOption);
|
||||
cmd.Add(backupConfigOption);
|
||||
cmd.Add(failoverRetryOption);
|
||||
cmd.SetAction(async (ParseResult result) =>
|
||||
{
|
||||
var siteId = result.GetValue(siteIdOption);
|
||||
var name = result.GetValue(nameOption)!;
|
||||
var protocol = result.GetValue(protocolOption)!;
|
||||
var config = result.GetValue(configOption);
|
||||
var backupConfig = result.GetValue(backupConfigOption);
|
||||
var failoverRetryCount = result.GetValue(failoverRetryOption);
|
||||
return await CommandHelpers.ExecuteCommandAsync(
|
||||
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||
new CreateDataConnectionCommand(siteId, name, protocol, config));
|
||||
new CreateDataConnectionCommand(siteId, name, protocol, config, backupConfig, failoverRetryCount));
|
||||
});
|
||||
return cmd;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@ namespace ScadaLink.Commons.Messages.Management;
|
||||
|
||||
public record ListDataConnectionsCommand(int? SiteId = null);
|
||||
public record GetDataConnectionCommand(int DataConnectionId);
|
||||
public record CreateDataConnectionCommand(int SiteId, string Name, string Protocol, string? PrimaryConfiguration);
|
||||
public record UpdateDataConnectionCommand(int DataConnectionId, string Name, string Protocol, string? PrimaryConfiguration);
|
||||
public record CreateDataConnectionCommand(int SiteId, string Name, string Protocol, string? PrimaryConfiguration, string? BackupConfiguration = null, int FailoverRetryCount = 3);
|
||||
public record UpdateDataConnectionCommand(int DataConnectionId, string Name, string Protocol, string? PrimaryConfiguration, string? BackupConfiguration = null, int FailoverRetryCount = 3);
|
||||
public record DeleteDataConnectionCommand(int DataConnectionId);
|
||||
|
||||
@@ -689,7 +689,12 @@ public class ManagementActor : ReceiveActor
|
||||
private static async Task<object?> HandleCreateDataConnection(IServiceProvider sp, CreateDataConnectionCommand cmd, string user)
|
||||
{
|
||||
var repo = sp.GetRequiredService<ISiteRepository>();
|
||||
var conn = new DataConnection(cmd.Name, cmd.Protocol, cmd.SiteId) { PrimaryConfiguration = cmd.PrimaryConfiguration };
|
||||
var conn = new DataConnection(cmd.Name, cmd.Protocol, cmd.SiteId)
|
||||
{
|
||||
PrimaryConfiguration = cmd.PrimaryConfiguration,
|
||||
BackupConfiguration = cmd.BackupConfiguration,
|
||||
FailoverRetryCount = cmd.FailoverRetryCount
|
||||
};
|
||||
await repo.AddDataConnectionAsync(conn);
|
||||
await repo.SaveChangesAsync();
|
||||
await AuditAsync(sp, user, "Create", "DataConnection", conn.Id.ToString(), conn.Name, conn);
|
||||
@@ -704,6 +709,8 @@ public class ManagementActor : ReceiveActor
|
||||
conn.Name = cmd.Name;
|
||||
conn.Protocol = cmd.Protocol;
|
||||
conn.PrimaryConfiguration = cmd.PrimaryConfiguration;
|
||||
conn.BackupConfiguration = cmd.BackupConfiguration;
|
||||
conn.FailoverRetryCount = cmd.FailoverRetryCount;
|
||||
await repo.UpdateDataConnectionAsync(conn);
|
||||
await repo.SaveChangesAsync();
|
||||
await AuditAsync(sp, user, "Update", "DataConnection", conn.Id.ToString(), conn.Name, conn);
|
||||
|
||||
@@ -630,7 +630,8 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers
|
||||
foreach (var dc in command.DataConnections)
|
||||
{
|
||||
await _storage.StoreDataConnectionDefinitionAsync(
|
||||
dc.Name, dc.Protocol, dc.PrimaryConfigurationJson);
|
||||
dc.Name, dc.Protocol, dc.PrimaryConfigurationJson,
|
||||
dc.BackupConfigurationJson, dc.FailoverRetryCount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ public class SiteReplicationActor : ReceiveActor
|
||||
|
||||
if (command.DataConnections != null)
|
||||
foreach (var dc in command.DataConnections)
|
||||
await _storage.StoreDataConnectionDefinitionAsync(dc.Name, dc.Protocol, dc.PrimaryConfigurationJson);
|
||||
await _storage.StoreDataConnectionDefinitionAsync(dc.Name, dc.Protocol, dc.PrimaryConfigurationJson, dc.BackupConfigurationJson, dc.FailoverRetryCount);
|
||||
|
||||
if (command.SmtpConfigurations != null)
|
||||
foreach (var smtp in command.SmtpConfigurations)
|
||||
|
||||
@@ -82,6 +82,8 @@ public class SiteStorageService
|
||||
name TEXT PRIMARY KEY,
|
||||
protocol TEXT NOT NULL,
|
||||
configuration TEXT,
|
||||
backup_configuration TEXT,
|
||||
failover_retry_count INTEGER NOT NULL DEFAULT 3,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
@@ -480,23 +482,28 @@ public class SiteStorageService
|
||||
/// <summary>
|
||||
/// Stores or updates a data connection definition (OPC UA endpoint, etc.).
|
||||
/// </summary>
|
||||
public async Task StoreDataConnectionDefinitionAsync(string name, string protocol, string? configJson)
|
||||
public async Task StoreDataConnectionDefinitionAsync(
|
||||
string name, string protocol, string? configJson, string? backupConfigJson = null, int failoverRetryCount = 3)
|
||||
{
|
||||
await using var connection = new SqliteConnection(_connectionString);
|
||||
await connection.OpenAsync();
|
||||
|
||||
await using var command = connection.CreateCommand();
|
||||
command.CommandText = @"
|
||||
INSERT INTO data_connection_definitions (name, protocol, configuration, updated_at)
|
||||
VALUES (@name, @protocol, @config, @updatedAt)
|
||||
INSERT INTO data_connection_definitions (name, protocol, configuration, backup_configuration, failover_retry_count, updated_at)
|
||||
VALUES (@name, @protocol, @config, @backupConfig, @failoverRetryCount, @updatedAt)
|
||||
ON CONFLICT(name) DO UPDATE SET
|
||||
protocol = excluded.protocol,
|
||||
configuration = excluded.configuration,
|
||||
backup_configuration = excluded.backup_configuration,
|
||||
failover_retry_count = excluded.failover_retry_count,
|
||||
updated_at = excluded.updated_at";
|
||||
|
||||
command.Parameters.AddWithValue("@name", name);
|
||||
command.Parameters.AddWithValue("@protocol", protocol);
|
||||
command.Parameters.AddWithValue("@config", (object?)configJson ?? DBNull.Value);
|
||||
command.Parameters.AddWithValue("@backupConfig", (object?)backupConfigJson ?? DBNull.Value);
|
||||
command.Parameters.AddWithValue("@failoverRetryCount", failoverRetryCount);
|
||||
command.Parameters.AddWithValue("@updatedAt", DateTimeOffset.UtcNow.ToString("O"));
|
||||
|
||||
await command.ExecuteNonQueryAsync();
|
||||
@@ -512,7 +519,7 @@ public class SiteStorageService
|
||||
await connection.OpenAsync();
|
||||
|
||||
await using var command = connection.CreateCommand();
|
||||
command.CommandText = "SELECT name, protocol, configuration FROM data_connection_definitions";
|
||||
command.CommandText = "SELECT name, protocol, configuration, backup_configuration, failover_retry_count FROM data_connection_definitions";
|
||||
|
||||
var results = new List<StoredDataConnectionDefinition>();
|
||||
await using var reader = await command.ExecuteReaderAsync();
|
||||
@@ -522,7 +529,9 @@ public class SiteStorageService
|
||||
{
|
||||
Name = reader.GetString(0),
|
||||
Protocol = reader.GetString(1),
|
||||
ConfigurationJson = reader.IsDBNull(2) ? null : reader.GetString(2)
|
||||
ConfigurationJson = reader.IsDBNull(2) ? null : reader.GetString(2),
|
||||
BackupConfigurationJson = reader.IsDBNull(3) ? null : reader.GetString(3),
|
||||
FailoverRetryCount = reader.GetInt32(4)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -562,4 +571,6 @@ public class StoredDataConnectionDefinition
|
||||
public string Name { get; init; } = string.Empty;
|
||||
public string Protocol { get; init; } = string.Empty;
|
||||
public string? ConfigurationJson { get; init; }
|
||||
public string? BackupConfigurationJson { get; init; }
|
||||
public int FailoverRetryCount { get; init; } = 3;
|
||||
}
|
||||
|
||||
@@ -96,7 +96,8 @@ public class SiteService
|
||||
// --- Data Connection CRUD ---
|
||||
|
||||
public async Task<Result<DataConnection>> CreateDataConnectionAsync(
|
||||
int siteId, string name, string protocol, string? configuration, string user,
|
||||
int siteId, string name, string protocol, string? primaryConfiguration,
|
||||
string? backupConfiguration, int failoverRetryCount, string user,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
@@ -104,7 +105,12 @@ public class SiteService
|
||||
if (string.IsNullOrWhiteSpace(protocol))
|
||||
return Result<DataConnection>.Failure("Protocol is required.");
|
||||
|
||||
var connection = new DataConnection(name, protocol, siteId) { PrimaryConfiguration = configuration };
|
||||
var connection = new DataConnection(name, protocol, siteId)
|
||||
{
|
||||
PrimaryConfiguration = primaryConfiguration,
|
||||
BackupConfiguration = backupConfiguration,
|
||||
FailoverRetryCount = failoverRetryCount
|
||||
};
|
||||
await _repository.AddDataConnectionAsync(connection, cancellationToken);
|
||||
await _repository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
@@ -115,7 +121,8 @@ public class SiteService
|
||||
}
|
||||
|
||||
public async Task<Result<DataConnection>> UpdateDataConnectionAsync(
|
||||
int connectionId, string name, string protocol, string? configuration, string user,
|
||||
int connectionId, string name, string protocol, string? primaryConfiguration,
|
||||
string? backupConfiguration, int failoverRetryCount, string user,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var connection = await _repository.GetDataConnectionByIdAsync(connectionId, cancellationToken);
|
||||
@@ -124,7 +131,9 @@ public class SiteService
|
||||
|
||||
connection.Name = name;
|
||||
connection.Protocol = protocol;
|
||||
connection.PrimaryConfiguration = configuration;
|
||||
connection.PrimaryConfiguration = primaryConfiguration;
|
||||
connection.BackupConfiguration = backupConfiguration;
|
||||
connection.FailoverRetryCount = failoverRetryCount;
|
||||
await _repository.UpdateDataConnectionAsync(connection, cancellationToken);
|
||||
await _repository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ public class SiteServiceTests
|
||||
_repoMock.Setup(r => r.SaveChangesAsync(It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(1);
|
||||
|
||||
var result = await _sut.CreateDataConnectionAsync(1, "OPC-Server1", "OpcUa", "{\"url\":\"opc.tcp://localhost\"}", "admin");
|
||||
var result = await _sut.CreateDataConnectionAsync(1, "OPC-Server1", "OpcUa", "{\"url\":\"opc.tcp://localhost\"}", null, 3, "admin");
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.Equal("OPC-Server1", result.Value.Name);
|
||||
|
||||
Reference in New Issue
Block a user