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
-5
@@ -80,6 +80,7 @@
|
||||
<div class="col-12">
|
||||
<label class="form-label">From Number</label>
|
||||
<input type="text" class="form-control" @bind="_fromNumber" placeholder="+15551234567" />
|
||||
<div class="form-text">Provide a From Number or a Messaging Service SID (at least one is required).</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">Messaging Service SID</label>
|
||||
@@ -195,7 +196,7 @@
|
||||
{
|
||||
_editingSms = sms;
|
||||
_accountSid = sms.AccountSid;
|
||||
_fromNumber = sms.FromNumber;
|
||||
_fromNumber = sms.FromNumber ?? string.Empty;
|
||||
_messagingServiceSid = sms.MessagingServiceSid;
|
||||
_apiBaseUrl = sms.ApiBaseUrl;
|
||||
// Never pre-fill the stored secret; blank means "keep existing".
|
||||
@@ -216,9 +217,12 @@
|
||||
private async Task Save()
|
||||
{
|
||||
_formError = null;
|
||||
if (string.IsNullOrWhiteSpace(_accountSid) || string.IsNullOrWhiteSpace(_fromNumber))
|
||||
// A valid Twilio config needs an Account SID and at least one sender identity:
|
||||
// a From number OR a Messaging Service SID (the latter is used instead of From).
|
||||
if (string.IsNullOrWhiteSpace(_accountSid)
|
||||
|| (string.IsNullOrWhiteSpace(_fromNumber) && string.IsNullOrWhiteSpace(_messagingServiceSid)))
|
||||
{
|
||||
_formError = "Account SID and From Number are required.";
|
||||
_formError = "Account SID and either a From Number or a Messaging Service SID are required.";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -229,7 +233,7 @@
|
||||
if (_editingSms != null)
|
||||
{
|
||||
_editingSms.AccountSid = _accountSid.Trim();
|
||||
_editingSms.FromNumber = _fromNumber.Trim();
|
||||
_editingSms.FromNumber = string.IsNullOrWhiteSpace(_fromNumber) ? null : _fromNumber.Trim();
|
||||
_editingSms.MessagingServiceSid = string.IsNullOrWhiteSpace(_messagingServiceSid)
|
||||
? null
|
||||
: _messagingServiceSid.Trim();
|
||||
@@ -252,7 +256,9 @@
|
||||
return;
|
||||
}
|
||||
|
||||
var sms = new SmsConfigurationEntity(_accountSid.Trim(), _fromNumber.Trim())
|
||||
var sms = new SmsConfigurationEntity(
|
||||
_accountSid.Trim(),
|
||||
string.IsNullOrWhiteSpace(_fromNumber) ? null : _fromNumber.Trim())
|
||||
{
|
||||
MessagingServiceSid = string.IsNullOrWhiteSpace(_messagingServiceSid)
|
||||
? null
|
||||
|
||||
Reference in New Issue
Block a user