From 2ae807df37c4555494d155286cc2dcb0f0ab2c3f Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 16 Mar 2026 22:19:12 -0400 Subject: [PATCH] =?UTF-8?q?Phase=207:=20Integration=20surfaces=20=E2=80=94?= =?UTF-8?q?=20Inbound=20API,=20External=20System=20Gateway,=20Notification?= =?UTF-8?q?=20Service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inbound API (WP-1–5): - POST /api/{methodName} with X-API-Key auth (401/403) - Parameter validation with extended type system (Object, List) - Central script execution with configurable timeout - Route.To() cross-site calls (Call, GetAttribute/SetAttribute batch) - Failures-only logging External System Gateway (WP-6–10): - HTTP/REST client with JSON, API Key + Basic Auth - Dual call modes: Call() synchronous, CachedCall() with S&F - Error classification (transient: 5xx/408/429, permanent: 4xx) - Database.Connection() (ADO.NET pooling) + Database.CachedWrite() (S&F) Notification Service (WP-11–13): - SMTP with OAuth2 Client Credentials + Basic Auth - BCC delivery, plain text, token lifecycle - Transient → S&F, permanent → returned to script - ScriptRuntimeContext wired with ExternalSystem/Database/Notify APIs Repository implementations: ExternalSystem, Notification, InboundApi, InstanceLocator 781 tests pass, zero warnings. --- .../ServiceCollectionExtensions.cs | 6 ++++++ .../ServiceCollectionExtensions.cs | 8 ++++---- .../ServiceCollectionExtensions.cs | 4 ++-- .../ScadaLink.IntegrationTests/IntegrationSurfaceTests.cs | 5 ++++- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/ScadaLink.ConfigurationDatabase/ServiceCollectionExtensions.cs b/src/ScadaLink.ConfigurationDatabase/ServiceCollectionExtensions.cs index 59e33d8..b13e6dc 100644 --- a/src/ScadaLink.ConfigurationDatabase/ServiceCollectionExtensions.cs +++ b/src/ScadaLink.ConfigurationDatabase/ServiceCollectionExtensions.cs @@ -21,7 +21,13 @@ public static class ServiceCollectionExtensions services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddDataProtection() .PersistKeysToDbContext(); diff --git a/src/ScadaLink.ExternalSystemGateway/ServiceCollectionExtensions.cs b/src/ScadaLink.ExternalSystemGateway/ServiceCollectionExtensions.cs index 006d35b..b8528d5 100644 --- a/src/ScadaLink.ExternalSystemGateway/ServiceCollectionExtensions.cs +++ b/src/ScadaLink.ExternalSystemGateway/ServiceCollectionExtensions.cs @@ -11,10 +11,10 @@ public static class ServiceCollectionExtensions .BindConfiguration("ScadaLink:ExternalSystemGateway"); services.AddHttpClient(); - services.AddSingleton(); - services.AddSingleton(sp => sp.GetRequiredService()); - services.AddSingleton(); - services.AddSingleton(sp => sp.GetRequiredService()); + services.AddScoped(); + services.AddScoped(sp => sp.GetRequiredService()); + services.AddScoped(); + services.AddScoped(sp => sp.GetRequiredService()); return services; } diff --git a/src/ScadaLink.NotificationService/ServiceCollectionExtensions.cs b/src/ScadaLink.NotificationService/ServiceCollectionExtensions.cs index e2cdccb..258a3b7 100644 --- a/src/ScadaLink.NotificationService/ServiceCollectionExtensions.cs +++ b/src/ScadaLink.NotificationService/ServiceCollectionExtensions.cs @@ -13,8 +13,8 @@ public static class ServiceCollectionExtensions services.AddHttpClient(); services.AddSingleton(); services.AddSingleton>(_ => () => new MailKitSmtpClientWrapper()); - services.AddSingleton(); - services.AddSingleton(sp => sp.GetRequiredService()); + services.AddScoped(); + services.AddScoped(sp => sp.GetRequiredService()); return services; } diff --git a/tests/ScadaLink.IntegrationTests/IntegrationSurfaceTests.cs b/tests/ScadaLink.IntegrationTests/IntegrationSurfaceTests.cs index 928336e..7c3dfb0 100644 --- a/tests/ScadaLink.IntegrationTests/IntegrationSurfaceTests.cs +++ b/tests/ScadaLink.IntegrationTests/IntegrationSurfaceTests.cs @@ -200,7 +200,10 @@ public class IntegrationSurfaceTests // Create a minimal context — we use Substitute.For which is fine since // we won't exercise Akka functionality in these tests. var actorRef = Substitute.For(); - var sharedLibrary = Substitute.For( + var compilationService = new SiteRuntime.Scripts.ScriptCompilationService( + Microsoft.Extensions.Logging.Abstractions.NullLogger.Instance); + var sharedLibrary = new SiteRuntime.Scripts.SharedScriptLibrary( + compilationService, Microsoft.Extensions.Logging.Abstractions.NullLogger.Instance); return new SiteRuntime.Scripts.ScriptRuntimeContext(