feat(notification-outbox): add Notification entity
This commit is contained in:
48
src/ScadaLink.Commons/Entities/Notifications/Notification.cs
Normal file
48
src/ScadaLink.Commons/Entities/Notifications/Notification.cs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
using ScadaLink.Commons.Types.Enums;
|
||||||
|
|
||||||
|
namespace ScadaLink.Commons.Entities.Notifications;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A single notification queued in the central outbox. Created at a site (where the
|
||||||
|
/// <see cref="NotificationId"/> GUID is generated) and forwarded to the central cluster
|
||||||
|
/// for delivery, retry, and audit. The lifecycle is tracked by <see cref="Status"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class Notification
|
||||||
|
{
|
||||||
|
/// <summary>GUID primary key, generated at the originating site.</summary>
|
||||||
|
public string NotificationId { get; set; }
|
||||||
|
public NotificationType Type { get; set; }
|
||||||
|
public string ListName { get; set; }
|
||||||
|
public string Subject { get; set; }
|
||||||
|
public string Body { get; set; }
|
||||||
|
|
||||||
|
/// <summary>JSON extensibility hook for channel-specific payload data.</summary>
|
||||||
|
public string? TypeData { get; set; }
|
||||||
|
public NotificationStatus Status { get; set; } = NotificationStatus.Pending;
|
||||||
|
public int RetryCount { get; set; }
|
||||||
|
public string? LastError { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Resolved delivery targets snapshotted at delivery time, for audit.</summary>
|
||||||
|
public string? ResolvedTargets { get; set; }
|
||||||
|
public string SourceSiteId { get; set; }
|
||||||
|
public string? SourceInstanceId { get; set; }
|
||||||
|
public string? SourceScript { get; set; }
|
||||||
|
public DateTimeOffset SiteEnqueuedAt { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Central ingest time.</summary>
|
||||||
|
public DateTimeOffset CreatedAt { get; set; }
|
||||||
|
public DateTimeOffset? LastAttemptAt { get; set; }
|
||||||
|
public DateTimeOffset? NextAttemptAt { get; set; }
|
||||||
|
public DateTimeOffset? DeliveredAt { get; set; }
|
||||||
|
|
||||||
|
public Notification(string notificationId, NotificationType type, string listName,
|
||||||
|
string subject, string body, string sourceSiteId)
|
||||||
|
{
|
||||||
|
NotificationId = notificationId ?? throw new ArgumentNullException(nameof(notificationId));
|
||||||
|
Type = type;
|
||||||
|
ListName = listName ?? throw new ArgumentNullException(nameof(listName));
|
||||||
|
Subject = subject ?? throw new ArgumentNullException(nameof(subject));
|
||||||
|
Body = body ?? throw new ArgumentNullException(nameof(body));
|
||||||
|
SourceSiteId = sourceSiteId ?? throw new ArgumentNullException(nameof(sourceSiteId));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using ScadaLink.Commons.Entities.Notifications;
|
||||||
|
using ScadaLink.Commons.Types.Enums;
|
||||||
|
|
||||||
|
namespace ScadaLink.Commons.Tests.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies the <see cref="Notification"/> outbox entity's constructor defaults
|
||||||
|
/// and null-argument guards on required reference-type parameters.
|
||||||
|
/// </summary>
|
||||||
|
public class NotificationEntityTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_SetsDefaults()
|
||||||
|
{
|
||||||
|
var n = new Notification("id-1", NotificationType.Email, "ops-team", "subj", "body", "SiteA");
|
||||||
|
Assert.Equal(NotificationStatus.Pending, n.Status);
|
||||||
|
Assert.Equal(0, n.RetryCount);
|
||||||
|
Assert.Equal("id-1", n.NotificationId);
|
||||||
|
Assert.Equal(NotificationType.Email, n.Type);
|
||||||
|
Assert.Equal("ops-team", n.ListName);
|
||||||
|
Assert.Equal("SiteA", n.SourceSiteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_NullListName_Throws()
|
||||||
|
=> Assert.Throws<ArgumentNullException>(
|
||||||
|
() => new Notification("id", NotificationType.Email, null!, "s", "b", "SiteA"));
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_NullNotificationId_Throws()
|
||||||
|
=> Assert.Throws<ArgumentNullException>(
|
||||||
|
() => new Notification(null!, NotificationType.Email, "list", "s", "b", "SiteA"));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user