feat(notif-outbox): add SourceNode to Notification entity + NotificationSubmit
This commit is contained in:
@@ -25,6 +25,15 @@ public class Notification
|
||||
/// <summary>Resolved delivery targets snapshotted at delivery time, for audit.</summary>
|
||||
public string? ResolvedTargets { get; set; }
|
||||
public string SourceSiteId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The cluster node on which the notification was emitted — `node-a` / `node-b`
|
||||
/// for site rows (qualified by <see cref="SourceSiteId"/>), `central-a` / `central-b`
|
||||
/// for central-originated rows. Carried from the site on the
|
||||
/// <see cref="Commons.Messages.Notification.NotificationSubmit"/> and persisted at
|
||||
/// central; nullable so rows submitted before the column existed don't block ingest.
|
||||
/// </summary>
|
||||
public string? SourceNode { get; set; }
|
||||
public string? SourceInstanceId { get; set; }
|
||||
public string? SourceScript { get; set; }
|
||||
|
||||
|
||||
@@ -18,6 +18,15 @@ namespace ScadaLink.Commons.Messages.Notification;
|
||||
/// <c>NotifyDeliver</c> audit rows. Additive trailing member — null for messages built
|
||||
/// before the field existed, or for non-routed runs.
|
||||
/// </param>
|
||||
/// <param name="SourceNode">
|
||||
/// The cluster node on which the notification was emitted — `node-a` / `node-b` for site
|
||||
/// submissions, `central-a` / `central-b` for central-originated rows. Stamped by the
|
||||
/// emitting node from <c>INodeIdentityProvider</c> and carried, inside the serialized
|
||||
/// payload, through the site store-and-forward buffer so the central dispatcher can
|
||||
/// persist it on the <c>Notifications</c> row and echo it onto the <c>NotifyDeliver</c>
|
||||
/// audit rows. Additive trailing member — null for messages built before the field
|
||||
/// existed.
|
||||
/// </param>
|
||||
public record NotificationSubmit(
|
||||
string NotificationId,
|
||||
string ListName,
|
||||
@@ -28,7 +37,8 @@ public record NotificationSubmit(
|
||||
string? SourceScript,
|
||||
DateTimeOffset SiteEnqueuedAt,
|
||||
Guid? OriginExecutionId = null,
|
||||
Guid? OriginParentExecutionId = null);
|
||||
Guid? OriginParentExecutionId = null,
|
||||
string? SourceNode = null);
|
||||
|
||||
/// <summary>
|
||||
/// Central -> Site: ack sent after the notification row is persisted.
|
||||
|
||||
@@ -51,6 +51,20 @@ public class NotificationEntityTests
|
||||
Assert.Equal(parentExecutionId, n.OriginParentExecutionId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SourceNode_DefaultsToNull_AndIsSettable()
|
||||
{
|
||||
// SourceNode identifies the cluster node that emitted the notification
|
||||
// (site node-a/node-b or central-a/central-b). Additive nullable
|
||||
// property — defaults to null on rows submitted before the column
|
||||
// existed, and round-trips its value when set.
|
||||
var n = new Notification("id-1", NotificationType.Email, "ops-team", "subj", "body", "SiteA");
|
||||
Assert.Null(n.SourceNode);
|
||||
|
||||
n.SourceNode = "node-a";
|
||||
Assert.Equal("node-a", n.SourceNode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_NullArguments_Throw()
|
||||
{
|
||||
|
||||
@@ -126,6 +126,32 @@ public class NotificationMessagesTests
|
||||
Assert.Equal(parentExecutionId, roundTripped!.OriginParentExecutionId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NotificationSubmit_carries_SourceNode()
|
||||
{
|
||||
// SourceNode is an additive trailing member — old call sites and old
|
||||
// serialized payloads leave it null. When supplied it round-trips
|
||||
// through both construction and JSON (the buffered S&F payload IS a
|
||||
// serialized NotificationSubmit).
|
||||
var defaulted = new NotificationSubmit(
|
||||
"notif-9", "Operators", "Subject", "Body",
|
||||
"site-01", "inst-1", "OnAlarm", DateTimeOffset.UtcNow);
|
||||
Assert.Null(defaulted.SourceNode);
|
||||
|
||||
var stamped = new NotificationSubmit(
|
||||
"notif-10", "Operators", "Subject", "Body",
|
||||
"site-01", "inst-1", "OnAlarm", DateTimeOffset.UtcNow,
|
||||
OriginExecutionId: null,
|
||||
OriginParentExecutionId: null,
|
||||
SourceNode: "node-a");
|
||||
Assert.Equal("node-a", stamped.SourceNode);
|
||||
|
||||
var json = System.Text.Json.JsonSerializer.Serialize(stamped);
|
||||
var roundTripped = System.Text.Json.JsonSerializer.Deserialize<NotificationSubmit>(json);
|
||||
Assert.NotNull(roundTripped);
|
||||
Assert.Equal("node-a", roundTripped!.SourceNode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NotificationSubmit_ValueEquality_EqualWhenAllFieldsMatch()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user