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:
118
src/ScadaLink.CentralUI/ScriptAnalysis/SandboxHostHelpers.cs
Normal file
118
src/ScadaLink.CentralUI/ScriptAnalysis/SandboxHostHelpers.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System.Data.Common;
|
||||
using ScadaLink.Commons.Interfaces.Services;
|
||||
|
||||
namespace ScadaLink.CentralUI.ScriptAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// User-facing surface for <c>ExternalSystem.Call</c> /
|
||||
/// <c>ExternalSystem.CachedCall</c> inside a Test Run. Mirrors
|
||||
/// ExternalSystemHelper in ScadaLink.SiteRuntime.Scripts.ScriptRuntimeContext
|
||||
/// so the same user code compiles against both. When constructed with a null
|
||||
/// client (the editor's metadata-only analysis pass) every call throws
|
||||
/// <see cref="ScriptSandboxException"/>; with a real client wired in (a Test
|
||||
/// Run) calls hit the live HTTP path.
|
||||
/// </summary>
|
||||
public class SandboxExternalHelper
|
||||
{
|
||||
private readonly IExternalSystemClient? _client;
|
||||
private readonly string _instanceName;
|
||||
|
||||
public SandboxExternalHelper(IExternalSystemClient? client, string instanceName)
|
||||
{
|
||||
_client = client;
|
||||
_instanceName = instanceName;
|
||||
}
|
||||
|
||||
public Task<ExternalCallResult> Call(
|
||||
string systemName,
|
||||
string methodName,
|
||||
IReadOnlyDictionary<string, object?>? parameters = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_client == null)
|
||||
throw new ScriptSandboxException(
|
||||
$"External.Call(\"{systemName}\", \"{methodName}\") — external system client not configured for Test Run.");
|
||||
return _client.CallAsync(systemName, methodName, parameters, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<ExternalCallResult> CachedCall(
|
||||
string systemName,
|
||||
string methodName,
|
||||
IReadOnlyDictionary<string, object?>? parameters = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_client == null)
|
||||
throw new ScriptSandboxException(
|
||||
$"External.CachedCall(\"{systemName}\", \"{methodName}\") — external system client not configured for Test Run.");
|
||||
return _client.CachedCallAsync(systemName, methodName, parameters, _instanceName, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
public class SandboxDatabaseHelper
|
||||
{
|
||||
private readonly IDatabaseGateway? _gateway;
|
||||
private readonly string _instanceName;
|
||||
|
||||
public SandboxDatabaseHelper(IDatabaseGateway? gateway, string instanceName)
|
||||
{
|
||||
_gateway = gateway;
|
||||
_instanceName = instanceName;
|
||||
}
|
||||
|
||||
public Task<DbConnection> Connection(string name, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_gateway == null)
|
||||
throw new ScriptSandboxException(
|
||||
$"Database.Connection(\"{name}\") — database gateway not configured for Test Run.");
|
||||
return _gateway.GetConnectionAsync(name, cancellationToken);
|
||||
}
|
||||
|
||||
public Task CachedWrite(
|
||||
string name,
|
||||
string sql,
|
||||
IReadOnlyDictionary<string, object?>? parameters = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_gateway == null)
|
||||
throw new ScriptSandboxException(
|
||||
$"Database.CachedWrite(\"{name}\") — database gateway not configured for Test Run.");
|
||||
return _gateway.CachedWriteAsync(name, sql, parameters, _instanceName, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
public class SandboxNotifyHelper
|
||||
{
|
||||
private readonly INotificationDeliveryService? _service;
|
||||
private readonly string _instanceName;
|
||||
|
||||
public SandboxNotifyHelper(INotificationDeliveryService? service, string instanceName)
|
||||
{
|
||||
_service = service;
|
||||
_instanceName = instanceName;
|
||||
}
|
||||
|
||||
public SandboxNotifyTarget To(string listName) =>
|
||||
new(listName, _service, _instanceName);
|
||||
}
|
||||
|
||||
public class SandboxNotifyTarget
|
||||
{
|
||||
private readonly string _listName;
|
||||
private readonly INotificationDeliveryService? _service;
|
||||
private readonly string _instanceName;
|
||||
|
||||
internal SandboxNotifyTarget(string listName, INotificationDeliveryService? service, string instanceName)
|
||||
{
|
||||
_listName = listName;
|
||||
_service = service;
|
||||
_instanceName = instanceName;
|
||||
}
|
||||
|
||||
public Task<NotificationResult> Send(string subject, string message, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_service == null)
|
||||
throw new ScriptSandboxException(
|
||||
$"Notify.To(\"{_listName}\").Send(...) — notification service not configured for Test Run.");
|
||||
return _service.SendAsync(_listName, subject, message, _instanceName, cancellationToken);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user