using System.Data.Common;
using ScadaLink.Commons.Interfaces.Services;
using ScadaLink.Commons.Messages.Notification;
namespace ScadaLink.CentralUI.ScriptAnalysis;
///
/// User-facing surface for ExternalSystem.Call /
/// ExternalSystem.CachedCall 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
/// ; with a real client wired in (a Test
/// Run) calls hit the live HTTP path.
///
public class SandboxExternalHelper
{
private readonly IExternalSystemClient? _client;
private readonly string _instanceName;
public SandboxExternalHelper(IExternalSystemClient? client, string instanceName)
{
_client = client;
_instanceName = instanceName;
}
public Task Call(
string systemName,
string methodName,
IReadOnlyDictionary? 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 CachedCall(
string systemName,
string methodName,
IReadOnlyDictionary? 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 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? 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);
}
}
///
/// Sandbox mirror of ScadaLink.SiteRuntime.Scripts.NotifyHelper — the
/// Notify global. Signature-faithful to production so the same user code
/// (Notify.To(...).Send(...) / Notify.Status(...)) compiles
/// identically against both surfaces.
///
/// In the Notification Outbox design production no longer delivers notification
/// email inline — Notify.Send enqueues into the site Store-and-Forward
/// Engine and returns a NotificationId. The sandbox has no S&F engine
/// and no central, so it is a pure no-op fake: Send returns a generated
/// fake id and Status returns a placeholder .
/// Nothing is delivered.
///
public class SandboxNotifyHelper
{
/// Selects the notification list to send to.
public SandboxNotifyTarget To(string listName) =>
new();
///
/// Queries the delivery status of a previously-sent notification. The
/// sandbox never delivers, so this always reports the placeholder
/// Unknown status — it exists for signature fidelity with
/// NotifyHelper.Status.
///
public Task Status(string notificationId) =>
Task.FromResult(new NotificationDeliveryStatus("Unknown", 0, null, null));
}
///
/// Sandbox mirror of ScadaLink.SiteRuntime.Scripts.NotifyTarget — the
/// target of Notify.To("listName").
///
public class SandboxNotifyTarget
{
internal SandboxNotifyTarget()
{
}
///
/// Mirrors NotifyTarget.Send — returns a NotificationId. In
/// the sandbox nothing is enqueued or delivered; a fake id is returned so
/// the call type-checks identically to production.
///
public Task Send(string subject, string message, CancellationToken cancellationToken = default) =>
Task.FromResult(Guid.NewGuid().ToString("N"));
}