feat(dcl): rename Configuration to PrimaryConfiguration, add BackupConfiguration and FailoverRetryCount

This commit is contained in:
Joseph Doherty
2026-03-22 08:18:31 -04:00
parent 5ca1be328c
commit 04af03980e
14 changed files with 31 additions and 20 deletions

View File

@@ -38,7 +38,7 @@ public static class DataConnectionCommands
var idOption = new Option<int>("--id") { Description = "Data connection ID", Required = true }; var idOption = new Option<int>("--id") { Description = "Data connection ID", Required = true };
var nameOption = new Option<string>("--name") { Description = "Connection name", Required = true }; var nameOption = new Option<string>("--name") { Description = "Connection name", Required = true };
var protocolOption = new Option<string>("--protocol") { Description = "Protocol", Required = true }; var protocolOption = new Option<string>("--protocol") { Description = "Protocol", Required = true };
var configOption = new Option<string?>("--configuration") { Description = "Configuration JSON" }; var configOption = new Option<string?>("--configuration") { Description = "Primary configuration JSON" };
var cmd = new Command("update") { Description = "Update a data connection" }; var cmd = new Command("update") { Description = "Update a data connection" };
cmd.Add(idOption); cmd.Add(idOption);
@@ -77,7 +77,7 @@ public static class DataConnectionCommands
var siteIdOption = new Option<int>("--site-id") { Description = "Site ID", Required = true }; var siteIdOption = new Option<int>("--site-id") { Description = "Site ID", Required = true };
var nameOption = new Option<string>("--name") { Description = "Connection name", 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 protocolOption = new Option<string>("--protocol") { Description = "Protocol (e.g. OpcUa)", Required = true };
var configOption = new Option<string?>("--configuration") { Description = "Connection configuration JSON" }; var configOption = new Option<string?>("--configuration") { Description = "Primary configuration JSON" };
var cmd = new Command("create") { Description = "Create a new data connection" }; var cmd = new Command("create") { Description = "Create a new data connection" };
cmd.Add(siteIdOption); cmd.Add(siteIdOption);

View File

@@ -100,7 +100,7 @@
_siteName = _sites.FirstOrDefault(s => s.Id == _formSiteId)?.Name ?? $"Site {_formSiteId}"; _siteName = _sites.FirstOrDefault(s => s.Id == _formSiteId)?.Name ?? $"Site {_formSiteId}";
_formName = _editingConnection.Name; _formName = _editingConnection.Name;
_formProtocol = _editingConnection.Protocol; _formProtocol = _editingConnection.Protocol;
_formConfiguration = _editingConnection.Configuration; _formConfiguration = _editingConnection.PrimaryConfiguration;
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -124,14 +124,14 @@
{ {
_editingConnection.Name = _formName.Trim(); _editingConnection.Name = _formName.Trim();
_editingConnection.Protocol = _formProtocol; _editingConnection.Protocol = _formProtocol;
_editingConnection.Configuration = _formConfiguration?.Trim(); _editingConnection.PrimaryConfiguration = _formConfiguration?.Trim();
await SiteRepository.UpdateDataConnectionAsync(_editingConnection); await SiteRepository.UpdateDataConnectionAsync(_editingConnection);
} }
else else
{ {
var conn = new DataConnection(_formName.Trim(), _formProtocol, _formSiteId) var conn = new DataConnection(_formName.Trim(), _formProtocol, _formSiteId)
{ {
Configuration = _formConfiguration?.Trim() PrimaryConfiguration = _formConfiguration?.Trim()
}; };
await SiteRepository.AddDataConnectionAsync(conn); await SiteRepository.AddDataConnectionAsync(conn);
} }

View File

@@ -50,7 +50,7 @@
<td>@conn.Name</td> <td>@conn.Name</td>
<td><span class="badge bg-secondary">@conn.Protocol</span></td> <td><span class="badge bg-secondary">@conn.Protocol</span></td>
<td>@(_siteLookup.GetValueOrDefault(conn.SiteId)?.Name ?? $"Site {conn.SiteId}")</td> <td>@(_siteLookup.GetValueOrDefault(conn.SiteId)?.Name ?? $"Site {conn.SiteId}")</td>
<td class="text-muted small text-truncate" style="max-width: 300px;">@(conn.Configuration ?? "—")</td> <td class="text-muted small text-truncate" style="max-width: 300px;">@(conn.PrimaryConfiguration ?? "—")</td>
<td> <td>
<button class="btn btn-outline-primary btn-sm py-0 px-1 me-1" <button class="btn btn-outline-primary btn-sm py-0 px-1 me-1"
@onclick='() => NavigationManager.NavigateTo($"/admin/data-connections/{conn.Id}/edit")'>Edit</button> @onclick='() => NavigationManager.NavigateTo($"/admin/data-connections/{conn.Id}/edit")'>Edit</button>

View File

@@ -6,7 +6,9 @@ public class DataConnection
public int SiteId { get; set; } public int SiteId { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string Protocol { get; set; } public string Protocol { get; set; }
public string? Configuration { get; set; } public string? PrimaryConfiguration { get; set; }
public string? BackupConfiguration { get; set; }
public int FailoverRetryCount { get; set; } = 3;
public DataConnection(string name, string protocol, int siteId) public DataConnection(string name, string protocol, int siteId)
{ {

View File

@@ -3,4 +3,6 @@ namespace ScadaLink.Commons.Messages.Artifacts;
public record DataConnectionArtifact( public record DataConnectionArtifact(
string Name, string Name,
string Protocol, string Protocol,
string? ConfigurationJson); string? PrimaryConfigurationJson,
string? BackupConfigurationJson,
int FailoverRetryCount = 3);

View File

@@ -2,6 +2,6 @@ namespace ScadaLink.Commons.Messages.Management;
public record ListDataConnectionsCommand(int? SiteId = null); public record ListDataConnectionsCommand(int? SiteId = null);
public record GetDataConnectionCommand(int DataConnectionId); public record GetDataConnectionCommand(int DataConnectionId);
public record CreateDataConnectionCommand(int SiteId, string Name, string Protocol, string? Configuration); public record CreateDataConnectionCommand(int SiteId, string Name, string Protocol, string? PrimaryConfiguration);
public record UpdateDataConnectionCommand(int DataConnectionId, string Name, string Protocol, string? Configuration); public record UpdateDataConnectionCommand(int DataConnectionId, string Name, string Protocol, string? PrimaryConfiguration);
public record DeleteDataConnectionCommand(int DataConnectionId); public record DeleteDataConnectionCommand(int DataConnectionId);

View File

@@ -43,9 +43,16 @@ public class DataConnectionConfiguration : IEntityTypeConfiguration<DataConnecti
.IsRequired() .IsRequired()
.HasMaxLength(50); .HasMaxLength(50);
builder.Property(d => d.Configuration) builder.Property(d => d.PrimaryConfiguration)
.HasMaxLength(4000); .HasMaxLength(4000);
builder.Property(d => d.BackupConfiguration)
.HasMaxLength(4000);
builder.Property(d => d.FailoverRetryCount)
.IsRequired()
.HasDefaultValue(3);
builder.HasOne<Site>() builder.HasOne<Site>()
.WithMany() .WithMany()
.HasForeignKey(d => d.SiteId) .HasForeignKey(d => d.SiteId)

View File

@@ -102,7 +102,7 @@ public class ArtifactDeploymentService
// Map data connections // Map data connections
var dataConnectionArtifacts = dataConnections.Select(dc => var dataConnectionArtifacts = dataConnections.Select(dc =>
new DataConnectionArtifact(dc.Name, dc.Protocol, dc.Configuration)).ToList(); new DataConnectionArtifact(dc.Name, dc.Protocol, dc.PrimaryConfiguration, dc.BackupConfiguration, dc.FailoverRetryCount)).ToList();
// Map SMTP configurations — use Host as the artifact name (matches SQLite PK on site) // Map SMTP configurations — use Host as the artifact name (matches SQLite PK on site)
var smtpArtifacts = smtpConfigurations.Select(smtp => var smtpArtifacts = smtpConfigurations.Select(smtp =>

View File

@@ -689,7 +689,7 @@ public class ManagementActor : ReceiveActor
private static async Task<object?> HandleCreateDataConnection(IServiceProvider sp, CreateDataConnectionCommand cmd, string user) private static async Task<object?> HandleCreateDataConnection(IServiceProvider sp, CreateDataConnectionCommand cmd, string user)
{ {
var repo = sp.GetRequiredService<ISiteRepository>(); var repo = sp.GetRequiredService<ISiteRepository>();
var conn = new DataConnection(cmd.Name, cmd.Protocol, cmd.SiteId) { Configuration = cmd.Configuration }; var conn = new DataConnection(cmd.Name, cmd.Protocol, cmd.SiteId) { PrimaryConfiguration = cmd.PrimaryConfiguration };
await repo.AddDataConnectionAsync(conn); await repo.AddDataConnectionAsync(conn);
await repo.SaveChangesAsync(); await repo.SaveChangesAsync();
await AuditAsync(sp, user, "Create", "DataConnection", conn.Id.ToString(), conn.Name, conn); await AuditAsync(sp, user, "Create", "DataConnection", conn.Id.ToString(), conn.Name, conn);
@@ -703,7 +703,7 @@ public class ManagementActor : ReceiveActor
?? throw new InvalidOperationException($"DataConnection with ID {cmd.DataConnectionId} not found."); ?? throw new InvalidOperationException($"DataConnection with ID {cmd.DataConnectionId} not found.");
conn.Name = cmd.Name; conn.Name = cmd.Name;
conn.Protocol = cmd.Protocol; conn.Protocol = cmd.Protocol;
conn.Configuration = cmd.Configuration; conn.PrimaryConfiguration = cmd.PrimaryConfiguration;
await repo.UpdateDataConnectionAsync(conn); await repo.UpdateDataConnectionAsync(conn);
await repo.SaveChangesAsync(); await repo.SaveChangesAsync();
await AuditAsync(sp, user, "Update", "DataConnection", conn.Id.ToString(), conn.Name, conn); await AuditAsync(sp, user, "Update", "DataConnection", conn.Id.ToString(), conn.Name, conn);

View File

@@ -615,7 +615,7 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers
foreach (var dc in command.DataConnections) foreach (var dc in command.DataConnections)
{ {
await _storage.StoreDataConnectionDefinitionAsync( await _storage.StoreDataConnectionDefinitionAsync(
dc.Name, dc.Protocol, dc.ConfigurationJson); dc.Name, dc.Protocol, dc.PrimaryConfigurationJson);
} }
} }

View File

@@ -185,7 +185,7 @@ public class SiteReplicationActor : ReceiveActor
if (command.DataConnections != null) if (command.DataConnections != null)
foreach (var dc in command.DataConnections) foreach (var dc in command.DataConnections)
await _storage.StoreDataConnectionDefinitionAsync(dc.Name, dc.Protocol, dc.ConfigurationJson); await _storage.StoreDataConnectionDefinitionAsync(dc.Name, dc.Protocol, dc.PrimaryConfigurationJson);
if (command.SmtpConfigurations != null) if (command.SmtpConfigurations != null)
foreach (var smtp in command.SmtpConfigurations) foreach (var smtp in command.SmtpConfigurations)

View File

@@ -90,7 +90,7 @@ public class FlatteningService
connections[attr.BoundDataConnectionName] = new ConnectionConfig connections[attr.BoundDataConnectionName] = new ConnectionConfig
{ {
Protocol = conn.Protocol, Protocol = conn.Protocol,
ConfigurationJson = conn.Configuration ConfigurationJson = conn.PrimaryConfiguration
}; };
} }
} }

View File

@@ -104,7 +104,7 @@ public class SiteService
if (string.IsNullOrWhiteSpace(protocol)) if (string.IsNullOrWhiteSpace(protocol))
return Result<DataConnection>.Failure("Protocol is required."); return Result<DataConnection>.Failure("Protocol is required.");
var connection = new DataConnection(name, protocol, siteId) { Configuration = configuration }; var connection = new DataConnection(name, protocol, siteId) { PrimaryConfiguration = configuration };
await _repository.AddDataConnectionAsync(connection, cancellationToken); await _repository.AddDataConnectionAsync(connection, cancellationToken);
await _repository.SaveChangesAsync(cancellationToken); await _repository.SaveChangesAsync(cancellationToken);
@@ -124,7 +124,7 @@ public class SiteService
connection.Name = name; connection.Name = name;
connection.Protocol = protocol; connection.Protocol = protocol;
connection.Configuration = configuration; connection.PrimaryConfiguration = configuration;
await _repository.UpdateDataConnectionAsync(connection, cancellationToken); await _repository.UpdateDataConnectionAsync(connection, cancellationToken);
await _repository.SaveChangesAsync(cancellationToken); await _repository.SaveChangesAsync(cancellationToken);

View File

@@ -200,7 +200,7 @@ public class FlatteningServiceTests
var connections = new Dictionary<int, DataConnection> var connections = new Dictionary<int, DataConnection>
{ {
[100] = new("OPC-Server1", "OpcUa", 1) { Id = 100, Configuration = "opc.tcp://localhost:4840" } [100] = new("OPC-Server1", "OpcUa", 1) { Id = 100, PrimaryConfiguration = "opc.tcp://localhost:4840" }
}; };
var result = _sut.Flatten( var result = _sut.Flatten(