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 { private readonly string _instanceName; public SandboxNotifyHelper(string instanceName) { _instanceName = instanceName; } /// Selects the notification list to send to. public SandboxNotifyTarget To(string listName) => new(listName, _instanceName); /// /// 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 { private readonly string _listName; private readonly string _instanceName; internal SandboxNotifyTarget(string listName, string instanceName) { _listName = listName; _instanceName = instanceName; } /// /// 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")); }