feat(sms): make FromNumber optional — support Twilio Messaging-Service-only configs (UI-Med-2)
Code-review finding UI-Med-2: the design doc + delivery adapter treat FromNumber and MessagingServiceSid as either-or, but the entity ctor, EF schema, UI and CLI all hard- required FromNumber — so a Messaging-Service-only Twilio config (a normal production setup) could not be created. Bring the implementation into line with the spec: - Commons: SmsConfiguration.FromNumber -> string? (ctor fromNumber optional); UpdateSmsConfigCommand.FromNumber -> string?. - ConfigurationDatabase: FromNumber.IsRequired(false) + migration SmsFromNumberOptional (ALTER COLUMN nullable, idempotent; Down backfills '' — harmless, MsgSid keeps it deliverable) + regenerated model snapshot. - Transport: SmsConfigDto.FromNumber -> string? (round-trips a Messaging-Service-only config). - CentralUI: form validation requires AccountSid + at-least-one-of(FromNumber, MsgSid); nullable create/edit paths; From-number help text. - CLI: --from-number no longer Required; BuildUpdateSmsConfigCommand validates the either-or. - Adapter: From branch null-forgiving (guarded by the existing incomplete-config check). Tests: ManagementActor MsgSid-only persists null FromNumber; CLI MsgSid-only builds + neither-throws + contract (--from-number not Required); CentralUI MsgSid-only save.
This commit is contained in:
@@ -11,8 +11,14 @@ public class SmsConfiguration
|
||||
/// during configuration, never a valid production value.
|
||||
/// </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 sender phone number (E.164) placed in the From field, or null when
|
||||
/// delivery is via a <see cref="MessagingServiceSid"/> instead. A valid config has a
|
||||
/// FromNumber and/or a MessagingServiceSid; that either-or invariant is enforced at the
|
||||
/// management/UI boundary and again by the delivery adapter (it is not a ctor invariant
|
||||
/// because MessagingServiceSid is a settable property assigned after construction).
|
||||
/// </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>
|
||||
@@ -39,15 +45,17 @@ public class SmsConfiguration
|
||||
public TimeSpan RetryDelay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="SmsConfiguration"/> with required fields and sensible defaults
|
||||
/// for the numeric and timeout fields.
|
||||
/// Initializes a new <see cref="SmsConfiguration"/> with the required Account SID and
|
||||
/// sensible defaults for the numeric and timeout fields. <paramref name="fromNumber"/>
|
||||
/// is optional — a Twilio Messaging-Service-only config sets <see cref="MessagingServiceSid"/>
|
||||
/// instead and leaves the From number null.
|
||||
/// </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)
|
||||
/// <param name="fromNumber">Sender phone number (E.164) for the From field, or null for a Messaging-Service-only config.</param>
|
||||
public SmsConfiguration(string accountSid, string? fromNumber = null)
|
||||
{
|
||||
AccountSid = accountSid ?? throw new ArgumentNullException(nameof(accountSid));
|
||||
FromNumber = fromNumber ?? throw new ArgumentNullException(nameof(fromNumber));
|
||||
FromNumber = fromNumber;
|
||||
ConnectionTimeoutSeconds = 30;
|
||||
MaxRetries = 10;
|
||||
RetryDelay = TimeSpan.FromMinutes(1);
|
||||
|
||||
Reference in New Issue
Block a user