feat: wire site-local repos, remove config DB from Site, update artifact service
- SiteExternalSystemRepository and SiteNotificationRepository registered in Site DI - Removed AddConfigurationDatabase from Site role in Program.cs - Removed ConfigurationDb from appsettings.Site.json - ArtifactDeploymentService collects all 6 artifact types including data connections and SMTP
This commit is contained in:
@@ -12,8 +12,8 @@ namespace ScadaLink.DeploymentManager;
|
||||
|
||||
/// <summary>
|
||||
/// WP-7: System-wide artifact deployment.
|
||||
/// Broadcasts artifacts (shared scripts, external systems, notification lists, DB connections)
|
||||
/// to all sites with per-site tracking.
|
||||
/// Broadcasts artifacts (shared scripts, external systems, notification lists, DB connections,
|
||||
/// data connections, and SMTP configurations) to all sites with per-site tracking.
|
||||
///
|
||||
/// - Successful sites are NOT rolled back on other failures.
|
||||
/// - Failed sites are retryable individually.
|
||||
@@ -24,6 +24,9 @@ public class ArtifactDeploymentService
|
||||
{
|
||||
private readonly ISiteRepository _siteRepo;
|
||||
private readonly IDeploymentManagerRepository _deploymentRepo;
|
||||
private readonly ITemplateEngineRepository _templateRepo;
|
||||
private readonly IExternalSystemRepository _externalSystemRepo;
|
||||
private readonly INotificationRepository _notificationRepo;
|
||||
private readonly CommunicationService _communicationService;
|
||||
private readonly IAuditService _auditService;
|
||||
private readonly DeploymentManagerOptions _options;
|
||||
@@ -32,6 +35,9 @@ public class ArtifactDeploymentService
|
||||
public ArtifactDeploymentService(
|
||||
ISiteRepository siteRepo,
|
||||
IDeploymentManagerRepository deploymentRepo,
|
||||
ITemplateEngineRepository templateRepo,
|
||||
IExternalSystemRepository externalSystemRepo,
|
||||
INotificationRepository notificationRepo,
|
||||
CommunicationService communicationService,
|
||||
IAuditService auditService,
|
||||
IOptions<DeploymentManagerOptions> options,
|
||||
@@ -39,12 +45,80 @@ public class ArtifactDeploymentService
|
||||
{
|
||||
_siteRepo = siteRepo;
|
||||
_deploymentRepo = deploymentRepo;
|
||||
_templateRepo = templateRepo;
|
||||
_externalSystemRepo = externalSystemRepo;
|
||||
_notificationRepo = notificationRepo;
|
||||
_communicationService = communicationService;
|
||||
_auditService = auditService;
|
||||
_options = options.Value;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collects all artifact types from repositories and builds a <see cref="DeployArtifactsCommand"/>.
|
||||
/// </summary>
|
||||
public async Task<DeployArtifactsCommand> BuildDeployArtifactsCommandAsync(
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var sharedScripts = await _templateRepo.GetAllSharedScriptsAsync(cancellationToken);
|
||||
var externalSystems = await _externalSystemRepo.GetAllExternalSystemsAsync(cancellationToken);
|
||||
var dbConnections = await _externalSystemRepo.GetAllDatabaseConnectionsAsync(cancellationToken);
|
||||
var notificationLists = await _notificationRepo.GetAllNotificationListsAsync(cancellationToken);
|
||||
var dataConnections = await _siteRepo.GetAllDataConnectionsAsync(cancellationToken);
|
||||
var smtpConfigurations = await _notificationRepo.GetAllSmtpConfigurationsAsync(cancellationToken);
|
||||
|
||||
// Map shared scripts
|
||||
var scriptArtifacts = sharedScripts.Select(s =>
|
||||
new SharedScriptArtifact(s.Name, s.Code, s.ParameterDefinitions, s.ReturnDefinition)).ToList();
|
||||
|
||||
// Map external systems (serialize methods per system)
|
||||
var externalSystemArtifacts = new List<ExternalSystemArtifact>();
|
||||
foreach (var es in externalSystems)
|
||||
{
|
||||
var methods = await _externalSystemRepo.GetMethodsByExternalSystemIdAsync(es.Id, cancellationToken);
|
||||
var methodsJson = methods.Count > 0
|
||||
? JsonSerializer.Serialize(methods.Select(m => new
|
||||
{
|
||||
m.Name,
|
||||
m.HttpMethod,
|
||||
m.Path,
|
||||
m.ParameterDefinitions,
|
||||
m.ReturnDefinition
|
||||
}))
|
||||
: null;
|
||||
externalSystemArtifacts.Add(new ExternalSystemArtifact(
|
||||
es.Name, es.EndpointUrl, es.AuthType, es.AuthConfiguration, methodsJson));
|
||||
}
|
||||
|
||||
// Map database connections
|
||||
var dbConnectionArtifacts = dbConnections.Select(d =>
|
||||
new DatabaseConnectionArtifact(d.Name, d.ConnectionString, d.MaxRetries, d.RetryDelay)).ToList();
|
||||
|
||||
// Map notification lists
|
||||
var notificationListArtifacts = notificationLists.Select(nl =>
|
||||
new NotificationListArtifact(nl.Name, nl.Recipients.Select(r => r.EmailAddress).ToList())).ToList();
|
||||
|
||||
// Map data connections
|
||||
var dataConnectionArtifacts = dataConnections.Select(dc =>
|
||||
new DataConnectionArtifact(dc.Name, dc.Protocol, dc.Configuration)).ToList();
|
||||
|
||||
// Map SMTP configurations — use Host as the artifact name (matches SQLite PK on site)
|
||||
var smtpArtifacts = smtpConfigurations.Select(smtp =>
|
||||
new SmtpConfigurationArtifact(
|
||||
$"{smtp.Host}:{smtp.Port}", smtp.Host, smtp.Port, smtp.AuthType, smtp.FromAddress,
|
||||
smtp.Credentials, null, smtp.TlsMode)).ToList();
|
||||
|
||||
return new DeployArtifactsCommand(
|
||||
Guid.NewGuid().ToString("N"),
|
||||
scriptArtifacts,
|
||||
externalSystemArtifacts,
|
||||
dbConnectionArtifacts,
|
||||
notificationListArtifacts,
|
||||
dataConnectionArtifacts,
|
||||
smtpArtifacts,
|
||||
DateTimeOffset.UtcNow);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deploys artifacts to all sites. Returns per-site result matrix.
|
||||
/// </summary>
|
||||
|
||||
@@ -152,14 +152,8 @@ try
|
||||
services.AddExternalSystemGateway();
|
||||
services.AddNotificationService();
|
||||
|
||||
// Configuration database (read-only access for external system definitions, notification lists)
|
||||
var configDbConnectionString = context.Configuration["ScadaLink:Database:ConfigurationDb"];
|
||||
if (!string.IsNullOrWhiteSpace(configDbConnectionString))
|
||||
{
|
||||
services.AddConfigurationDatabase(configDbConnectionString);
|
||||
}
|
||||
|
||||
// Site-only components — AddSiteRuntime registers SiteStorageService with SQLite path
|
||||
// and site-local repository implementations (IExternalSystemRepository, INotificationRepository)
|
||||
var siteDbPath = context.Configuration["ScadaLink:Database:SiteDbPath"] ?? "site.db";
|
||||
services.AddSiteRuntime($"Data Source={siteDbPath}");
|
||||
services.AddDataConnectionLayer();
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
"MinNrOfMembers": 1
|
||||
},
|
||||
"Database": {
|
||||
"SiteDbPath": "./data/scadalink.db",
|
||||
"ConfigurationDb": "Server=localhost,1433;Database=ScadaLinkConfig;User Id=scadalink_app;Password=ScadaLink_Dev1#;TrustServerCertificate=true"
|
||||
"SiteDbPath": "./data/scadalink.db"
|
||||
},
|
||||
"DataConnection": {
|
||||
"ReconnectInterval": "00:00:05",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ScadaLink.Commons.Interfaces.Repositories;
|
||||
using ScadaLink.SiteRuntime.Persistence;
|
||||
using ScadaLink.SiteRuntime.Repositories;
|
||||
using ScadaLink.SiteRuntime.Scripts;
|
||||
|
||||
namespace ScadaLink.SiteRuntime;
|
||||
@@ -38,6 +40,10 @@ public static class ServiceCollectionExtensions
|
||||
// WP-17: Shared script library
|
||||
services.AddSingleton<SharedScriptLibrary>();
|
||||
|
||||
// Site-local repository implementations backed by SQLite
|
||||
services.AddScoped<IExternalSystemRepository, SiteExternalSystemRepository>();
|
||||
services.AddScoped<INotificationRepository, SiteNotificationRepository>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,12 +16,18 @@ public class ArtifactDeploymentServiceTests
|
||||
{
|
||||
private readonly ISiteRepository _siteRepo;
|
||||
private readonly IDeploymentManagerRepository _deploymentRepo;
|
||||
private readonly ITemplateEngineRepository _templateRepo;
|
||||
private readonly IExternalSystemRepository _externalSystemRepo;
|
||||
private readonly INotificationRepository _notificationRepo;
|
||||
private readonly IAuditService _audit;
|
||||
|
||||
public ArtifactDeploymentServiceTests()
|
||||
{
|
||||
_siteRepo = Substitute.For<ISiteRepository>();
|
||||
_deploymentRepo = Substitute.For<IDeploymentManagerRepository>();
|
||||
_templateRepo = Substitute.For<ITemplateEngineRepository>();
|
||||
_externalSystemRepo = Substitute.For<IExternalSystemRepository>();
|
||||
_notificationRepo = Substitute.For<INotificationRepository>();
|
||||
_audit = Substitute.For<IAuditService>();
|
||||
}
|
||||
|
||||
@@ -72,7 +78,8 @@ public class ArtifactDeploymentServiceTests
|
||||
NullLogger<CommunicationService>.Instance);
|
||||
|
||||
return new ArtifactDeploymentService(
|
||||
_siteRepo, _deploymentRepo, comms, _audit,
|
||||
_siteRepo, _deploymentRepo, _templateRepo, _externalSystemRepo, _notificationRepo,
|
||||
comms, _audit,
|
||||
Options.Create(new DeploymentManagerOptions()),
|
||||
NullLogger<ArtifactDeploymentService>.Instance);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user