feat: achieve CLI parity with Central UI

Add 33 new management message records, ManagementActor handlers, and CLI
commands to close all functionality gaps between the Central UI and the
Management CLI. New capabilities include:

- Template member CRUD (attributes, alarms, scripts, compositions)
- Shared script CRUD
- Database connection definition CRUD
- Inbound API method CRUD
- LDAP scope rule management
- API key enable/disable
- Area update
- Remote event log and parked message queries
- Missing get/update commands for templates, sites, instances, data
  connections, external systems, notifications, and SMTP config

Includes 12 new ManagementActor unit tests covering authorization,
happy-path queries, and error handling. Updates CLI README and component
design documents (Component-CLI.md, Component-ManagementService.md).
This commit is contained in:
Joseph Doherty
2026-03-18 01:21:20 -04:00
parent b2385709f8
commit c63fb1c4a6
24 changed files with 2500 additions and 15 deletions
@@ -0,0 +1,113 @@
using System.CommandLine;
using System.CommandLine.Parsing;
using ScadaLink.Commons.Messages.Management;
namespace ScadaLink.CLI.Commands;
public static class SharedScriptCommands
{
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption)
{
var command = new Command("shared-script") { Description = "Manage shared scripts" };
command.Add(BuildList(contactPointsOption, formatOption));
command.Add(BuildGet(contactPointsOption, formatOption));
command.Add(BuildCreate(contactPointsOption, formatOption));
command.Add(BuildUpdate(contactPointsOption, formatOption));
command.Add(BuildDelete(contactPointsOption, formatOption));
return command;
}
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption)
{
var cmd = new Command("list") { Description = "List all shared scripts" };
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new ListSharedScriptsCommand());
});
return cmd;
}
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption)
{
var idOption = new Option<int>("--id") { Description = "Shared script ID", Required = true };
var cmd = new Command("get") { Description = "Get a shared script by ID" };
cmd.Add(idOption);
cmd.SetAction(async (ParseResult result) =>
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new GetSharedScriptCommand(id));
});
return cmd;
}
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption)
{
var nameOption = new Option<string>("--name") { Description = "Script name", Required = true };
var codeOption = new Option<string>("--code") { Description = "Script code", Required = true };
var parametersOption = new Option<string?>("--parameters") { Description = "Parameter definitions JSON" };
var returnDefOption = new Option<string?>("--return-def") { Description = "Return type definition" };
var cmd = new Command("create") { Description = "Create a shared script" };
cmd.Add(nameOption);
cmd.Add(codeOption);
cmd.Add(parametersOption);
cmd.Add(returnDefOption);
cmd.SetAction(async (ParseResult result) =>
{
var name = result.GetValue(nameOption)!;
var code = result.GetValue(codeOption)!;
var parameters = result.GetValue(parametersOption);
var returnDef = result.GetValue(returnDefOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
new CreateSharedScriptCommand(name, code, parameters, returnDef));
});
return cmd;
}
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption)
{
var idOption = new Option<int>("--id") { Description = "Shared script ID", Required = true };
var nameOption = new Option<string>("--name") { Description = "Script name", Required = true };
var codeOption = new Option<string>("--code") { Description = "Script code", Required = true };
var parametersOption = new Option<string?>("--parameters") { Description = "Parameter definitions JSON" };
var returnDefOption = new Option<string?>("--return-def") { Description = "Return type definition" };
var cmd = new Command("update") { Description = "Update a shared script" };
cmd.Add(idOption);
cmd.Add(nameOption);
cmd.Add(codeOption);
cmd.Add(parametersOption);
cmd.Add(returnDefOption);
cmd.SetAction(async (ParseResult result) =>
{
var id = result.GetValue(idOption);
var name = result.GetValue(nameOption)!;
var code = result.GetValue(codeOption)!;
var parameters = result.GetValue(parametersOption);
var returnDef = result.GetValue(returnDefOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
new UpdateSharedScriptCommand(id, name, code, parameters, returnDef));
});
return cmd;
}
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption)
{
var idOption = new Option<int>("--id") { Description = "Shared script ID", Required = true };
var cmd = new Command("delete") { Description = "Delete a shared script" };
cmd.Add(idOption);
cmd.SetAction(async (ParseResult result) =>
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new DeleteSharedScriptCommand(id));
});
return cmd;
}
}