diff --git a/src/ScadaLink.DeploymentManager/ArtifactDeploymentService.cs b/src/ScadaLink.DeploymentManager/ArtifactDeploymentService.cs index 15571bd..d216afe 100644 --- a/src/ScadaLink.DeploymentManager/ArtifactDeploymentService.cs +++ b/src/ScadaLink.DeploymentManager/ArtifactDeploymentService.cs @@ -12,8 +12,8 @@ namespace ScadaLink.DeploymentManager; /// /// 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 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; } + /// + /// Collects all artifact types from repositories and builds a . + /// + public async Task 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(); + 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); + } + /// /// Deploys artifacts to all sites. Returns per-site result matrix. /// diff --git a/src/ScadaLink.Host/Program.cs b/src/ScadaLink.Host/Program.cs index 11f118d..cf4a20b 100644 --- a/src/ScadaLink.Host/Program.cs +++ b/src/ScadaLink.Host/Program.cs @@ -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(); diff --git a/src/ScadaLink.Host/appsettings.Site.json b/src/ScadaLink.Host/appsettings.Site.json index 95b47c7..fc68384 100644 --- a/src/ScadaLink.Host/appsettings.Site.json +++ b/src/ScadaLink.Host/appsettings.Site.json @@ -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", diff --git a/src/ScadaLink.SiteRuntime/ServiceCollectionExtensions.cs b/src/ScadaLink.SiteRuntime/ServiceCollectionExtensions.cs index eca99ce..7888a1c 100644 --- a/src/ScadaLink.SiteRuntime/ServiceCollectionExtensions.cs +++ b/src/ScadaLink.SiteRuntime/ServiceCollectionExtensions.cs @@ -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(); + // Site-local repository implementations backed by SQLite + services.AddScoped(); + services.AddScoped(); + return services; } diff --git a/tests/ScadaLink.DeploymentManager.Tests/ArtifactDeploymentServiceTests.cs b/tests/ScadaLink.DeploymentManager.Tests/ArtifactDeploymentServiceTests.cs index 55af776..5dde1d9 100644 --- a/tests/ScadaLink.DeploymentManager.Tests/ArtifactDeploymentServiceTests.cs +++ b/tests/ScadaLink.DeploymentManager.Tests/ArtifactDeploymentServiceTests.cs @@ -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(); _deploymentRepo = Substitute.For(); + _templateRepo = Substitute.For(); + _externalSystemRepo = Substitute.For(); + _notificationRepo = Substitute.For(); _audit = Substitute.For(); } @@ -72,7 +78,8 @@ public class ArtifactDeploymentServiceTests NullLogger.Instance); return new ArtifactDeploymentService( - _siteRepo, _deploymentRepo, comms, _audit, + _siteRepo, _deploymentRepo, _templateRepo, _externalSystemRepo, _notificationRepo, + comms, _audit, Options.Create(new DeploymentManagerOptions()), NullLogger.Instance); }