feat(scripts): realign Test Run with runtime API, add anonymous-object calls and instance binding
The Test Run sandbox and Monaco analysis modelled a script API that had drifted from the site runtime's ScriptGlobals, so real scripts failed to compile in Test Run. Realign both to the runtime surface (Instance/Scripts/ExternalSystem/Attributes/Children/Parent) and drop the duplicate ScriptHost stub so the two cannot diverge again. - Script calls (Scripts.CallShared, Instance.CallScript, Route.To().Call) accept an anonymous object instead of a hand-built dictionary, via a shared ScriptArgs normalizer; existing dictionary calls still compile. - Test Run can optionally bind to a deployed instance, so Instance/ Attributes/CallScript route to it cross-site; adds site-side RouteToGetAttributes/RouteToSetAttributes handlers. - Adds Test Run panels to the API method and template script editors. - Fixes the TestDatabaseQuery seed script, which queried a table that never existed. Also commits unrelated in-progress work already in the tree: the health monitoring report loop, site streaming changes, and the Admin/Design data-connection and SMTP page reorganization.
This commit is contained in:
@@ -4,6 +4,7 @@ using ScadaLink.Commons.Messages.Artifacts;
|
||||
using ScadaLink.Commons.Messages.DebugView;
|
||||
using ScadaLink.Commons.Messages.Deployment;
|
||||
using ScadaLink.Commons.Messages.InboundApi;
|
||||
using ScadaLink.Commons.Messages.Instance;
|
||||
using ScadaLink.Commons.Messages.Lifecycle;
|
||||
using ScadaLink.Commons.Messages.ScriptExecution;
|
||||
using ScadaLink.Commons.Types.Enums;
|
||||
@@ -81,6 +82,8 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers
|
||||
|
||||
// Inbound API Route.To().Call() — route to Instance Actors
|
||||
Receive<RouteToCallRequest>(RouteInboundApiCall);
|
||||
Receive<RouteToGetAttributesRequest>(RouteInboundApiGetAttributes);
|
||||
Receive<RouteToSetAttributesRequest>(RouteInboundApiSetAttributes);
|
||||
|
||||
// Internal startup messages
|
||||
Receive<StartupConfigsLoaded>(HandleStartupConfigsLoaded);
|
||||
@@ -567,6 +570,75 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads attribute values from a deployed instance for a Route.To().GetAttribute(s)
|
||||
/// call (or a central Test Run bound to the instance). Asks the Instance Actor
|
||||
/// per attribute and combines the results.
|
||||
/// </summary>
|
||||
private void RouteInboundApiGetAttributes(RouteToGetAttributesRequest request)
|
||||
{
|
||||
if (!_instanceActors.TryGetValue(request.InstanceUniqueName, out var instanceActor))
|
||||
{
|
||||
Sender.Tell(new RouteToGetAttributesResponse(
|
||||
request.CorrelationId, new Dictionary<string, object?>(), false,
|
||||
$"Instance '{request.InstanceUniqueName}' not found on this site.",
|
||||
DateTimeOffset.UtcNow));
|
||||
return;
|
||||
}
|
||||
|
||||
var sender = Sender;
|
||||
var names = request.AttributeNames;
|
||||
var asks = names
|
||||
.Select(name => instanceActor.Ask<GetAttributeResponse>(
|
||||
new GetAttributeRequest(
|
||||
request.CorrelationId, request.InstanceUniqueName, name, DateTimeOffset.UtcNow),
|
||||
TimeSpan.FromSeconds(30)))
|
||||
.ToArray();
|
||||
|
||||
Task.WhenAll(asks).ContinueWith(t =>
|
||||
{
|
||||
if (t.IsCompletedSuccessfully)
|
||||
{
|
||||
var values = new Dictionary<string, object?>();
|
||||
for (var i = 0; i < names.Count; i++)
|
||||
values[names[i]] = t.Result[i].Found ? t.Result[i].Value : null;
|
||||
return new RouteToGetAttributesResponse(
|
||||
request.CorrelationId, values, true, null, DateTimeOffset.UtcNow);
|
||||
}
|
||||
return new RouteToGetAttributesResponse(
|
||||
request.CorrelationId, new Dictionary<string, object?>(), false,
|
||||
t.Exception?.GetBaseException().Message ?? "Attribute read timed out",
|
||||
DateTimeOffset.UtcNow);
|
||||
}).PipeTo(sender);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes attribute values on a deployed instance for a Route.To().SetAttribute(s)
|
||||
/// call (or a central Test Run bound to the instance). Writes are Tell'd to the
|
||||
/// Instance Actor — serialized through its mailbox — and acknowledged optimistically,
|
||||
/// matching the fire-and-forget semantics of Instance.SetAttribute.
|
||||
/// </summary>
|
||||
private void RouteInboundApiSetAttributes(RouteToSetAttributesRequest request)
|
||||
{
|
||||
if (!_instanceActors.TryGetValue(request.InstanceUniqueName, out var instanceActor))
|
||||
{
|
||||
Sender.Tell(new RouteToSetAttributesResponse(
|
||||
request.CorrelationId, false,
|
||||
$"Instance '{request.InstanceUniqueName}' not found on this site.",
|
||||
DateTimeOffset.UtcNow));
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (name, value) in request.AttributeValues)
|
||||
{
|
||||
instanceActor.Tell(new SetStaticAttributeCommand(
|
||||
request.CorrelationId, request.InstanceUniqueName, name, value, DateTimeOffset.UtcNow));
|
||||
}
|
||||
|
||||
Sender.Tell(new RouteToSetAttributesResponse(
|
||||
request.CorrelationId, true, null, DateTimeOffset.UtcNow));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WP-33: Handles system-wide artifact deployment (shared scripts, external systems, etc.).
|
||||
/// Persists artifacts to SiteStorageService and recompiles shared scripts.
|
||||
|
||||
Reference in New Issue
Block a user