feat(notification-outbox): populate SourceScript on outbound notifications
FU3: thread the executing script identifier from the script-execution context down to the Notify outbox API so NotifyTarget.Send stamps NotificationSubmit.SourceScript instead of leaving it null. - ScriptRuntimeContext / NotifyHelper / NotifyTarget take an optional sourceScript value, carried through to NotificationSubmit.SourceScript. - ScriptExecutionActor supplies "ScriptActor:<scriptName>", matching the Site Event Logging "Source" convention used for script error events. - AlarmExecutionActor builds the context without the S&F engine, so its Notify API is inert; sourceScript defaults to null there.
This commit is contained in:
@@ -125,7 +125,10 @@ public class ScriptExecutionActor : ReceiveActor
|
|||||||
databaseGateway,
|
databaseGateway,
|
||||||
storeAndForward,
|
storeAndForward,
|
||||||
siteCommunicationActor,
|
siteCommunicationActor,
|
||||||
siteId);
|
siteId,
|
||||||
|
// Notification Outbox (FU3): stamp the executing script onto outbound
|
||||||
|
// notifications using the Site Event Logging "Source" convention.
|
||||||
|
sourceScript: $"ScriptActor:{scriptName}");
|
||||||
|
|
||||||
var globals = new ScriptGlobals
|
var globals = new ScriptGlobals
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -66,6 +66,15 @@ public class ScriptRuntimeContext
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly string _siteId;
|
private readonly string _siteId;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Notification Outbox (FU3): identifier of the script currently executing in this
|
||||||
|
/// context — stamped onto <c>NotificationSubmit.SourceScript</c> for the central
|
||||||
|
/// audit trail. Uses the Site Event Logging "Source" convention
|
||||||
|
/// (<c>"ScriptActor:<scriptName>"</c>). Null when no single script owns the
|
||||||
|
/// context (e.g. alarm on-trigger paths that do not wire the Notify outbox).
|
||||||
|
/// </summary>
|
||||||
|
private readonly string? _sourceScript;
|
||||||
|
|
||||||
public ScriptRuntimeContext(
|
public ScriptRuntimeContext(
|
||||||
IActorRef instanceActor,
|
IActorRef instanceActor,
|
||||||
IActorRef self,
|
IActorRef self,
|
||||||
@@ -79,7 +88,8 @@ public class ScriptRuntimeContext
|
|||||||
IDatabaseGateway? databaseGateway = null,
|
IDatabaseGateway? databaseGateway = null,
|
||||||
StoreAndForwardService? storeAndForward = null,
|
StoreAndForwardService? storeAndForward = null,
|
||||||
ICanTell? siteCommunicationActor = null,
|
ICanTell? siteCommunicationActor = null,
|
||||||
string siteId = "")
|
string siteId = "",
|
||||||
|
string? sourceScript = null)
|
||||||
{
|
{
|
||||||
_instanceActor = instanceActor;
|
_instanceActor = instanceActor;
|
||||||
_self = self;
|
_self = self;
|
||||||
@@ -94,6 +104,7 @@ public class ScriptRuntimeContext
|
|||||||
_storeAndForward = storeAndForward;
|
_storeAndForward = storeAndForward;
|
||||||
_siteCommunicationActor = siteCommunicationActor;
|
_siteCommunicationActor = siteCommunicationActor;
|
||||||
_siteId = siteId;
|
_siteId = siteId;
|
||||||
|
_sourceScript = sourceScript;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -209,7 +220,7 @@ public class ScriptRuntimeContext
|
|||||||
/// <c>Notify.Status(id)</c> queries the delivery status of that notification.
|
/// <c>Notify.Status(id)</c> queries the delivery status of that notification.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public NotifyHelper Notify => new(
|
public NotifyHelper Notify => new(
|
||||||
_storeAndForward, _siteCommunicationActor, _siteId, _instanceName, _askTimeout, _logger);
|
_storeAndForward, _siteCommunicationActor, _siteId, _instanceName, _sourceScript, _askTimeout, _logger);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper class for Scripts.CallShared() syntax.
|
/// Helper class for Scripts.CallShared() syntax.
|
||||||
@@ -356,6 +367,7 @@ public class ScriptRuntimeContext
|
|||||||
private readonly ICanTell? _siteCommunicationActor;
|
private readonly ICanTell? _siteCommunicationActor;
|
||||||
private readonly string _siteId;
|
private readonly string _siteId;
|
||||||
private readonly string _instanceName;
|
private readonly string _instanceName;
|
||||||
|
private readonly string? _sourceScript;
|
||||||
private readonly TimeSpan _askTimeout;
|
private readonly TimeSpan _askTimeout;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
@@ -364,6 +376,7 @@ public class ScriptRuntimeContext
|
|||||||
ICanTell? siteCommunicationActor,
|
ICanTell? siteCommunicationActor,
|
||||||
string siteId,
|
string siteId,
|
||||||
string instanceName,
|
string instanceName,
|
||||||
|
string? sourceScript,
|
||||||
TimeSpan askTimeout,
|
TimeSpan askTimeout,
|
||||||
ILogger logger)
|
ILogger logger)
|
||||||
{
|
{
|
||||||
@@ -371,6 +384,7 @@ public class ScriptRuntimeContext
|
|||||||
_siteCommunicationActor = siteCommunicationActor;
|
_siteCommunicationActor = siteCommunicationActor;
|
||||||
_siteId = siteId;
|
_siteId = siteId;
|
||||||
_instanceName = instanceName;
|
_instanceName = instanceName;
|
||||||
|
_sourceScript = sourceScript;
|
||||||
_askTimeout = askTimeout;
|
_askTimeout = askTimeout;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
@@ -381,7 +395,7 @@ public class ScriptRuntimeContext
|
|||||||
public NotifyTarget To(string listName)
|
public NotifyTarget To(string listName)
|
||||||
{
|
{
|
||||||
return new NotifyTarget(
|
return new NotifyTarget(
|
||||||
listName, _storeAndForward, _siteId, _instanceName, _logger);
|
listName, _storeAndForward, _siteId, _instanceName, _sourceScript, _logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -453,6 +467,7 @@ public class ScriptRuntimeContext
|
|||||||
private readonly StoreAndForwardService? _storeAndForward;
|
private readonly StoreAndForwardService? _storeAndForward;
|
||||||
private readonly string _siteId;
|
private readonly string _siteId;
|
||||||
private readonly string _instanceName;
|
private readonly string _instanceName;
|
||||||
|
private readonly string? _sourceScript;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
internal NotifyTarget(
|
internal NotifyTarget(
|
||||||
@@ -460,12 +475,14 @@ public class ScriptRuntimeContext
|
|||||||
StoreAndForwardService? storeAndForward,
|
StoreAndForwardService? storeAndForward,
|
||||||
string siteId,
|
string siteId,
|
||||||
string instanceName,
|
string instanceName,
|
||||||
|
string? sourceScript,
|
||||||
ILogger logger)
|
ILogger logger)
|
||||||
{
|
{
|
||||||
_listName = listName;
|
_listName = listName;
|
||||||
_storeAndForward = storeAndForward;
|
_storeAndForward = storeAndForward;
|
||||||
_siteId = siteId;
|
_siteId = siteId;
|
||||||
_instanceName = instanceName;
|
_instanceName = instanceName;
|
||||||
|
_sourceScript = sourceScript;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,9 +521,10 @@ public class ScriptRuntimeContext
|
|||||||
// value is the best-effort site id known to the script runtime.
|
// value is the best-effort site id known to the script runtime.
|
||||||
SourceSiteId: _siteId,
|
SourceSiteId: _siteId,
|
||||||
SourceInstanceId: _instanceName,
|
SourceInstanceId: _instanceName,
|
||||||
// SourceScript: the script runtime does not currently thread the script
|
// SourceScript (FU3): identifier of the script that raised this
|
||||||
// name down to the Notify helper; left null until that wiring exists.
|
// notification, threaded down from the script-execution context for the
|
||||||
SourceScript: null,
|
// central audit trail. Null when no single script owns the context.
|
||||||
|
SourceScript: _sourceScript,
|
||||||
SiteEnqueuedAt: DateTimeOffset.UtcNow);
|
SiteEnqueuedAt: DateTimeOffset.UtcNow);
|
||||||
|
|
||||||
var payloadJson = JsonSerializer.Serialize(payload);
|
var payloadJson = JsonSerializer.Serialize(payload);
|
||||||
|
|||||||
@@ -58,13 +58,16 @@ public class NotifyHelperTests : TestKit, IAsyncLifetime, IDisposable
|
|||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScriptRuntimeContext.NotifyHelper CreateHelper(IActorRef siteCommunicationActor)
|
private ScriptRuntimeContext.NotifyHelper CreateHelper(
|
||||||
|
IActorRef siteCommunicationActor,
|
||||||
|
string? sourceScript = null)
|
||||||
{
|
{
|
||||||
return new ScriptRuntimeContext.NotifyHelper(
|
return new ScriptRuntimeContext.NotifyHelper(
|
||||||
_saf,
|
_saf,
|
||||||
siteCommunicationActor,
|
siteCommunicationActor,
|
||||||
"site-7",
|
"site-7",
|
||||||
"Plant.Pump3",
|
"Plant.Pump3",
|
||||||
|
sourceScript,
|
||||||
TimeSpan.FromSeconds(3),
|
TimeSpan.FromSeconds(3),
|
||||||
NullLogger.Instance);
|
NullLogger.Instance);
|
||||||
}
|
}
|
||||||
@@ -113,6 +116,36 @@ public class NotifyHelperTests : TestKit, IAsyncLifetime, IDisposable
|
|||||||
Assert.Equal("Plant.Pump3", payload.SourceInstanceId);
|
Assert.Equal("Plant.Pump3", payload.SourceInstanceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Send_WhenHelperHasSourceScript_StampsItOnTheNotificationSubmit()
|
||||||
|
{
|
||||||
|
var commProbe = CreateTestProbe();
|
||||||
|
var notify = CreateHelper(commProbe.Ref, sourceScript: "ScriptActor:MonitorSpeed");
|
||||||
|
|
||||||
|
var notificationId = await notify.To("Operators").Send("Pump alarm", "Pump 3 tripped");
|
||||||
|
|
||||||
|
var buffered = await _saf.GetMessageByIdAsync(notificationId);
|
||||||
|
Assert.NotNull(buffered);
|
||||||
|
|
||||||
|
var payload = JsonSerializer.Deserialize<NotificationSubmit>(buffered!.PayloadJson);
|
||||||
|
Assert.NotNull(payload);
|
||||||
|
// FU3: the executing script name is threaded down and stamped for the audit trail.
|
||||||
|
Assert.Equal("ScriptActor:MonitorSpeed", payload!.SourceScript);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Send_WhenHelperHasNoSourceScript_LeavesSourceScriptNull()
|
||||||
|
{
|
||||||
|
var commProbe = CreateTestProbe();
|
||||||
|
var notify = CreateHelper(commProbe.Ref, sourceScript: null);
|
||||||
|
|
||||||
|
var notificationId = await notify.To("Operators").Send("Pump alarm", "Pump 3 tripped");
|
||||||
|
|
||||||
|
var buffered = await _saf.GetMessageByIdAsync(notificationId);
|
||||||
|
var payload = JsonSerializer.Deserialize<NotificationSubmit>(buffered!.PayloadJson);
|
||||||
|
Assert.Null(payload!.SourceScript);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Status_WhenStillBufferedAtSite_ReportsForwarding()
|
public async Task Status_WhenStillBufferedAtSite_ReportsForwarding()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user