feat: wire ExternalSystem, Database, and Notify APIs into script runtime
IServiceProvider now flows through the actor chain (DeploymentManagerActor → InstanceActor → ScriptActor → ScriptExecutionActor) so scripts can resolve IExternalSystemClient, IDatabaseGateway, and INotificationDeliveryService from DI. ScriptGlobals exposes ExternalSystem, Database, Notify, and Scripts as top-level properties so scripts can use them without the Instance. prefix.
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
using Akka.Actor;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ScadaLink.Commons.Interfaces.Services;
|
||||
using ScadaLink.Commons.Messages.ScriptExecution;
|
||||
using ScadaLink.HealthMonitoring;
|
||||
using ScadaLink.SiteRuntime.Scripts;
|
||||
@@ -30,7 +32,8 @@ public class ScriptExecutionActor : ReceiveActor
|
||||
IActorRef replyTo,
|
||||
string correlationId,
|
||||
ILogger logger,
|
||||
ISiteHealthCollector? healthCollector = null)
|
||||
ISiteHealthCollector? healthCollector = null,
|
||||
IServiceProvider? serviceProvider = null)
|
||||
{
|
||||
// Immediately begin execution
|
||||
var self = Self;
|
||||
@@ -39,7 +42,7 @@ public class ScriptExecutionActor : ReceiveActor
|
||||
ExecuteScript(
|
||||
scriptName, instanceName, compiledScript, parameters, callDepth,
|
||||
instanceActor, sharedScriptLibrary, options, replyTo, correlationId,
|
||||
self, parent, logger, healthCollector);
|
||||
self, parent, logger, healthCollector, serviceProvider);
|
||||
}
|
||||
|
||||
private static void ExecuteScript(
|
||||
@@ -56,16 +59,31 @@ public class ScriptExecutionActor : ReceiveActor
|
||||
IActorRef self,
|
||||
IActorRef parent,
|
||||
ILogger logger,
|
||||
ISiteHealthCollector? healthCollector)
|
||||
ISiteHealthCollector? healthCollector,
|
||||
IServiceProvider? serviceProvider)
|
||||
{
|
||||
var timeout = TimeSpan.FromSeconds(options.ScriptExecutionTimeoutSeconds);
|
||||
|
||||
// CTS must be created inside the async lambda so it outlives this method
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
IServiceScope? serviceScope = null;
|
||||
using var cts = new CancellationTokenSource(timeout);
|
||||
try
|
||||
{
|
||||
// Resolve integration services from DI (scoped lifetime)
|
||||
IExternalSystemClient? externalSystemClient = null;
|
||||
IDatabaseGateway? databaseGateway = null;
|
||||
INotificationDeliveryService? notificationService = null;
|
||||
|
||||
if (serviceProvider != null)
|
||||
{
|
||||
serviceScope = serviceProvider.CreateScope();
|
||||
externalSystemClient = serviceScope.ServiceProvider.GetService<IExternalSystemClient>();
|
||||
databaseGateway = serviceScope.ServiceProvider.GetService<IDatabaseGateway>();
|
||||
notificationService = serviceScope.ServiceProvider.GetService<INotificationDeliveryService>();
|
||||
}
|
||||
|
||||
var context = new ScriptRuntimeContext(
|
||||
instanceActor,
|
||||
self,
|
||||
@@ -74,7 +92,10 @@ public class ScriptExecutionActor : ReceiveActor
|
||||
options.MaxScriptCallDepth,
|
||||
timeout,
|
||||
instanceName,
|
||||
logger);
|
||||
logger,
|
||||
externalSystemClient,
|
||||
databaseGateway,
|
||||
notificationService);
|
||||
|
||||
var globals = new ScriptGlobals
|
||||
{
|
||||
@@ -123,6 +144,8 @@ public class ScriptExecutionActor : ReceiveActor
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Dispose the DI scope (and scoped services) after script execution completes
|
||||
serviceScope?.Dispose();
|
||||
// Stop self after execution completes
|
||||
self.Tell(PoisonPill.Instance);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user