feat(sms): NotificationType.Sms + recipient phone + SmsConfiguration + repo iface (S1)
This commit is contained in:
@@ -8,11 +8,13 @@ public class NotificationRecipient
|
|||||||
public int NotificationListId { get; set; }
|
public int NotificationListId { get; set; }
|
||||||
/// <summary>Gets or sets the display name of the recipient.</summary>
|
/// <summary>Gets or sets the display name of the recipient.</summary>
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
/// <summary>Gets or sets the recipient's email address.</summary>
|
/// <summary>Gets or sets the recipient's email address, or null for non-email recipients.</summary>
|
||||||
public string EmailAddress { get; set; }
|
public string? EmailAddress { get; set; }
|
||||||
|
/// <summary>Gets or sets the recipient's phone number (E.164), or null for non-SMS recipients.</summary>
|
||||||
|
public string? PhoneNumber { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new <see cref="NotificationRecipient"/> with the required fields.
|
/// Initializes a new <see cref="NotificationRecipient"/> with the required fields (email path).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">Display name of the recipient.</param>
|
/// <param name="name">Display name of the recipient.</param>
|
||||||
/// <param name="emailAddress">Email address of the recipient.</param>
|
/// <param name="emailAddress">Email address of the recipient.</param>
|
||||||
@@ -21,4 +23,59 @@ public class NotificationRecipient
|
|||||||
Name = name ?? throw new ArgumentNullException(nameof(name));
|
Name = name ?? throw new ArgumentNullException(nameof(name));
|
||||||
EmailAddress = emailAddress ?? throw new ArgumentNullException(nameof(emailAddress));
|
EmailAddress = emailAddress ?? throw new ArgumentNullException(nameof(emailAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an email recipient with the given name and email address; the phone number is left null.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Display name of the recipient.</param>
|
||||||
|
/// <param name="emailAddress">Email address of the recipient.</param>
|
||||||
|
/// <returns>A new email <see cref="NotificationRecipient"/>.</returns>
|
||||||
|
public static NotificationRecipient ForEmail(string name, string emailAddress)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Name must not be empty.", nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emailAddress is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(emailAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NotificationRecipient(name, emailAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an SMS recipient with the given name and phone number; the email address is left null.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Display name of the recipient.</param>
|
||||||
|
/// <param name="phoneNumber">Phone number (E.164) of the recipient.</param>
|
||||||
|
/// <returns>A new SMS <see cref="NotificationRecipient"/>.</returns>
|
||||||
|
public static NotificationRecipient ForSms(string name, string phoneNumber)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Name must not be empty.", nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phoneNumber is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(phoneNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NotificationRecipient
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
PhoneNumber = phoneNumber
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parameterless constructor used by EF Core materialization and the SMS factory, where the
|
||||||
|
/// contact field is assigned via property setters rather than constructor parameters.
|
||||||
|
/// </summary>
|
||||||
|
private NotificationRecipient()
|
||||||
|
{
|
||||||
|
Name = string.Empty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
namespace ZB.MOM.WW.ScadaBridge.Commons.Entities.Notifications;
|
||||||
|
|
||||||
|
public class SmsConfiguration
|
||||||
|
{
|
||||||
|
/// <summary>Gets or sets the primary key.</summary>
|
||||||
|
public int Id { get; set; }
|
||||||
|
/// <summary>Gets or sets the Twilio Account SID.</summary>
|
||||||
|
public string AccountSid { get; set; }
|
||||||
|
/// <summary>Gets or sets the Twilio Auth Token (secret), or null when not applicable.</summary>
|
||||||
|
public string? AuthToken { get; set; }
|
||||||
|
/// <summary>Gets or sets the sender phone number (E.164) placed in the From field.</summary>
|
||||||
|
public string FromNumber { get; set; }
|
||||||
|
/// <summary>Gets or sets the Twilio Messaging Service SID used instead of a From number, or null.</summary>
|
||||||
|
public string? MessagingServiceSid { get; set; }
|
||||||
|
/// <summary>Gets or sets the Twilio REST API base URL, or null to use the provider default.</summary>
|
||||||
|
public string? ApiBaseUrl { get; set; }
|
||||||
|
/// <summary>Gets or sets the connection timeout in seconds.</summary>
|
||||||
|
public int ConnectionTimeoutSeconds { get; set; }
|
||||||
|
/// <summary>Gets or sets the maximum number of delivery retries before parking.</summary>
|
||||||
|
public int MaxRetries { get; set; }
|
||||||
|
/// <summary>Gets or sets the delay between retry attempts.</summary>
|
||||||
|
public TimeSpan RetryDelay { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new <see cref="SmsConfiguration"/> with required fields and sensible defaults
|
||||||
|
/// for the numeric and timeout fields.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="accountSid">Twilio Account SID.</param>
|
||||||
|
/// <param name="fromNumber">Sender phone number (E.164) for the From field.</param>
|
||||||
|
public SmsConfiguration(string accountSid, string fromNumber)
|
||||||
|
{
|
||||||
|
AccountSid = accountSid ?? throw new ArgumentNullException(nameof(accountSid));
|
||||||
|
FromNumber = fromNumber ?? throw new ArgumentNullException(nameof(fromNumber));
|
||||||
|
ConnectionTimeoutSeconds = 30;
|
||||||
|
MaxRetries = 10;
|
||||||
|
RetryDelay = TimeSpan.FromMinutes(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -101,6 +101,29 @@ public interface INotificationRepository
|
|||||||
/// <returns>A task representing the asynchronous operation.</returns>
|
/// <returns>A task representing the asynchronous operation.</returns>
|
||||||
Task DeleteSmtpConfigurationAsync(int id, CancellationToken cancellationToken = default);
|
Task DeleteSmtpConfigurationAsync(int id, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
// SmsConfiguration
|
||||||
|
/// <summary>Gets the SMS configuration, or null if none is configured.</summary>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
|
/// <returns>The SMS configuration, or null if not found.</returns>
|
||||||
|
Task<SmsConfiguration?> GetSmsConfigurationAsync(CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
/// <summary>Gets all SMS configurations.</summary>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
|
/// <returns>A read-only list of SMS configurations.</returns>
|
||||||
|
Task<IReadOnlyList<SmsConfiguration>> GetAllSmsConfigurationsAsync(CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
/// <summary>Adds a new SMS configuration.</summary>
|
||||||
|
/// <param name="smsConfiguration">The SMS configuration to add.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
|
/// <returns>A task representing the asynchronous operation.</returns>
|
||||||
|
Task AddSmsConfigurationAsync(SmsConfiguration smsConfiguration, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
/// <summary>Updates an existing SMS configuration.</summary>
|
||||||
|
/// <param name="smsConfiguration">The SMS configuration to update.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
|
/// <returns>A task representing the asynchronous operation.</returns>
|
||||||
|
Task UpdateSmsConfigurationAsync(SmsConfiguration smsConfiguration, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
/// <summary>Saves pending changes to the repository.</summary>
|
/// <summary>Saves pending changes to the repository.</summary>
|
||||||
/// <param name="cancellationToken">Cancellation token.</param>
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
/// <returns>The number of entities saved.</returns>
|
/// <returns>The number of entities saved.</returns>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delivery channel for a notification. Currently only email is supported.
|
/// Delivery channel for a notification. Email and SMS are supported.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum NotificationType
|
public enum NotificationType
|
||||||
{
|
{
|
||||||
Email
|
Email,
|
||||||
|
Sms
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public class EnumTests
|
|||||||
[InlineData(typeof(AlarmTriggerType), new[] { "ValueMatch", "RangeViolation", "RateOfChange", "HiLo", "Expression" })]
|
[InlineData(typeof(AlarmTriggerType), new[] { "ValueMatch", "RangeViolation", "RateOfChange", "HiLo", "Expression" })]
|
||||||
[InlineData(typeof(ConnectionHealth), new[] { "Connected", "Disconnected", "Connecting", "Error" })]
|
[InlineData(typeof(ConnectionHealth), new[] { "Connected", "Disconnected", "Connecting", "Error" })]
|
||||||
[InlineData(typeof(NotificationStatus), new[] { "Pending", "Retrying", "Delivered", "Parked", "Discarded" })]
|
[InlineData(typeof(NotificationStatus), new[] { "Pending", "Retrying", "Delivered", "Parked", "Discarded" })]
|
||||||
[InlineData(typeof(NotificationType), new[] { "Email" })]
|
[InlineData(typeof(NotificationType), new[] { "Email", "Sms" })]
|
||||||
public void Enum_ShouldHaveExpectedValues(Type enumType, string[] expectedNames)
|
public void Enum_ShouldHaveExpectedValues(Type enumType, string[] expectedNames)
|
||||||
{
|
{
|
||||||
var actualNames = Enum.GetNames(enumType);
|
var actualNames = Enum.GetNames(enumType);
|
||||||
|
|||||||
Reference in New Issue
Block a user