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:
@@ -7,7 +7,7 @@ public record AlarmStateChanged(
|
||||
string AlarmName,
|
||||
AlarmState State,
|
||||
int Priority,
|
||||
DateTimeOffset Timestamp)
|
||||
DateTimeOffset Timestamp) : ISiteStreamEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Severity level when <see cref="State"/> is <see cref="AlarmState.Active"/>.
|
||||
|
||||
@@ -6,4 +6,4 @@ public record AttributeValueChanged(
|
||||
string AttributeName,
|
||||
object? Value,
|
||||
string Quality,
|
||||
DateTimeOffset Timestamp);
|
||||
DateTimeOffset Timestamp) : ISiteStreamEvent;
|
||||
|
||||
10
src/ScadaLink.Commons/Messages/Streaming/ISiteStreamEvent.cs
Normal file
10
src/ScadaLink.Commons/Messages/Streaming/ISiteStreamEvent.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace ScadaLink.Commons.Messages.Streaming;
|
||||
|
||||
/// <summary>
|
||||
/// Marker interface for events published to the site-wide stream
|
||||
/// (attribute value changes and alarm state changes).
|
||||
/// </summary>
|
||||
public interface ISiteStreamEvent
|
||||
{
|
||||
string InstanceUniqueName { get; }
|
||||
}
|
||||
52
src/ScadaLink.Commons/Types/ScriptArgs.cs
Normal file
52
src/ScadaLink.Commons/Types/ScriptArgs.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ScadaLink.Commons.Types;
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the loosely-typed <c>parameters</c> argument of a script call
|
||||
/// (<c>Scripts.CallShared</c>, <c>Instance.CallScript</c>,
|
||||
/// <c>Children["X"].CallScript</c>, <c>Parent.CallScript</c>,
|
||||
/// <c>Route.To().Call</c>) into the dictionary the runtime carries.
|
||||
///
|
||||
/// Accepts: <c>null</c>; an existing dictionary; or any object whose public
|
||||
/// properties become the parameter entries — so callers can pass an anonymous
|
||||
/// object, <c>new { name = "Bob", count = 3 }</c>, instead of building a
|
||||
/// <c>Dictionary<string, object?></c> by hand.
|
||||
/// </summary>
|
||||
public static class ScriptArgs
|
||||
{
|
||||
public static IReadOnlyDictionary<string, object?>? Normalize(object? parameters)
|
||||
{
|
||||
switch (parameters)
|
||||
{
|
||||
case null:
|
||||
return null;
|
||||
case IReadOnlyDictionary<string, object?> roDict:
|
||||
return roDict;
|
||||
case IDictionary<string, object?> dict:
|
||||
return new Dictionary<string, object?>(dict);
|
||||
case IDictionary raw:
|
||||
{
|
||||
var result = new Dictionary<string, object?>();
|
||||
foreach (DictionaryEntry entry in raw)
|
||||
result[entry.Key?.ToString() ?? string.Empty] = entry.Value;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
var type = parameters.GetType();
|
||||
if (type.IsPrimitive || parameters is string or decimal)
|
||||
throw new ArgumentException(
|
||||
$"Script call parameters must be an object or dictionary, not {type.Name}.",
|
||||
nameof(parameters));
|
||||
|
||||
var bag = new Dictionary<string, object?>();
|
||||
foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
if (prop.GetIndexParameters().Length > 0) continue;
|
||||
bag[prop.Name] = prop.GetValue(parameters);
|
||||
}
|
||||
return bag;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user