feat: add JoeAppEngine OPC UA nodes, fix DCL auto-reconnect and quality push

- Add JoeAppEngine folder to OPC UA nodes.json (BTCS, AlarmCntsBySeverity, Scheduler/ScanTime)
- Fix DataConnectionActor: capture Self in PreStart for use from non-actor threads,
  preventing Self.Tell failure in Disconnected event handler
- Implement InstanceActor.HandleConnectionQualityChanged to mark attributes Bad on disconnect
- Fix LmxFakeProxy TagMapper to serialize arrays as JSON instead of "System.Int32[]"
- Allow DataType and DataSourceReference updates in TemplateService.UpdateAttributeAsync
- Update test_infra_opcua.md with JoeAppEngine documentation
This commit is contained in:
Joseph Doherty
2026-03-19 13:27:54 -04:00
parent ffdda51990
commit 7740a3bcf9
70 changed files with 2684 additions and 541 deletions

View File

@@ -8,6 +8,9 @@ public class CliConfig
public string? LdapServer { get; set; }
public int LdapPort { get; set; } = 636;
public bool LdapUseTls { get; set; } = true;
public string LdapSearchBase { get; set; } = string.Empty;
public string LdapServiceAccountDn { get; set; } = string.Empty;
public string LdapServiceAccountPassword { get; set; } = string.Empty;
public string DefaultFormat { get; set; } = "json";
public static CliConfig Load()
@@ -31,6 +34,12 @@ public class CliConfig
config.LdapServer = fileConfig.Ldap.Server;
config.LdapPort = fileConfig.Ldap.Port;
config.LdapUseTls = fileConfig.Ldap.UseTls;
if (!string.IsNullOrEmpty(fileConfig.Ldap.SearchBase))
config.LdapSearchBase = fileConfig.Ldap.SearchBase;
if (!string.IsNullOrEmpty(fileConfig.Ldap.ServiceAccountDn))
config.LdapServiceAccountDn = fileConfig.Ldap.ServiceAccountDn;
if (!string.IsNullOrEmpty(fileConfig.Ldap.ServiceAccountPassword))
config.LdapServiceAccountPassword = fileConfig.Ldap.ServiceAccountPassword;
}
if (!string.IsNullOrEmpty(fileConfig.DefaultFormat)) config.DefaultFormat = fileConfig.DefaultFormat;
}
@@ -62,5 +71,8 @@ public class CliConfig
public string? Server { get; set; }
public int Port { get; set; } = 636;
public bool UseTls { get; set; } = true;
public string? SearchBase { get; set; }
public string? ServiceAccountDn { get; set; }
public string? ServiceAccountPassword { get; set; }
}
}

View File

@@ -6,31 +6,31 @@ namespace ScadaLink.CLI.Commands;
public static class ApiMethodCommands
{
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption)
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var command = new Command("api-method") { Description = "Manage inbound API methods" };
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));
command.Add(BuildList(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildGet(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildUpdate(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
return command;
}
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var cmd = new Command("list") { Description = "List all API methods" };
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new ListApiMethodsCommand());
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListApiMethodsCommand());
});
return cmd;
}
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "API method ID", Required = true };
var cmd = new Command("get") { Description = "Get an API method by ID" };
@@ -39,12 +39,12 @@ public static class ApiMethodCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new GetApiMethodCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetApiMethodCommand(id));
});
return cmd;
}
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var nameOption = new Option<string>("--name") { Description = "Method name", Required = true };
var scriptOption = new Option<string>("--script") { Description = "Script code", Required = true };
@@ -67,13 +67,13 @@ public static class ApiMethodCommands
var parameters = result.GetValue(parametersOption);
var returnDef = result.GetValue(returnDefOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new CreateApiMethodCommand(name, script, timeout, parameters, returnDef));
});
return cmd;
}
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "API method ID", Required = true };
var scriptOption = new Option<string>("--script") { Description = "Script code", Required = true };
@@ -96,13 +96,13 @@ public static class ApiMethodCommands
var parameters = result.GetValue(parametersOption);
var returnDef = result.GetValue(returnDefOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new UpdateApiMethodCommand(id, script, timeout, parameters, returnDef));
});
return cmd;
}
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "API method ID", Required = true };
var cmd = new Command("delete") { Description = "Delete an API method" };
@@ -111,7 +111,7 @@ public static class ApiMethodCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new DeleteApiMethodCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteApiMethodCommand(id));
});
return cmd;
}

View File

@@ -6,16 +6,16 @@ namespace ScadaLink.CLI.Commands;
public static class AuditLogCommands
{
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption)
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var command = new Command("audit-log") { Description = "Query audit logs" };
command.Add(BuildQuery(contactPointsOption, formatOption));
command.Add(BuildQuery(contactPointsOption, formatOption, usernameOption, passwordOption));
return command;
}
private static Command BuildQuery(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildQuery(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var userOption = new Option<string?>("--user") { Description = "Filter by username" };
var entityTypeOption = new Option<string?>("--entity-type") { Description = "Filter by entity type" };
@@ -45,7 +45,7 @@ public static class AuditLogCommands
var page = result.GetValue(pageOption);
var pageSize = result.GetValue(pageSizeOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new QueryAuditLogCommand(user, entityType, action, from, to, page, pageSize));
});
return cmd;

View File

@@ -1,27 +1,33 @@
using System.CommandLine;
using System.CommandLine.Parsing;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using ScadaLink.Commons.Messages.Management;
using ScadaLink.Security;
namespace ScadaLink.CLI.Commands;
internal static class CommandHelpers
{
internal static AuthenticatedUser PlaceholderUser { get; } =
new("cli-user", "CLI User", ["Admin", "Design", "Deployment"], Array.Empty<string>());
internal static string NewCorrelationId() => Guid.NewGuid().ToString("N");
internal static async Task<int> ExecuteCommandAsync(
ParseResult result,
Option<string> contactPointsOption,
Option<string> formatOption,
Option<string> usernameOption,
Option<string> passwordOption,
object command)
{
var contactPointsRaw = result.GetValue(contactPointsOption);
var format = result.GetValue(formatOption) ?? "json";
var config = CliConfig.Load();
if (string.IsNullOrWhiteSpace(contactPointsRaw))
{
var config = CliConfig.Load();
if (config.ContactPoints.Count > 0)
contactPointsRaw = string.Join(",", config.ContactPoints);
}
@@ -34,21 +40,97 @@ internal static class CommandHelpers
var contactPoints = contactPointsRaw.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
// Authenticate via LDAP
var username = result.GetValue(usernameOption);
var password = result.GetValue(passwordOption);
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
{
OutputFormatter.WriteError(
"Credentials required. Use --username and --password options.",
"NO_CREDENTIALS");
return 1;
}
// Authenticate against LDAP
var securityOptions = new SecurityOptions
{
LdapServer = config.LdapServer ?? string.Empty,
LdapPort = config.LdapPort,
LdapUseTls = config.LdapUseTls,
AllowInsecureLdap = !config.LdapUseTls,
LdapSearchBase = config.LdapSearchBase,
LdapServiceAccountDn = config.LdapServiceAccountDn,
LdapServiceAccountPassword = config.LdapServiceAccountPassword
};
var ldapAuth = new LdapAuthService(
Options.Create(securityOptions),
NullLogger<LdapAuthService>.Instance);
var authResult = await ldapAuth.AuthenticateAsync(username, password);
if (!authResult.Success)
{
OutputFormatter.WriteError(
authResult.ErrorMessage ?? "Authentication failed.",
"AUTH_FAILED");
return 1;
}
await using var connection = new ClusterConnection();
await connection.ConnectAsync(contactPoints, TimeSpan.FromSeconds(10));
var envelope = new ManagementEnvelope(PlaceholderUser, command, NewCorrelationId());
// Resolve roles server-side
var resolveEnvelope = new ManagementEnvelope(
new AuthenticatedUser(authResult.Username!, authResult.DisplayName!, Array.Empty<string>(), Array.Empty<string>()),
new ResolveRolesCommand(authResult.Groups ?? (IReadOnlyList<string>)Array.Empty<string>()),
NewCorrelationId());
var resolveResponse = await connection.AskManagementAsync(resolveEnvelope, TimeSpan.FromSeconds(30));
string[] roles;
string[] permittedSiteIds;
if (resolveResponse is ManagementSuccess resolveSuccess)
{
var rolesDoc = JsonDocument.Parse(resolveSuccess.JsonData);
roles = rolesDoc.RootElement.TryGetProperty("Roles", out var rolesEl)
? rolesEl.EnumerateArray().Select(e => e.GetString()!).ToArray()
: Array.Empty<string>();
permittedSiteIds = rolesDoc.RootElement.TryGetProperty("PermittedSiteIds", out var sitesEl)
? sitesEl.EnumerateArray().Select(e => e.GetString()!).ToArray()
: Array.Empty<string>();
}
else
{
return HandleResponse(resolveResponse, format);
}
var authenticatedUser = new AuthenticatedUser(
authResult.Username!,
authResult.DisplayName!,
roles,
permittedSiteIds);
var envelope = new ManagementEnvelope(authenticatedUser, command, NewCorrelationId());
var response = await connection.AskManagementAsync(envelope, TimeSpan.FromSeconds(30));
return HandleResponse(response);
return HandleResponse(response, format);
}
internal static int HandleResponse(object response)
internal static int HandleResponse(object response, string format)
{
switch (response)
{
case ManagementSuccess success:
Console.WriteLine(success.JsonData);
if (string.Equals(format, "table", StringComparison.OrdinalIgnoreCase))
{
WriteAsTable(success.JsonData);
}
else
{
Console.WriteLine(success.JsonData);
}
return 0;
case ManagementError error:
@@ -64,4 +146,51 @@ internal static class CommandHelpers
return 1;
}
}
private static void WriteAsTable(string json)
{
using var doc = JsonDocument.Parse(json);
var root = doc.RootElement;
if (root.ValueKind == JsonValueKind.Array)
{
var items = root.EnumerateArray().ToList();
if (items.Count == 0)
{
Console.WriteLine("(no results)");
return;
}
// Extract headers from first object's property names
var headers = items[0].ValueKind == JsonValueKind.Object
? items[0].EnumerateObject().Select(p => p.Name).ToArray()
: new[] { "Value" };
var rows = items.Select(item =>
{
if (item.ValueKind == JsonValueKind.Object)
{
return headers.Select(h =>
item.TryGetProperty(h, out var val)
? val.ValueKind == JsonValueKind.Null ? "" : val.ToString()
: "").ToArray();
}
return new[] { item.ToString() };
});
OutputFormatter.WriteTable(rows, headers);
}
else if (root.ValueKind == JsonValueKind.Object)
{
// Single object: render as key-value pairs
var headers = new[] { "Property", "Value" };
var rows = root.EnumerateObject().Select(p =>
new[] { p.Name, p.Value.ValueKind == JsonValueKind.Null ? "" : p.Value.ToString() });
OutputFormatter.WriteTable(rows, headers);
}
else
{
Console.WriteLine(root.ToString());
}
}
}

View File

@@ -6,22 +6,22 @@ namespace ScadaLink.CLI.Commands;
public static class DataConnectionCommands
{
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption)
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var command = new Command("data-connection") { Description = "Manage data connections" };
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));
command.Add(BuildAssign(contactPointsOption, formatOption));
command.Add(BuildUnassign(contactPointsOption, formatOption));
command.Add(BuildList(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildGet(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildUpdate(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildAssign(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildUnassign(contactPointsOption, formatOption, usernameOption, passwordOption));
return command;
}
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Data connection ID", Required = true };
var cmd = new Command("get") { Description = "Get a data connection by ID" };
@@ -30,12 +30,12 @@ public static class DataConnectionCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new GetDataConnectionCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetDataConnectionCommand(id));
});
return cmd;
}
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Data connection ID", Required = true };
var nameOption = new Option<string>("--name") { Description = "Connection name", Required = true };
@@ -54,13 +54,13 @@ public static class DataConnectionCommands
var protocol = result.GetValue(protocolOption)!;
var config = result.GetValue(configOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new UpdateDataConnectionCommand(id, name, protocol, config));
});
return cmd;
}
private static Command BuildUnassign(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildUnassign(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--assignment-id") { Description = "Assignment ID", Required = true };
var cmd = new Command("unassign") { Description = "Unassign a data connection from a site" };
@@ -69,23 +69,23 @@ public static class DataConnectionCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new UnassignDataConnectionFromSiteCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new UnassignDataConnectionFromSiteCommand(id));
});
return cmd;
}
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var cmd = new Command("list") { Description = "List all data connections" };
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new ListDataConnectionsCommand());
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListDataConnectionsCommand());
});
return cmd;
}
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var nameOption = new Option<string>("--name") { Description = "Connection name", Required = true };
var protocolOption = new Option<string>("--protocol") { Description = "Protocol (e.g. OpcUa)", Required = true };
@@ -101,13 +101,13 @@ public static class DataConnectionCommands
var protocol = result.GetValue(protocolOption)!;
var config = result.GetValue(configOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new CreateDataConnectionCommand(name, protocol, config));
});
return cmd;
}
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Data connection ID", Required = true };
var cmd = new Command("delete") { Description = "Delete a data connection" };
@@ -116,12 +116,12 @@ public static class DataConnectionCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new DeleteDataConnectionCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteDataConnectionCommand(id));
});
return cmd;
}
private static Command BuildAssign(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildAssign(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var connectionIdOption = new Option<int>("--connection-id") { Description = "Data connection ID", Required = true };
var siteIdOption = new Option<int>("--site-id") { Description = "Site ID", Required = true };
@@ -134,7 +134,7 @@ public static class DataConnectionCommands
var connectionId = result.GetValue(connectionIdOption);
var siteId = result.GetValue(siteIdOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new AssignDataConnectionToSiteCommand(connectionId, siteId));
});
return cmd;

View File

@@ -6,31 +6,31 @@ namespace ScadaLink.CLI.Commands;
public static class DbConnectionCommands
{
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption)
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var command = new Command("db-connection") { Description = "Manage database connections" };
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));
command.Add(BuildList(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildGet(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildUpdate(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
return command;
}
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var cmd = new Command("list") { Description = "List all database connections" };
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new ListDatabaseConnectionsCommand());
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListDatabaseConnectionsCommand());
});
return cmd;
}
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Database connection ID", Required = true };
var cmd = new Command("get") { Description = "Get a database connection by ID" };
@@ -39,12 +39,12 @@ public static class DbConnectionCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new GetDatabaseConnectionCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetDatabaseConnectionCommand(id));
});
return cmd;
}
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var nameOption = new Option<string>("--name") { Description = "Connection name", Required = true };
var connStrOption = new Option<string>("--connection-string") { Description = "Connection string", Required = true };
@@ -57,13 +57,13 @@ public static class DbConnectionCommands
var name = result.GetValue(nameOption)!;
var connStr = result.GetValue(connStrOption)!;
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new CreateDatabaseConnectionDefCommand(name, connStr));
});
return cmd;
}
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Database connection ID", Required = true };
var nameOption = new Option<string>("--name") { Description = "Connection name", Required = true };
@@ -79,13 +79,13 @@ public static class DbConnectionCommands
var name = result.GetValue(nameOption)!;
var connStr = result.GetValue(connStrOption)!;
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new UpdateDatabaseConnectionDefCommand(id, name, connStr));
});
return cmd;
}
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Database connection ID", Required = true };
var cmd = new Command("delete") { Description = "Delete a database connection" };
@@ -94,7 +94,7 @@ public static class DbConnectionCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new DeleteDatabaseConnectionDefCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteDatabaseConnectionDefCommand(id));
});
return cmd;
}

View File

@@ -6,16 +6,16 @@ namespace ScadaLink.CLI.Commands;
public static class DebugCommands
{
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption)
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var command = new Command("debug") { Description = "Runtime debugging" };
command.Add(BuildSnapshot(contactPointsOption, formatOption));
command.Add(BuildSnapshot(contactPointsOption, formatOption, usernameOption, passwordOption));
return command;
}
private static Command BuildSnapshot(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildSnapshot(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
var cmd = new Command("snapshot") { Description = "Get a point-in-time snapshot of instance attribute values and alarm states" };
@@ -23,7 +23,7 @@ public static class DebugCommands
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new DebugSnapshotCommand(result.GetValue(idOption)));
});
return cmd;

View File

@@ -6,18 +6,18 @@ namespace ScadaLink.CLI.Commands;
public static class DeployCommands
{
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption)
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var command = new Command("deploy") { Description = "Deployment operations" };
command.Add(BuildInstance(contactPointsOption, formatOption));
command.Add(BuildArtifacts(contactPointsOption, formatOption));
command.Add(BuildStatus(contactPointsOption, formatOption));
command.Add(BuildInstance(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildArtifacts(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildStatus(contactPointsOption, formatOption, usernameOption, passwordOption));
return command;
}
private static Command BuildInstance(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildInstance(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
var cmd = new Command("instance") { Description = "Deploy a single instance" };
@@ -26,12 +26,12 @@ public static class DeployCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new MgmtDeployInstanceCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new MgmtDeployInstanceCommand(id));
});
return cmd;
}
private static Command BuildArtifacts(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildArtifacts(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var siteIdOption = new Option<int?>("--site-id") { Description = "Target site ID (all sites if omitted)" };
var cmd = new Command("artifacts") { Description = "Deploy artifacts to site(s)" };
@@ -40,12 +40,12 @@ public static class DeployCommands
{
var siteId = result.GetValue(siteIdOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new MgmtDeployArtifactsCommand(siteId));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new MgmtDeployArtifactsCommand(siteId));
});
return cmd;
}
private static Command BuildStatus(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildStatus(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var instanceIdOption = new Option<int?>("--instance-id") { Description = "Filter by instance ID" };
var statusOption = new Option<string?>("--status") { Description = "Filter by status" };
@@ -66,7 +66,7 @@ public static class DeployCommands
var page = result.GetValue(pageOption);
var pageSize = result.GetValue(pageSizeOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new QueryDeploymentsCommand(instanceId, status, page, pageSize));
});
return cmd;

View File

@@ -6,21 +6,21 @@ namespace ScadaLink.CLI.Commands;
public static class ExternalSystemCommands
{
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption)
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var command = new Command("external-system") { Description = "Manage external systems" };
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));
command.Add(BuildMethodGroup(contactPointsOption, formatOption));
command.Add(BuildList(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildGet(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildUpdate(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildMethodGroup(contactPointsOption, formatOption, usernameOption, passwordOption));
return command;
}
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "External system ID", Required = true };
var cmd = new Command("get") { Description = "Get an external system by ID" };
@@ -29,12 +29,12 @@ public static class ExternalSystemCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new GetExternalSystemCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetExternalSystemCommand(id));
});
return cmd;
}
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "External system ID", Required = true };
var nameOption = new Option<string>("--name") { Description = "System name", Required = true };
@@ -56,24 +56,24 @@ public static class ExternalSystemCommands
var authType = result.GetValue(authTypeOption)!;
var authConfig = result.GetValue(authConfigOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new UpdateExternalSystemCommand(id, name, url, authType, authConfig));
});
return cmd;
}
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var cmd = new Command("list") { Description = "List all external systems" };
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new ListExternalSystemsCommand());
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListExternalSystemsCommand());
});
return cmd;
}
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var nameOption = new Option<string>("--name") { Description = "System name", Required = true };
var urlOption = new Option<string>("--endpoint-url") { Description = "Endpoint URL", Required = true };
@@ -92,13 +92,13 @@ public static class ExternalSystemCommands
var authType = result.GetValue(authTypeOption)!;
var authConfig = result.GetValue(authConfigOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new CreateExternalSystemCommand(name, url, authType, authConfig));
});
return cmd;
}
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "External system ID", Required = true };
var cmd = new Command("delete") { Description = "Delete an external system" };
@@ -107,25 +107,25 @@ public static class ExternalSystemCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new DeleteExternalSystemCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteExternalSystemCommand(id));
});
return cmd;
}
// ── Method subcommands ──
// -- Method subcommands --
private static Command BuildMethodGroup(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildMethodGroup(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var group = new Command("method") { Description = "Manage external system methods" };
group.Add(BuildMethodList(contactPointsOption, formatOption));
group.Add(BuildMethodGet(contactPointsOption, formatOption));
group.Add(BuildMethodCreate(contactPointsOption, formatOption));
group.Add(BuildMethodUpdate(contactPointsOption, formatOption));
group.Add(BuildMethodDelete(contactPointsOption, formatOption));
group.Add(BuildMethodList(contactPointsOption, formatOption, usernameOption, passwordOption));
group.Add(BuildMethodGet(contactPointsOption, formatOption, usernameOption, passwordOption));
group.Add(BuildMethodCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
group.Add(BuildMethodUpdate(contactPointsOption, formatOption, usernameOption, passwordOption));
group.Add(BuildMethodDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
return group;
}
private static Command BuildMethodList(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildMethodList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var sysIdOption = new Option<int>("--external-system-id") { Description = "External system ID", Required = true };
var cmd = new Command("list") { Description = "List methods for an external system" };
@@ -133,13 +133,13 @@ public static class ExternalSystemCommands
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new ListExternalSystemMethodsCommand(result.GetValue(sysIdOption)));
});
return cmd;
}
private static Command BuildMethodGet(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildMethodGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Method ID", Required = true };
var cmd = new Command("get") { Description = "Get an external system method by ID" };
@@ -147,13 +147,13 @@ public static class ExternalSystemCommands
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new GetExternalSystemMethodCommand(result.GetValue(idOption)));
});
return cmd;
}
private static Command BuildMethodCreate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildMethodCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var sysIdOption = new Option<int>("--external-system-id") { Description = "External system ID", Required = true };
var nameOption = new Option<string>("--name") { Description = "Method name", Required = true };
@@ -172,7 +172,7 @@ public static class ExternalSystemCommands
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new CreateExternalSystemMethodCommand(
result.GetValue(sysIdOption),
result.GetValue(nameOption)!,
@@ -184,7 +184,7 @@ public static class ExternalSystemCommands
return cmd;
}
private static Command BuildMethodUpdate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildMethodUpdate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Method ID", Required = true };
var nameOption = new Option<string?>("--name") { Description = "Method name" };
@@ -203,7 +203,7 @@ public static class ExternalSystemCommands
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new UpdateExternalSystemMethodCommand(
result.GetValue(idOption),
result.GetValue(nameOption),
@@ -215,7 +215,7 @@ public static class ExternalSystemCommands
return cmd;
}
private static Command BuildMethodDelete(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildMethodDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Method ID", Required = true };
var cmd = new Command("delete") { Description = "Delete an external system method" };
@@ -223,7 +223,7 @@ public static class ExternalSystemCommands
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new DeleteExternalSystemMethodCommand(result.GetValue(idOption)));
});
return cmd;

View File

@@ -6,30 +6,30 @@ namespace ScadaLink.CLI.Commands;
public static class HealthCommands
{
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption)
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var command = new Command("health") { Description = "Health monitoring" };
command.Add(BuildSummary(contactPointsOption, formatOption));
command.Add(BuildSite(contactPointsOption, formatOption));
command.Add(BuildEventLog(contactPointsOption, formatOption));
command.Add(BuildParkedMessages(contactPointsOption, formatOption));
command.Add(BuildSummary(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildSite(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildEventLog(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildParkedMessages(contactPointsOption, formatOption, usernameOption, passwordOption));
return command;
}
private static Command BuildSummary(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildSummary(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var cmd = new Command("summary") { Description = "Get system health summary" };
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new GetHealthSummaryCommand());
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetHealthSummaryCommand());
});
return cmd;
}
private static Command BuildSite(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildSite(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var identifierOption = new Option<string>("--identifier") { Description = "Site identifier", Required = true };
var cmd = new Command("site") { Description = "Get health for a specific site" };
@@ -38,12 +38,12 @@ public static class HealthCommands
{
var identifier = result.GetValue(identifierOption)!;
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new GetSiteHealthCommand(identifier));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetSiteHealthCommand(identifier));
});
return cmd;
}
private static Command BuildEventLog(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildEventLog(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var siteOption = new Option<string>("--site") { Description = "Site identifier", Required = true };
var eventTypeOption = new Option<string?>("--event-type") { Description = "Filter by event type" };
@@ -55,6 +55,7 @@ public static class HealthCommands
pageOption.DefaultValueFactory = _ => 1;
var pageSizeOption = new Option<int>("--page-size") { Description = "Page size" };
pageSizeOption.DefaultValueFactory = _ => 50;
var instanceNameOption = new Option<string?>("--instance-name") { Description = "Filter by instance name" };
var cmd = new Command("event-log") { Description = "Query site event logs" };
cmd.Add(siteOption);
@@ -65,10 +66,11 @@ public static class HealthCommands
cmd.Add(toOption);
cmd.Add(pageOption);
cmd.Add(pageSizeOption);
cmd.Add(instanceNameOption);
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new QueryEventLogsCommand(
result.GetValue(siteOption)!,
result.GetValue(eventTypeOption),
@@ -77,12 +79,13 @@ public static class HealthCommands
result.GetValue(fromOption),
result.GetValue(toOption),
result.GetValue(pageOption),
result.GetValue(pageSizeOption)));
result.GetValue(pageSizeOption),
result.GetValue(instanceNameOption)));
});
return cmd;
}
private static Command BuildParkedMessages(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildParkedMessages(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var siteOption = new Option<string>("--site") { Description = "Site identifier", Required = true };
var pageOption = new Option<int>("--page") { Description = "Page number" };
@@ -97,7 +100,7 @@ public static class HealthCommands
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new QueryParkedMessagesCommand(
result.GetValue(siteOption)!,
result.GetValue(pageOption),

View File

@@ -6,23 +6,26 @@ namespace ScadaLink.CLI.Commands;
public static class InstanceCommands
{
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption)
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var command = new Command("instance") { Description = "Manage instances" };
command.Add(BuildList(contactPointsOption, formatOption));
command.Add(BuildGet(contactPointsOption, formatOption));
command.Add(BuildCreate(contactPointsOption, formatOption));
command.Add(BuildSetBindings(contactPointsOption, formatOption));
command.Add(BuildDeploy(contactPointsOption, formatOption));
command.Add(BuildEnable(contactPointsOption, formatOption));
command.Add(BuildDisable(contactPointsOption, formatOption));
command.Add(BuildDelete(contactPointsOption, formatOption));
command.Add(BuildList(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildGet(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildSetBindings(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildSetOverrides(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildSetArea(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildDiff(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildDeploy(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildEnable(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildDisable(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
return command;
}
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
var cmd = new Command("get") { Description = "Get an instance by ID" };
@@ -31,12 +34,12 @@ public static class InstanceCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new GetInstanceCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetInstanceCommand(id));
});
return cmd;
}
private static Command BuildSetBindings(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildSetBindings(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
var bindingsOption = new Option<string>("--bindings") { Description = "JSON array of [attributeName, dataConnectionId] pairs", Required = true };
@@ -53,13 +56,13 @@ public static class InstanceCommands
var bindings = pairs.Select(p =>
(p[0].ToString()!, int.Parse(p[1].ToString()!))).ToList();
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new SetConnectionBindingsCommand(id, bindings));
});
return cmd;
}
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var siteIdOption = new Option<int?>("--site-id") { Description = "Filter by site ID" };
var templateIdOption = new Option<int?>("--template-id") { Description = "Filter by template ID" };
@@ -75,13 +78,13 @@ public static class InstanceCommands
var templateId = result.GetValue(templateIdOption);
var search = result.GetValue(searchOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new ListInstancesCommand(siteId, templateId, search));
});
return cmd;
}
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var nameOption = new Option<string>("--name") { Description = "Unique instance name", Required = true };
var templateIdOption = new Option<int>("--template-id") { Description = "Template ID", Required = true };
@@ -100,13 +103,13 @@ public static class InstanceCommands
var siteId = result.GetValue(siteIdOption);
var areaId = result.GetValue(areaIdOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new CreateInstanceCommand(name, templateId, siteId, areaId));
});
return cmd;
}
private static Command BuildDeploy(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildDeploy(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
var cmd = new Command("deploy") { Description = "Deploy an instance" };
@@ -115,12 +118,12 @@ public static class InstanceCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new MgmtDeployInstanceCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new MgmtDeployInstanceCommand(id));
});
return cmd;
}
private static Command BuildEnable(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildEnable(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
var cmd = new Command("enable") { Description = "Enable an instance" };
@@ -129,12 +132,12 @@ public static class InstanceCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new MgmtEnableInstanceCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new MgmtEnableInstanceCommand(id));
});
return cmd;
}
private static Command BuildDisable(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildDisable(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
var cmd = new Command("disable") { Description = "Disable an instance" };
@@ -143,12 +146,12 @@ public static class InstanceCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new MgmtDisableInstanceCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new MgmtDisableInstanceCommand(id));
});
return cmd;
}
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
var cmd = new Command("delete") { Description = "Delete an instance" };
@@ -157,7 +160,63 @@ public static class InstanceCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new MgmtDeleteInstanceCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new MgmtDeleteInstanceCommand(id));
});
return cmd;
}
private static Command BuildSetOverrides(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
var overridesOption = new Option<string>("--overrides") { Description = "JSON object of attribute name -> value pairs, e.g. {\"Speed\": \"100\", \"Mode\": null}", Required = true };
var cmd = new Command("set-overrides") { Description = "Set attribute overrides for an instance" };
cmd.Add(idOption);
cmd.Add(overridesOption);
cmd.SetAction(async (ParseResult result) =>
{
var id = result.GetValue(idOption);
var overridesJson = result.GetValue(overridesOption)!;
var overrides = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string?>>(overridesJson)
?? throw new InvalidOperationException("Invalid overrides JSON");
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new SetInstanceOverridesCommand(id, overrides));
});
return cmd;
}
private static Command BuildSetArea(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
var areaIdOption = new Option<int?>("--area-id") { Description = "Area ID (omit to clear area assignment)" };
var cmd = new Command("set-area") { Description = "Reassign an instance to a different area" };
cmd.Add(idOption);
cmd.Add(areaIdOption);
cmd.SetAction(async (ParseResult result) =>
{
var id = result.GetValue(idOption);
var areaId = result.GetValue(areaIdOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new SetInstanceAreaCommand(id, areaId));
});
return cmd;
}
private static Command BuildDiff(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
var cmd = new Command("diff") { Description = "Show deployment diff (deployed vs current template)" };
cmd.Add(idOption);
cmd.SetAction(async (ParseResult result) =>
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new GetDeploymentDiffCommand(id));
});
return cmd;
}

View File

@@ -6,21 +6,21 @@ namespace ScadaLink.CLI.Commands;
public static class NotificationCommands
{
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption)
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var command = new Command("notification") { Description = "Manage notification lists" };
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));
command.Add(BuildSmtp(contactPointsOption, formatOption));
command.Add(BuildList(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildGet(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildUpdate(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildSmtp(contactPointsOption, formatOption, usernameOption, passwordOption));
return command;
}
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Notification list ID", Required = true };
var cmd = new Command("get") { Description = "Get a notification list by ID" };
@@ -29,12 +29,12 @@ public static class NotificationCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new GetNotificationListCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetNotificationListCommand(id));
});
return cmd;
}
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Notification list ID", Required = true };
var nameOption = new Option<string>("--name") { Description = "List name", Required = true };
@@ -51,13 +51,13 @@ public static class NotificationCommands
var emailsRaw = result.GetValue(emailsOption)!;
var emails = emailsRaw.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList();
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new UpdateNotificationListCommand(id, name, emails));
});
return cmd;
}
private static Command BuildSmtp(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildSmtp(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var group = new Command("smtp") { Description = "Manage SMTP configuration" };
@@ -65,7 +65,7 @@ public static class NotificationCommands
listCmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new ListSmtpConfigsCommand());
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListSmtpConfigsCommand());
});
group.Add(listCmd);
@@ -88,7 +88,7 @@ public static class NotificationCommands
var authMode = result.GetValue(authModeOption)!;
var from = result.GetValue(fromOption)!;
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new UpdateSmtpConfigCommand(id, server, port, authMode, from));
});
group.Add(updateCmd);
@@ -96,18 +96,18 @@ public static class NotificationCommands
return group;
}
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var cmd = new Command("list") { Description = "List all notification lists" };
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new ListNotificationListsCommand());
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListNotificationListsCommand());
});
return cmd;
}
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var nameOption = new Option<string>("--name") { Description = "Notification list name", Required = true };
var emailsOption = new Option<string>("--emails") { Description = "Comma-separated recipient emails", Required = true };
@@ -121,13 +121,13 @@ public static class NotificationCommands
var emailsRaw = result.GetValue(emailsOption)!;
var emails = emailsRaw.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList();
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new CreateNotificationListCommand(name, emails));
});
return cmd;
}
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Notification list ID", Required = true };
var cmd = new Command("delete") { Description = "Delete a notification list" };
@@ -136,7 +136,7 @@ public static class NotificationCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new DeleteNotificationListCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteNotificationListCommand(id));
});
return cmd;
}

View File

@@ -6,18 +6,18 @@ namespace ScadaLink.CLI.Commands;
public static class SecurityCommands
{
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption)
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var command = new Command("security") { Description = "Manage security settings" };
command.Add(BuildApiKey(contactPointsOption, formatOption));
command.Add(BuildRoleMapping(contactPointsOption, formatOption));
command.Add(BuildScopeRule(contactPointsOption, formatOption));
command.Add(BuildApiKey(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildRoleMapping(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildScopeRule(contactPointsOption, formatOption, usernameOption, passwordOption));
return command;
}
private static Command BuildApiKey(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildApiKey(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var group = new Command("api-key") { Description = "Manage API keys" };
@@ -25,7 +25,7 @@ public static class SecurityCommands
listCmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new ListApiKeysCommand());
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListApiKeysCommand());
});
group.Add(listCmd);
@@ -36,7 +36,7 @@ public static class SecurityCommands
{
var name = result.GetValue(nameOption)!;
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new CreateApiKeyCommand(name));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new CreateApiKeyCommand(name));
});
group.Add(createCmd);
@@ -47,7 +47,7 @@ public static class SecurityCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new DeleteApiKeyCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteApiKeyCommand(id));
});
group.Add(deleteCmd);
@@ -61,14 +61,14 @@ public static class SecurityCommands
var id = result.GetValue(updateIdOption);
var enabled = result.GetValue(enabledOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new UpdateApiKeyCommand(id, enabled));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new UpdateApiKeyCommand(id, enabled));
});
group.Add(updateCmd);
return group;
}
private static Command BuildRoleMapping(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildRoleMapping(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var group = new Command("role-mapping") { Description = "Manage LDAP role mappings" };
@@ -76,7 +76,7 @@ public static class SecurityCommands
listCmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new ListRoleMappingsCommand());
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListRoleMappingsCommand());
});
group.Add(listCmd);
@@ -90,7 +90,7 @@ public static class SecurityCommands
var ldapGroup = result.GetValue(ldapGroupOption)!;
var role = result.GetValue(roleOption)!;
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new CreateRoleMappingCommand(ldapGroup, role));
});
group.Add(createCmd);
@@ -102,7 +102,7 @@ public static class SecurityCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new DeleteRoleMappingCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteRoleMappingCommand(id));
});
group.Add(deleteCmd);
@@ -119,7 +119,7 @@ public static class SecurityCommands
var ldapGroup = result.GetValue(updateLdapGroupOption)!;
var role = result.GetValue(updateRoleOption)!;
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new UpdateRoleMappingCommand(id, ldapGroup, role));
});
group.Add(updateCmd);
@@ -127,7 +127,7 @@ public static class SecurityCommands
return group;
}
private static Command BuildScopeRule(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildScopeRule(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var group = new Command("scope-rule") { Description = "Manage LDAP scope rules" };
@@ -138,7 +138,7 @@ public static class SecurityCommands
{
var mappingId = result.GetValue(mappingIdOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new ListScopeRulesCommand(mappingId));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListScopeRulesCommand(mappingId));
});
group.Add(listCmd);
@@ -152,7 +152,7 @@ public static class SecurityCommands
var mappingId = result.GetValue(addMappingIdOption);
var siteId = result.GetValue(siteIdOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new AddScopeRuleCommand(mappingId, siteId));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new AddScopeRuleCommand(mappingId, siteId));
});
group.Add(addCmd);
@@ -163,7 +163,7 @@ public static class SecurityCommands
{
var id = result.GetValue(deleteIdOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new DeleteScopeRuleCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteScopeRuleCommand(id));
});
group.Add(deleteCmd);

View File

@@ -6,31 +6,31 @@ namespace ScadaLink.CLI.Commands;
public static class SharedScriptCommands
{
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption)
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
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));
command.Add(BuildList(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildGet(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildUpdate(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
return command;
}
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var cmd = new Command("list") { Description = "List all shared scripts" };
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new ListSharedScriptsCommand());
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListSharedScriptsCommand());
});
return cmd;
}
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Shared script ID", Required = true };
var cmd = new Command("get") { Description = "Get a shared script by ID" };
@@ -39,12 +39,12 @@ public static class SharedScriptCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new GetSharedScriptCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetSharedScriptCommand(id));
});
return cmd;
}
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var nameOption = new Option<string>("--name") { Description = "Script name", Required = true };
var codeOption = new Option<string>("--code") { Description = "Script code", Required = true };
@@ -63,13 +63,13 @@ public static class SharedScriptCommands
var parameters = result.GetValue(parametersOption);
var returnDef = result.GetValue(returnDefOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new CreateSharedScriptCommand(name, code, parameters, returnDef));
});
return cmd;
}
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Shared script ID", Required = true };
var nameOption = new Option<string>("--name") { Description = "Script name", Required = true };
@@ -91,13 +91,13 @@ public static class SharedScriptCommands
var parameters = result.GetValue(parametersOption);
var returnDef = result.GetValue(returnDefOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new UpdateSharedScriptCommand(id, name, code, parameters, returnDef));
});
return cmd;
}
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Shared script ID", Required = true };
var cmd = new Command("delete") { Description = "Delete a shared script" };
@@ -106,7 +106,7 @@ public static class SharedScriptCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new DeleteSharedScriptCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteSharedScriptCommand(id));
});
return cmd;
}

View File

@@ -6,22 +6,22 @@ namespace ScadaLink.CLI.Commands;
public static class SiteCommands
{
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption)
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var command = new Command("site") { Description = "Manage sites" };
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));
command.Add(BuildDeployArtifacts(contactPointsOption, formatOption));
command.Add(BuildArea(contactPointsOption, formatOption));
command.Add(BuildList(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildGet(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildUpdate(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildDeployArtifacts(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildArea(contactPointsOption, formatOption, usernameOption, passwordOption));
return command;
}
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Site ID", Required = true };
var cmd = new Command("get") { Description = "Get a site by ID" };
@@ -30,23 +30,23 @@ public static class SiteCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new GetSiteCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetSiteCommand(id));
});
return cmd;
}
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var cmd = new Command("list") { Description = "List all sites" };
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new ListSitesCommand());
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListSitesCommand());
});
return cmd;
}
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var nameOption = new Option<string>("--name") { Description = "Site name", Required = true };
var identifierOption = new Option<string>("--identifier") { Description = "Site identifier", Required = true };
@@ -68,13 +68,13 @@ public static class SiteCommands
var nodeA = result.GetValue(nodeAOption);
var nodeB = result.GetValue(nodeBOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new CreateSiteCommand(name, identifier, desc, nodeA, nodeB));
});
return cmd;
}
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Site ID", Required = true };
var nameOption = new Option<string>("--name") { Description = "Site name", Required = true };
@@ -96,13 +96,13 @@ public static class SiteCommands
var nodeA = result.GetValue(nodeAOption);
var nodeB = result.GetValue(nodeBOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new UpdateSiteCommand(id, name, desc, nodeA, nodeB));
});
return cmd;
}
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Site ID", Required = true };
var cmd = new Command("delete") { Description = "Delete a site" };
@@ -111,12 +111,12 @@ public static class SiteCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new DeleteSiteCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteSiteCommand(id));
});
return cmd;
}
private static Command BuildArea(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildArea(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var group = new Command("area") { Description = "Manage areas" };
@@ -127,7 +127,7 @@ public static class SiteCommands
{
var siteId = result.GetValue(siteIdOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new ListAreasCommand(siteId));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListAreasCommand(siteId));
});
group.Add(listCmd);
@@ -144,7 +144,7 @@ public static class SiteCommands
var name = result.GetValue(nameOption)!;
var parentId = result.GetValue(parentOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new CreateAreaCommand(siteId, name, parentId));
});
group.Add(createCmd);
@@ -159,7 +159,7 @@ public static class SiteCommands
var id = result.GetValue(updateIdOption);
var name = result.GetValue(updateNameOption)!;
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new UpdateAreaCommand(id, name));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new UpdateAreaCommand(id, name));
});
group.Add(updateCmd);
@@ -170,14 +170,14 @@ public static class SiteCommands
{
var id = result.GetValue(deleteIdOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new DeleteAreaCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteAreaCommand(id));
});
group.Add(deleteCmd);
return group;
}
private static Command BuildDeployArtifacts(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildDeployArtifacts(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var siteIdOption = new Option<int?>("--site-id") { Description = "Target site ID (all sites if omitted)" };
var cmd = new Command("deploy-artifacts") { Description = "Deploy artifacts to site(s)" };
@@ -186,7 +186,7 @@ public static class SiteCommands
{
var siteId = result.GetValue(siteIdOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new MgmtDeployArtifactsCommand(siteId));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new MgmtDeployArtifactsCommand(siteId));
});
return cmd;
}

View File

@@ -6,36 +6,36 @@ namespace ScadaLink.CLI.Commands;
public static class TemplateCommands
{
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption)
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var command = new Command("template") { Description = "Manage templates" };
command.Add(BuildList(contactPointsOption, formatOption));
command.Add(BuildGet(contactPointsOption, formatOption));
command.Add(BuildCreate(contactPointsOption, formatOption));
command.Add(BuildUpdate(contactPointsOption, formatOption));
command.Add(BuildValidate(contactPointsOption, formatOption));
command.Add(BuildDelete(contactPointsOption, formatOption));
command.Add(BuildAttribute(contactPointsOption, formatOption));
command.Add(BuildAlarm(contactPointsOption, formatOption));
command.Add(BuildScript(contactPointsOption, formatOption));
command.Add(BuildComposition(contactPointsOption, formatOption));
command.Add(BuildList(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildGet(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildUpdate(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildValidate(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildAttribute(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildAlarm(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildScript(contactPointsOption, formatOption, usernameOption, passwordOption));
command.Add(BuildComposition(contactPointsOption, formatOption, usernameOption, passwordOption));
return command;
}
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var cmd = new Command("list") { Description = "List all templates" };
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new ListTemplatesCommand());
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListTemplatesCommand());
});
return cmd;
}
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Template ID", Required = true };
var cmd = new Command("get") { Description = "Get a template by ID" };
@@ -44,12 +44,12 @@ public static class TemplateCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new GetTemplateCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetTemplateCommand(id));
});
return cmd;
}
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var nameOption = new Option<string>("--name") { Description = "Template name", Required = true };
var descOption = new Option<string?>("--description") { Description = "Template description" };
@@ -65,13 +65,13 @@ public static class TemplateCommands
var desc = result.GetValue(descOption);
var parentId = result.GetValue(parentOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new CreateTemplateCommand(name, desc, parentId));
});
return cmd;
}
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Template ID", Required = true };
var nameOption = new Option<string>("--name") { Description = "Template name", Required = true };
@@ -90,13 +90,13 @@ public static class TemplateCommands
var desc = result.GetValue(descOption);
var parentId = result.GetValue(parentOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new UpdateTemplateCommand(id, name, desc, parentId));
});
return cmd;
}
private static Command BuildValidate(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildValidate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Template ID", Required = true };
var cmd = new Command("validate") { Description = "Validate a template" };
@@ -105,12 +105,12 @@ public static class TemplateCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new ValidateTemplateCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ValidateTemplateCommand(id));
});
return cmd;
}
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var idOption = new Option<int>("--id") { Description = "Template ID", Required = true };
var cmd = new Command("delete") { Description = "Delete a template" };
@@ -119,12 +119,12 @@ public static class TemplateCommands
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new DeleteTemplateCommand(id));
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteTemplateCommand(id));
});
return cmd;
}
private static Command BuildAttribute(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildAttribute(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var group = new Command("attribute") { Description = "Manage template attributes" };
@@ -148,7 +148,7 @@ public static class TemplateCommands
addCmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new AddTemplateAttributeCommand(
result.GetValue(templateIdOption),
result.GetValue(nameOption)!,
@@ -180,7 +180,7 @@ public static class TemplateCommands
updateCmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new UpdateTemplateAttributeCommand(
result.GetValue(updateIdOption),
result.GetValue(updateNameOption)!,
@@ -198,7 +198,7 @@ public static class TemplateCommands
deleteCmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new DeleteTemplateAttributeCommand(result.GetValue(deleteIdOption)));
});
group.Add(deleteCmd);
@@ -206,7 +206,7 @@ public static class TemplateCommands
return group;
}
private static Command BuildAlarm(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildAlarm(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var group = new Command("alarm") { Description = "Manage template alarms" };
@@ -230,7 +230,7 @@ public static class TemplateCommands
addCmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new AddTemplateAlarmCommand(
result.GetValue(templateIdOption),
result.GetValue(nameOption)!,
@@ -262,7 +262,7 @@ public static class TemplateCommands
updateCmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new UpdateTemplateAlarmCommand(
result.GetValue(updateIdOption),
result.GetValue(updateNameOption)!,
@@ -280,7 +280,7 @@ public static class TemplateCommands
deleteCmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new DeleteTemplateAlarmCommand(result.GetValue(deleteIdOption)));
});
group.Add(deleteCmd);
@@ -288,7 +288,7 @@ public static class TemplateCommands
return group;
}
private static Command BuildScript(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildScript(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var group = new Command("script") { Description = "Manage template scripts" };
@@ -315,7 +315,7 @@ public static class TemplateCommands
addCmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new AddTemplateScriptCommand(
result.GetValue(templateIdOption),
result.GetValue(nameOption)!,
@@ -351,7 +351,7 @@ public static class TemplateCommands
updateCmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new UpdateTemplateScriptCommand(
result.GetValue(updateIdOption),
result.GetValue(updateNameOption)!,
@@ -370,7 +370,7 @@ public static class TemplateCommands
deleteCmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new DeleteTemplateScriptCommand(result.GetValue(deleteIdOption)));
});
group.Add(deleteCmd);
@@ -378,7 +378,7 @@ public static class TemplateCommands
return group;
}
private static Command BuildComposition(Option<string> contactPointsOption, Option<string> formatOption)
private static Command BuildComposition(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var group = new Command("composition") { Description = "Manage template compositions" };
@@ -393,7 +393,7 @@ public static class TemplateCommands
addCmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new AddTemplateCompositionCommand(
result.GetValue(templateIdOption),
result.GetValue(instanceNameOption)!,
@@ -407,7 +407,7 @@ public static class TemplateCommands
deleteCmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
result, contactPointsOption, formatOption, usernameOption, passwordOption,
new DeleteTemplateCompositionCommand(result.GetValue(deleteIdOption)));
});
group.Add(deleteCmd);

View File

@@ -16,20 +16,20 @@ rootCommand.Add(passwordOption);
rootCommand.Add(formatOption);
// Register command groups
rootCommand.Add(TemplateCommands.Build(contactPointsOption, formatOption));
rootCommand.Add(InstanceCommands.Build(contactPointsOption, formatOption));
rootCommand.Add(SiteCommands.Build(contactPointsOption, formatOption));
rootCommand.Add(DeployCommands.Build(contactPointsOption, formatOption));
rootCommand.Add(DataConnectionCommands.Build(contactPointsOption, formatOption));
rootCommand.Add(ExternalSystemCommands.Build(contactPointsOption, formatOption));
rootCommand.Add(NotificationCommands.Build(contactPointsOption, formatOption));
rootCommand.Add(SecurityCommands.Build(contactPointsOption, formatOption));
rootCommand.Add(AuditLogCommands.Build(contactPointsOption, formatOption));
rootCommand.Add(HealthCommands.Build(contactPointsOption, formatOption));
rootCommand.Add(DebugCommands.Build(contactPointsOption, formatOption));
rootCommand.Add(SharedScriptCommands.Build(contactPointsOption, formatOption));
rootCommand.Add(DbConnectionCommands.Build(contactPointsOption, formatOption));
rootCommand.Add(ApiMethodCommands.Build(contactPointsOption, formatOption));
rootCommand.Add(TemplateCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
rootCommand.Add(InstanceCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
rootCommand.Add(SiteCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
rootCommand.Add(DeployCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
rootCommand.Add(DataConnectionCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
rootCommand.Add(ExternalSystemCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
rootCommand.Add(NotificationCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
rootCommand.Add(SecurityCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
rootCommand.Add(AuditLogCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
rootCommand.Add(HealthCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
rootCommand.Add(DebugCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
rootCommand.Add(SharedScriptCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
rootCommand.Add(DbConnectionCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
rootCommand.Add(ApiMethodCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
rootCommand.SetAction(_ =>
{

View File

@@ -41,8 +41,8 @@ These options are accepted by the root command and inherited by all subcommands.
| Option | Description |
|--------|-------------|
| `--contact-points <value>` | Comma-separated Akka cluster contact point URIs |
| `--username <value>` | LDAP username (reserved for future auth integration) |
| `--password <value>` | LDAP password (reserved for future auth integration) |
| `--username <value>` | LDAP username for authentication |
| `--password <value>` | LDAP password for authentication |
| `--format <json\|table>` | Output format (default: `json`) |
## Configuration File
@@ -55,12 +55,19 @@ These options are accepted by the root command and inherited by all subcommands.
"ldap": {
"server": "ldap.company.com",
"port": 636,
"useTls": true
"useTls": true,
"searchBase": "dc=example,dc=com",
"serviceAccountDn": "cn=admin,dc=example,dc=com",
"serviceAccountPassword": "secret"
},
"defaultFormat": "json"
}
```
The `searchBase` and `serviceAccountDn`/`serviceAccountPassword` fields are required for LDAP servers that need search-then-bind authentication (including the test GLAuth server). Without them, direct bind with `cn={username},{searchBase}` is attempted, which may fail if the user's DN doesn't follow that pattern.
For the Docker test environment, see `docker/README.md` for a ready-to-use config.
## Environment Variables
| Variable | Description |
@@ -435,7 +442,7 @@ scadalink --contact-points <uri> instance set-bindings --id <int> --bindings <js
| Option | Required | Description |
|--------|----------|-------------|
| `--id` | yes | Instance ID |
| `--bindings` | yes | JSON string mapping attribute names to data connection IDs (e.g. `{"attr1": 1, "attr2": 2}`) |
| `--bindings` | yes | JSON array of `[attributeName, dataConnectionId]` pairs (e.g. `[["Speed",7],["Temperature",7]]`) |
---
@@ -1270,7 +1277,7 @@ The CLI connects to the Central cluster using Akka.NET's `ClusterClient`. It doe
The connection is established per-command invocation and torn down cleanly via `CoordinatedShutdown` when the command completes.
Role enforcement is applied by the ManagementActor on the server side. The current CLI placeholder user carries `Admin`, `Design`, and `Deployment` roles; production use will integrate LDAP authentication via `--username` / `--password`.
Role enforcement is applied by the ManagementActor on the server side. The CLI authenticates against LDAP using `--username` / `--password`, resolves LDAP group memberships, then maps groups to ScadaLink roles (Admin, Design, Deployment) via role mappings configured in the security settings. Operations require the appropriate role — for example, creating templates requires `Design`, deploying requires `Deployment`. In the test environment, use the `multi-role` user (password: `password`) which has all three roles.
## Issues & Missing Features

View File

@@ -7,14 +7,20 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AssemblyName>scadalink</AssemblyName>
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="ScadaLink.CLI.Tests" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Akka" Version="1.5.62" />
<PackageReference Include="Akka.Remote" Version="1.5.62" />
<PackageReference Include="Akka.Cluster.Tools" Version="1.5.62" />
<PackageReference Include="System.CommandLine" Version="2.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.5" />
<PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="3.6.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../ScadaLink.Commons/ScadaLink.Commons.csproj" />
<ProjectReference Include="../ScadaLink.Security/ScadaLink.Security.csproj" />
</ItemGroup>
</Project>