Complete management API and CLI command parity
This commit is contained in:
@@ -23,6 +23,8 @@ public static class NotificationCommands
|
||||
command.Add(BuildGet(urlOption, formatOption, usernameOption, passwordOption));
|
||||
command.Add(BuildCreate(urlOption, formatOption, usernameOption, passwordOption));
|
||||
command.Add(BuildUpdate(urlOption, formatOption, usernameOption, passwordOption));
|
||||
command.Add(BuildRename(urlOption, formatOption, usernameOption, passwordOption));
|
||||
command.Add(BuildRecipient(urlOption, formatOption, usernameOption, passwordOption));
|
||||
command.Add(BuildDelete(urlOption, formatOption, usernameOption, passwordOption));
|
||||
command.Add(BuildSmtp(urlOption, formatOption, usernameOption, passwordOption));
|
||||
command.Add(BuildSms(urlOption, formatOption, usernameOption, passwordOption));
|
||||
@@ -30,6 +32,49 @@ public static class NotificationCommands
|
||||
return command;
|
||||
}
|
||||
|
||||
private static Option<int> RequiredId(string name, string description)
|
||||
{
|
||||
var option = new Option<int>(name) { Description = description, Required = true };
|
||||
option.Validators.Add(result => { if (result.GetValueOrDefault<int>() <= 0) result.AddError($"{name} must be positive."); });
|
||||
return option;
|
||||
}
|
||||
|
||||
private static Command BuildRename(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||
{
|
||||
var cmd = new Command("rename") { Description = "Rename a list, preserving all recipients and their IDs" };
|
||||
var id = RequiredId("--id", "Notification list ID");
|
||||
var name = new Option<string>("--name") { Required = true, Description = "New list name" };
|
||||
cmd.Add(id); cmd.Add(name);
|
||||
cmd.SetAction(async result => await CommandHelpers.ExecuteCommandAsync(result, urlOption, formatOption, usernameOption, passwordOption,
|
||||
new RenameNotificationListCommand(result.GetValue(id), result.GetValue(name)!)));
|
||||
return cmd;
|
||||
}
|
||||
|
||||
private static Command BuildRecipient(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||
{
|
||||
var group = new Command("recipient") { Description = "Manage individual named recipients without replacing the list" };
|
||||
var add = new Command("add") { Description = "Add a named recipient (address must match the list delivery channel)" };
|
||||
var listId = RequiredId("--list-id", "Notification list ID");
|
||||
var name = new Option<string>("--name") { Required = true, Description = "Recipient name" };
|
||||
var email = new Option<string?>("--email") { Description = "Email address" };
|
||||
var phone = new Option<string?>("--phone") { Description = "Phone number in E.164 format" };
|
||||
add.Add(listId); add.Add(name); add.Add(email); add.Add(phone);
|
||||
add.Validators.Add(result =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(result.GetValue(email)) && string.IsNullOrWhiteSpace(result.GetValue(phone)))
|
||||
result.AddError("Provide --email and/or --phone for the recipient.");
|
||||
});
|
||||
add.SetAction(async result => await CommandHelpers.ExecuteCommandAsync(result, urlOption, formatOption, usernameOption, passwordOption,
|
||||
new AddNotificationRecipientCommand(result.GetValue(listId), result.GetValue(name)!, result.GetValue(email), result.GetValue(phone))));
|
||||
var delete = new Command("delete") { Description = "Delete one recipient by its recipient ID" };
|
||||
var id = RequiredId("--id", "Recipient ID");
|
||||
delete.Add(id);
|
||||
delete.SetAction(async result => await CommandHelpers.ExecuteCommandAsync(result, urlOption, formatOption, usernameOption, passwordOption,
|
||||
new DeleteNotificationRecipientCommand(result.GetValue(id))));
|
||||
group.Add(add); group.Add(delete);
|
||||
return group;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Notification list create/update options (static so the parsed values can
|
||||
// be read back both from the SetAction and from the testable Build* helpers).
|
||||
@@ -134,6 +179,15 @@ public static class NotificationCommands
|
||||
});
|
||||
group.Add(listCmd);
|
||||
|
||||
var createCmd = new Command("create") { Description = "Create SMTP configuration" };
|
||||
foreach (var option in new Option[] { SmtpServerOption, SmtpPortOption, SmtpAuthModeOption, SmtpFromOption, SmtpTlsModeOption,
|
||||
SmtpCredentialsOption, SmtpOAuth2AuthorityOption, SmtpOAuth2ScopeOption, SmtpTransportOption }) createCmd.Add(option);
|
||||
createCmd.SetAction(async result => await CommandHelpers.ExecuteCommandAsync(result, urlOption, formatOption, usernameOption, passwordOption,
|
||||
new CreateSmtpConfigCommand(result.GetValue(SmtpServerOption)!, result.GetValue(SmtpPortOption), result.GetValue(SmtpAuthModeOption)!,
|
||||
result.GetValue(SmtpFromOption)!, result.GetValue(SmtpTlsModeOption), result.GetValue(SmtpCredentialsOption),
|
||||
result.GetValue(SmtpOAuth2AuthorityOption), result.GetValue(SmtpOAuth2ScopeOption), result.GetValue(SmtpTransportOption))));
|
||||
group.Add(createCmd);
|
||||
|
||||
var updateCmd = new Command("update") { Description = "Update SMTP configuration" };
|
||||
updateCmd.Add(SmtpIdOption);
|
||||
updateCmd.Add(SmtpServerOption);
|
||||
@@ -247,6 +301,21 @@ public static class NotificationCommands
|
||||
});
|
||||
group.Add(listCmd);
|
||||
|
||||
var createCmd = new Command("create") { Description = "Create SMS configuration (Auth Token required)" };
|
||||
foreach (var option in new Option[] { SmsAccountSidOption, SmsFromNumberOption, SmsMessagingServiceSidOption, SmsApiBaseUrlOption,
|
||||
SmsAuthTokenOption, SmsTimeoutOption, SmsMaxRetriesOption, SmsRetryDelayOption }) createCmd.Add(option);
|
||||
createCmd.Validators.Add(result =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(result.GetValue(SmsAuthTokenOption))) result.AddError("--auth-token is required when creating SMS configuration.");
|
||||
if (string.IsNullOrWhiteSpace(result.GetValue(SmsFromNumberOption)) && string.IsNullOrWhiteSpace(result.GetValue(SmsMessagingServiceSidOption)))
|
||||
result.AddError("Provide --from-number and/or --messaging-service-sid.");
|
||||
});
|
||||
createCmd.SetAction(async result => await CommandHelpers.ExecuteCommandAsync(result, urlOption, formatOption, usernameOption, passwordOption,
|
||||
new CreateSmsConfigCommand(result.GetValue(SmsAccountSidOption)!, result.GetValue(SmsFromNumberOption), result.GetValue(SmsMessagingServiceSidOption),
|
||||
result.GetValue(SmsApiBaseUrlOption), result.GetValue(SmsAuthTokenOption), result.GetValue(SmsTimeoutOption),
|
||||
result.GetValue(SmsMaxRetriesOption), SmsRetryDelay(result))));
|
||||
group.Add(createCmd);
|
||||
|
||||
var updateCmd = new Command("update") { Description = "Update SMS configuration" };
|
||||
updateCmd.Add(SmsIdOption);
|
||||
updateCmd.Add(SmsAccountSidOption);
|
||||
@@ -254,6 +323,9 @@ public static class NotificationCommands
|
||||
updateCmd.Add(SmsMessagingServiceSidOption);
|
||||
updateCmd.Add(SmsApiBaseUrlOption);
|
||||
updateCmd.Add(SmsAuthTokenOption);
|
||||
updateCmd.Add(SmsTimeoutOption);
|
||||
updateCmd.Add(SmsMaxRetriesOption);
|
||||
updateCmd.Add(SmsRetryDelayOption);
|
||||
updateCmd.SetAction(async (ParseResult result) =>
|
||||
{
|
||||
return await CommandHelpers.ExecuteCommandAsync(
|
||||
@@ -265,6 +337,24 @@ public static class NotificationCommands
|
||||
return group;
|
||||
}
|
||||
|
||||
private static readonly Option<int?> SmsTimeoutOption = SmsPolicyOption("--connection-timeout-seconds", "Connection timeout in seconds", 1);
|
||||
private static readonly Option<int?> SmsMaxRetriesOption = SmsPolicyOption("--max-retries", "Retry count (zero uses the outbox fallback)", 0);
|
||||
private static readonly Option<int?> SmsRetryDelayOption = SmsPolicyOption("--retry-delay-seconds", "Retry delay in seconds (zero uses the outbox fallback)", 0);
|
||||
|
||||
private static Option<int?> SmsPolicyOption(string name, string description, int minimum)
|
||||
{
|
||||
var option = new Option<int?>(name) { Description = description + "; omission preserves updates or uses create defaults" };
|
||||
option.Validators.Add(result =>
|
||||
{
|
||||
if (result.GetValueOrDefault<int?>() is { } value && value < minimum)
|
||||
result.AddError($"{name} must be at least {minimum}.");
|
||||
});
|
||||
return option;
|
||||
}
|
||||
|
||||
private static TimeSpan? SmsRetryDelay(ParseResult result) =>
|
||||
result.GetValue(SmsRetryDelayOption) is { } seconds ? TimeSpan.FromSeconds(seconds) : null;
|
||||
|
||||
// SMS update options are static so the parsed values can be read back both
|
||||
// from the SetAction and from BuildUpdateSmsConfigCommand (used by tests).
|
||||
private static readonly Option<int> SmsIdOption =
|
||||
@@ -317,7 +407,8 @@ public static class NotificationCommands
|
||||
throw new ArgumentException(
|
||||
"Provide --from-number and/or --messaging-service-sid (at least one sender identity is required).");
|
||||
}
|
||||
return new UpdateSmsConfigCommand(id, accountSid, fromNumber, messagingServiceSid, apiBaseUrl, authToken);
|
||||
return new UpdateSmsConfigCommand(id, accountSid, fromNumber, messagingServiceSid, apiBaseUrl, authToken,
|
||||
result.GetValue(SmsTimeoutOption), result.GetValue(SmsMaxRetriesOption), SmsRetryDelay(result));
|
||||
}
|
||||
|
||||
private static Command BuildList(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||
|
||||
Reference in New Issue
Block a user