feat(notification-outbox): async Notify.Send with status handle
Notify.To(list).Send(subject,body) now generates a NotificationId GUID, enqueues a Notification-category message into the site Store-and-Forward Engine, and returns the NotificationId immediately (Task<string>). The NotificationId is the single idempotency key end-to-end: it is the S&F message Id, it is carried inside the buffered NotificationSubmit payload, and it is the id the forwarder submits to central. NotificationForwarder now deserializes the buffered payload as a NotificationSubmit and reads NotificationId from it (re-stamping only the site-owned SourceSiteId / SourceInstanceId), instead of deriving the id from StoreAndForwardMessage.Id. Adds NotifyHelper.Status(id): queries central via the site communication actor; reports the site-local Forwarding state while the notification is still buffered at the site, maps central's response when found, and Unknown otherwise. Adds a NotificationDeliveryStatus record. SiteCommunicationActor gains a NotificationStatusQuery forwarding handler mirroring NotificationSubmit. StoreAndForwardService.EnqueueAsync gains an optional messageId parameter and exposes GetMessageByIdAsync.
This commit is contained in:
@@ -156,15 +156,26 @@ public class IntegrationSurfaceTests
|
||||
[Fact]
|
||||
public async Task ScriptContext_Notify_Send_Wired()
|
||||
{
|
||||
var mockNotify = Substitute.For<INotificationDeliveryService>();
|
||||
mockNotify.SendAsync("ops", "Alert", "Body", Arg.Any<string?>(), Arg.Any<CancellationToken>())
|
||||
.Returns(new NotificationResult(true, null));
|
||||
// Notification Outbox: Notify.Send enqueues into the site Store-and-Forward
|
||||
// Engine and returns the NotificationId handle immediately.
|
||||
var dbName = $"NotifyWired_{Guid.NewGuid():N}";
|
||||
var connStr = $"Data Source={dbName};Mode=Memory;Cache=Shared";
|
||||
using var keepAlive = new Microsoft.Data.Sqlite.SqliteConnection(connStr);
|
||||
keepAlive.Open();
|
||||
var storage = new StoreAndForward.StoreAndForwardStorage(
|
||||
connStr, Microsoft.Extensions.Logging.Abstractions.NullLogger<StoreAndForward.StoreAndForwardStorage>.Instance);
|
||||
await storage.InitializeAsync();
|
||||
var saf = new StoreAndForward.StoreAndForwardService(
|
||||
storage, new StoreAndForward.StoreAndForwardOptions(),
|
||||
Microsoft.Extensions.Logging.Abstractions.NullLogger<StoreAndForward.StoreAndForwardService>.Instance);
|
||||
|
||||
var context = CreateMinimalScriptContext(notificationService: mockNotify);
|
||||
var context = CreateMinimalScriptContext(storeAndForward: saf);
|
||||
|
||||
var result = await context.Notify.To("ops").Send("Alert", "Body");
|
||||
var notificationId = await context.Notify.To("ops").Send("Alert", "Body");
|
||||
|
||||
Assert.True(result.Success);
|
||||
Assert.False(string.IsNullOrEmpty(notificationId));
|
||||
var buffered = await saf.GetMessageByIdAsync(notificationId);
|
||||
Assert.NotNull(buffered);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -188,6 +199,7 @@ public class IntegrationSurfaceTests
|
||||
[Fact]
|
||||
public async Task ScriptContext_Notify_NoService_Throws()
|
||||
{
|
||||
// No Store-and-Forward Engine wired → Notify.Send cannot enqueue and throws.
|
||||
var context = CreateMinimalScriptContext();
|
||||
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
@@ -197,7 +209,7 @@ public class IntegrationSurfaceTests
|
||||
private static SiteRuntime.Scripts.ScriptRuntimeContext CreateMinimalScriptContext(
|
||||
IExternalSystemClient? externalSystemClient = null,
|
||||
IDatabaseGateway? databaseGateway = null,
|
||||
INotificationDeliveryService? notificationService = null)
|
||||
StoreAndForward.StoreAndForwardService? storeAndForward = null)
|
||||
{
|
||||
// Create a minimal context — we use Substitute.For<IActorRef> which is fine since
|
||||
// we won't exercise Akka functionality in these tests.
|
||||
@@ -219,6 +231,7 @@ public class IntegrationSurfaceTests
|
||||
logger: Microsoft.Extensions.Logging.Abstractions.NullLogger.Instance,
|
||||
externalSystemClient: externalSystemClient,
|
||||
databaseGateway: databaseGateway,
|
||||
notificationService: notificationService);
|
||||
storeAndForward: storeAndForward,
|
||||
siteId: "test-site");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user