feat: add HTTP Management API, migrate CLI from Akka ClusterClient to HTTP
Replace the CLI's Akka.NET ClusterClient transport with a simple HTTP client targeting a new POST /management endpoint on the Central Host. The endpoint handles Basic Auth, LDAP authentication, role resolution, and ManagementActor dispatch in a single round-trip — eliminating the CLI's Akka, LDAP, and Security dependencies. Also fixes DCL ReSubscribeAll losing subscriptions on repeated reconnect by deriving the tag list from _subscriptionsByInstance instead of _subscriptionIds.
This commit is contained in:
@@ -152,9 +152,9 @@ This project contains design documentation for a distributed SCADA system built
|
|||||||
|
|
||||||
### CLI Quick Reference (Docker / OrbStack)
|
### CLI Quick Reference (Docker / OrbStack)
|
||||||
|
|
||||||
- **Contact point**: `akka.tcp://scadalink@scadalink-central-a:8081` — the hostname must match the container's Akka `NodeHostname` config. Do NOT use `localhost:9011`; Akka remoting requires the hostname in the URI to match what the node advertises.
|
- **Management URL**: `http://localhost:9001` — the CLI connects to the Central Host's HTTP management API (port 5000 mapped to 9001 in Docker).
|
||||||
- **Test user**: `--username multi-role --password password` — has Admin, Design, and Deployment roles. The `admin` user only has the Admin role and cannot create templates, data connections, or deploy.
|
- **Test user**: `--username multi-role --password password` — has Admin, Design, and Deployment roles. The `admin` user only has the Admin role and cannot create templates, data connections, or deploy.
|
||||||
- **Config file**: `~/.scadalink/config.json` — stores contact points, LDAP settings (including `searchBase`, `serviceAccountDn`, `serviceAccountPassword`), and default format. See `docker/README.md` for a ready-to-use test config.
|
- **Config file**: `~/.scadalink/config.json` — stores `managementUrl` and default format. See `docker/README.md` for a ready-to-use test config.
|
||||||
- **Rebuild cluster**: `bash docker/deploy.sh` — builds the `scadalink:latest` image and recreates all containers. Run this after code changes to ManagementActor, Host, or any server-side component.
|
- **Rebuild cluster**: `bash docker/deploy.sh` — builds the `scadalink:latest` image and recreates all containers. Run this after code changes to ManagementActor, Host, or any server-side component.
|
||||||
- **Infrastructure services**: `cd infra && docker compose up -d` — starts LDAP, MS SQL, OPC UA, SMTP, REST API, and LmxFakeProxy. These are separate from the cluster containers in `docker/`.
|
- **Infrastructure services**: `cd infra && docker compose up -d` — starts LDAP, MS SQL, OPC UA, SMTP, REST API, and LmxFakeProxy. These are separate from the cluster containers in `docker/`.
|
||||||
- **All test LDAP passwords**: `password` (see `infra/glauth/config.toml` for users and groups).
|
- **All test LDAP passwords**: `password` (see `infra/glauth/config.toml` for users and groups).
|
||||||
|
|||||||
@@ -2,47 +2,43 @@
|
|||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
The CLI is a standalone command-line tool for scripting and automating administrative operations against the ScadaLink central cluster. It connects to the ManagementActor via Akka.NET ClusterClient — it does not join the cluster as a full member and does not use HTTP/REST. The CLI provides the same administrative capabilities as the Central UI, enabling automation, batch operations, and integration with CI/CD pipelines.
|
The CLI is a standalone command-line tool for scripting and automating administrative operations against the ScadaLink central cluster. It connects to the Central Host's HTTP Management API (`POST /management`), which dispatches commands to the ManagementActor. Authentication and role resolution are handled server-side — the CLI sends credentials via HTTP Basic Auth. The CLI provides the same administrative capabilities as the Central UI, enabling automation, batch operations, and integration with CI/CD pipelines.
|
||||||
|
|
||||||
## Location
|
## Location
|
||||||
|
|
||||||
Standalone executable, not part of the Host binary. Deployed on any Windows machine with network access to the central cluster.
|
Standalone executable, not part of the Host binary. Deployed on any machine with HTTP access to a central node.
|
||||||
|
|
||||||
`src/ScadaLink.CLI/`
|
`src/ScadaLink.CLI/`
|
||||||
|
|
||||||
## Responsibilities
|
## Responsibilities
|
||||||
|
|
||||||
- Parse command-line arguments and dispatch to the appropriate management operation.
|
- Parse command-line arguments and dispatch to the appropriate management operation.
|
||||||
- Authenticate the user via LDAP credentials and include identity in every message sent to the ManagementActor.
|
- Send HTTP requests to the Central Host's Management API endpoint with Basic Auth credentials.
|
||||||
- Connect to the central cluster via Akka.NET ClusterClient using configured contact points.
|
- Display structured responses from the Management API.
|
||||||
- Send management messages to the ManagementActor and display structured responses.
|
|
||||||
- Support both JSON and human-readable table output formats.
|
- Support both JSON and human-readable table output formats.
|
||||||
|
|
||||||
## Technology
|
## Technology
|
||||||
|
|
||||||
- **Argument parsing**: `System.CommandLine` library for command/subcommand/option parsing with built-in help generation.
|
- **Argument parsing**: `System.CommandLine` library for command/subcommand/option parsing with built-in help generation.
|
||||||
- **Transport**: Akka.NET `ClusterClient` connecting to `ClusterClientReceptionist` on the central cluster. The CLI does not join the cluster — it is a lightweight external client.
|
- **Transport**: HTTP client connecting to the Central Host's `POST /management` endpoint. Authentication is via HTTP Basic Auth — the server performs LDAP bind and role resolution.
|
||||||
- **Serialization**: Message contracts from Commons (`Messages/Management/`), same as ManagementActor expects.
|
- **Serialization**: Commands serialized as JSON with a type discriminator (`command` field). Message contracts from Commons define the command types.
|
||||||
|
|
||||||
## Authentication
|
## Authentication
|
||||||
|
|
||||||
The CLI authenticates the user against LDAP/AD before any operation:
|
The CLI sends user credentials to the Management API via HTTP Basic Auth:
|
||||||
|
|
||||||
1. The user provides credentials via `--username` / `--password` options, or is prompted interactively if omitted.
|
1. The user provides credentials via `--username` / `--password` options.
|
||||||
2. The CLI performs a direct LDAP bind against the configured LDAP server (same mechanism as the Central UI login).
|
2. On each request, the CLI encodes credentials as a Basic Auth header and sends them with the command.
|
||||||
3. On successful bind, the CLI queries group memberships to determine roles and permitted sites.
|
3. The server performs LDAP authentication, group lookup, and role resolution — the CLI does not communicate with LDAP directly.
|
||||||
4. Every message sent to the ManagementActor includes the `AuthenticatedUser` envelope with the user's identity, roles, and site permissions.
|
4. Credentials are not stored or cached between invocations. Each CLI invocation requires fresh credentials.
|
||||||
5. Credentials are not stored or cached between invocations. Each CLI invocation requires fresh authentication.
|
|
||||||
|
|
||||||
LDAP connection settings are read from the CLI configuration (see Configuration section).
|
|
||||||
|
|
||||||
## Connection
|
## Connection
|
||||||
|
|
||||||
The CLI uses Akka.NET ClusterClient to connect to the central cluster:
|
The CLI connects to the Central Host via HTTP:
|
||||||
|
|
||||||
- **Contact points**: One or more seed node addresses for the ClusterClientReceptionist. The CLI sends an initial contact to these addresses; the receptionist responds with the current set of cluster nodes hosting the ManagementActor.
|
- **Management URL**: The URL of a central node's web server (e.g., `http://localhost:9001`). The management API is served at `POST /management` on the same host as the Central UI.
|
||||||
- **No cluster membership**: The CLI does not join the Akka.NET cluster. It is an external process that communicates via the ClusterClient protocol.
|
- **Failover**: For HA, use a load balancer URL in front of both central nodes. The management API is stateless (Basic Auth per request), so any central node can handle any request without sticky sessions.
|
||||||
- **Failover**: If the active central node fails over, ClusterClient transparently reconnects to the new active node via the receptionist. In-flight commands may time out and need to be retried.
|
- **No Akka.NET dependency**: The CLI is a pure HTTP client with no Akka.NET runtime.
|
||||||
|
|
||||||
## Command Structure
|
## Command Structure
|
||||||
|
|
||||||
@@ -205,28 +201,17 @@ scadalink api-method delete --id <id>
|
|||||||
|
|
||||||
Configuration is resolved in the following priority order (highest wins):
|
Configuration is resolved in the following priority order (highest wins):
|
||||||
|
|
||||||
1. **Command-line options**: `--contact-points`, `--username`, `--password`, `--format`.
|
1. **Command-line options**: `--url`, `--username`, `--password`, `--format`.
|
||||||
2. **Environment variables**:
|
2. **Environment variables**:
|
||||||
- `SCADALINK_CONTACT_POINTS` — Comma-separated list of central cluster contact point addresses (e.g., `akka.tcp://ScadaLink@central1:8081,akka.tcp://ScadaLink@central2:8081`).
|
- `SCADALINK_MANAGEMENT_URL` — Management API URL (e.g., `http://central-host:5000`).
|
||||||
- `SCADALINK_LDAP_SERVER` — LDAP server address.
|
|
||||||
- `SCADALINK_LDAP_PORT` — LDAP port (default: 636 for LDAPS).
|
|
||||||
- `SCADALINK_FORMAT` — Default output format (`json` or `table`).
|
- `SCADALINK_FORMAT` — Default output format (`json` or `table`).
|
||||||
3. **Configuration file**: `~/.scadalink/config.json` — Persistent defaults for contact points, LDAP settings, and output format.
|
3. **Configuration file**: `~/.scadalink/config.json` — Persistent defaults for management URL and output format.
|
||||||
|
|
||||||
### Configuration File Format
|
### Configuration File Format
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"contactPoints": [
|
"managementUrl": "http://central-host:5000"
|
||||||
"akka.tcp://ScadaLink@central1:8081",
|
|
||||||
"akka.tcp://ScadaLink@central2:8081"
|
|
||||||
],
|
|
||||||
"ldap": {
|
|
||||||
"server": "ad.example.com",
|
|
||||||
"port": 636,
|
|
||||||
"useTls": true
|
|
||||||
},
|
|
||||||
"defaultFormat": "json"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -240,28 +225,22 @@ Configuration is resolved in the following priority order (highest wins):
|
|||||||
| Code | Meaning |
|
| Code | Meaning |
|
||||||
|------|---------|
|
|------|---------|
|
||||||
| 0 | Success |
|
| 0 | Success |
|
||||||
| 1 | General error (command failed) |
|
| 1 | General error (command failed, connection failure, or authentication failure) |
|
||||||
| 2 | Authentication failure (LDAP bind failed) |
|
| 2 | Authorization failure (insufficient role) |
|
||||||
| 3 | Authorization failure (insufficient role) |
|
|
||||||
| 4 | Connection failure (cannot reach central cluster) |
|
|
||||||
| 5 | Validation failure (e.g., template validation errors) |
|
|
||||||
|
|
||||||
## Error Handling
|
## Error Handling
|
||||||
|
|
||||||
- **Connection failure**: If the CLI cannot establish a ClusterClient connection within a timeout (default 10 seconds), it exits with code 4 and a descriptive error message.
|
- **Connection failure**: If the CLI cannot connect to the management URL (e.g., DNS failure, connection refused), it exits with code 1 and a descriptive error message.
|
||||||
- **Command timeout**: If the ManagementActor does not respond within 30 seconds (configurable), the command fails with a timeout error.
|
- **Command timeout**: If the server does not respond within 30 seconds, the command fails with a timeout error (HTTP 504).
|
||||||
- **Authentication failure**: If the LDAP bind fails, the CLI exits with code 2 before sending any commands.
|
- **Authentication failure**: If the server returns HTTP 401 (LDAP bind failed), the CLI exits with code 1.
|
||||||
- **Authorization failure**: If the ManagementActor returns an Unauthorized response, the CLI exits with code 3.
|
- **Authorization failure**: If the server returns HTTP 403, the CLI exits with code 2.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
- **Commons**: Message contracts (`Messages/Management/`), shared types.
|
- **Commons**: Message contracts (`Messages/Management/`) for command type definitions and registry.
|
||||||
- **System.CommandLine**: Command-line argument parsing.
|
- **System.CommandLine**: Command-line argument parsing.
|
||||||
- **Akka.NET (Akka.Cluster.Tools)**: ClusterClient for communication with the central cluster.
|
|
||||||
- **LDAP client library**: For direct LDAP bind authentication (same approach as Security & Auth component).
|
|
||||||
|
|
||||||
## Interactions
|
## Interactions
|
||||||
|
|
||||||
- **Management Service (ManagementActor)**: The CLI's sole runtime dependency. All operations are sent as messages to the ManagementActor via ClusterClient.
|
- **Management Service (via HTTP)**: The CLI's sole runtime dependency. All operations are sent as HTTP POST requests to the Management API endpoint on a central node, which dispatches to the ManagementActor.
|
||||||
- **Security & Auth**: The CLI performs LDAP authentication independently (same LDAP server, same bind mechanism) and passes the authenticated identity to the ManagementActor. The ManagementActor enforces authorization.
|
- **Central Host**: Serves the Management API at `POST /management`. Handles LDAP authentication, role resolution, and ManagementActor dispatch.
|
||||||
- **LDAP/Active Directory**: Direct bind for user authentication before any operation.
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
The Management Service is an Akka.NET actor on the central cluster that provides programmatic access to all administrative operations. It exposes the same capabilities as the Central UI but through an actor-based interface, enabling the CLI (and potentially other tooling) to interact with the system without going through the web UI. The ManagementActor registers with ClusterClientReceptionist so that external processes can reach it via ClusterClient without joining the cluster.
|
The Management Service is an Akka.NET actor on the central cluster that provides programmatic access to all administrative operations. It exposes the same capabilities as the Central UI but through an actor-based interface, enabling the CLI (and potentially other tooling) to interact with the system without going through the web UI. The ManagementActor registers with ClusterClientReceptionist for cross-cluster access and is also exposed via an HTTP Management API endpoint (`POST /management`) for external tools like the CLI.
|
||||||
|
|
||||||
## Location
|
## Location
|
||||||
|
|
||||||
@@ -14,6 +14,7 @@ Central cluster only. The ManagementActor runs as a plain actor on **every** cen
|
|||||||
|
|
||||||
- Provide an actor-based interface to all administrative operations available in the Central UI.
|
- Provide an actor-based interface to all administrative operations available in the Central UI.
|
||||||
- Register with Akka.NET ClusterClientReceptionist so external tools (CLI) can discover and communicate with it via ClusterClient.
|
- Register with Akka.NET ClusterClientReceptionist so external tools (CLI) can discover and communicate with it via ClusterClient.
|
||||||
|
- Expose an HTTP API endpoint (`POST /management`) that accepts JSON commands with Basic Auth, performs LDAP authentication and role resolution, and dispatches to the ManagementActor.
|
||||||
- Validate and authorize all incoming commands using the authenticated user identity carried in message envelopes.
|
- Validate and authorize all incoming commands using the authenticated user identity carried in message envelopes.
|
||||||
- Delegate to the appropriate services and repositories for each operation.
|
- Delegate to the appropriate services and repositories for each operation.
|
||||||
- Return structured response messages for all commands and queries.
|
- Return structured response messages for all commands and queries.
|
||||||
@@ -25,6 +26,18 @@ Central cluster only. The ManagementActor runs as a plain actor on **every** cen
|
|||||||
|
|
||||||
The central actor that receives and processes all management commands. Registered at a well-known actor path (`/user/management`) and with ClusterClientReceptionist.
|
The central actor that receives and processes all management commands. Registered at a well-known actor path (`/user/management`) and with ClusterClientReceptionist.
|
||||||
|
|
||||||
|
### ManagementEndpoints
|
||||||
|
|
||||||
|
Minimal API endpoint (`POST /management`) that serves as the HTTP interface to the ManagementActor. Handles Basic Auth decoding, LDAP authentication via `LdapAuthService`, role resolution via `RoleMapper`, command deserialization via `ManagementCommandRegistry`, and ManagementActor dispatch.
|
||||||
|
|
||||||
|
### ManagementActorHolder
|
||||||
|
|
||||||
|
DI-registered singleton that holds the `IActorRef` for the ManagementActor. Set during actor registration in `AkkaHostedService` and injected into the HTTP endpoint handler.
|
||||||
|
|
||||||
|
### ManagementCommandRegistry
|
||||||
|
|
||||||
|
Static registry mapping command names (e.g., `"ListSites"`) to command types (e.g., `ListSitesCommand`). Built via reflection at startup. Used by the HTTP endpoint to deserialize JSON payloads into the correct command type.
|
||||||
|
|
||||||
### Message Contracts
|
### Message Contracts
|
||||||
|
|
||||||
All request/response messages are defined in **Commons** under `Messages/Management/`. Messages follow the existing additive-only evolution rules for version compatibility. Every request message includes:
|
All request/response messages are defined in **Commons** under `Messages/Management/`. Messages follow the existing additive-only evolution rules for version compatibility. Every request message includes:
|
||||||
@@ -36,6 +49,31 @@ All request/response messages are defined in **Commons** under `Messages/Managem
|
|||||||
|
|
||||||
The ManagementActor registers itself with `ClusterClientReceptionist` at startup. This allows external processes using `ClusterClient` to send messages to the ManagementActor without joining the Akka.NET cluster as a full member. The receptionist advertises the actor under its well-known path.
|
The ManagementActor registers itself with `ClusterClientReceptionist` at startup. This allows external processes using `ClusterClient` to send messages to the ManagementActor without joining the Akka.NET cluster as a full member. The receptionist advertises the actor under its well-known path.
|
||||||
|
|
||||||
|
## HTTP Management API
|
||||||
|
|
||||||
|
The Management Service also exposes a `POST /management` endpoint on the Central Host's web server. This provides an HTTP interface to the same ManagementActor, enabling the CLI (and other HTTP clients) to interact without Akka.NET dependencies.
|
||||||
|
|
||||||
|
**Request format:**
|
||||||
|
```json
|
||||||
|
POST /management
|
||||||
|
Authorization: Basic base64(username:password)
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"command": "ListSites",
|
||||||
|
"payload": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response mapping:**
|
||||||
|
- `ManagementSuccess` → HTTP 200 with JSON body
|
||||||
|
- `ManagementError` → HTTP 400 with `{ "error": "...", "code": "..." }`
|
||||||
|
- `ManagementUnauthorized` → HTTP 403 with `{ "error": "...", "code": "UNAUTHORIZED" }`
|
||||||
|
- Authentication failure → HTTP 401
|
||||||
|
- Actor timeout → HTTP 504
|
||||||
|
|
||||||
|
The endpoint performs LDAP authentication and role resolution server-side, collapsing the CLI's previous two-step flow (ResolveRoles + actual command) into a single HTTP round-trip.
|
||||||
|
|
||||||
## Message Groups
|
## Message Groups
|
||||||
|
|
||||||
### Templates
|
### Templates
|
||||||
@@ -173,9 +211,9 @@ The ManagementActor receives the following services and repositories via DI (inj
|
|||||||
|
|
||||||
## Interactions
|
## Interactions
|
||||||
|
|
||||||
- **CLI**: The primary consumer. Connects via Akka.NET ClusterClient and sends management messages to the ManagementActor.
|
- **CLI**: The primary consumer. Connects via the HTTP Management API (`POST /management`) and sends commands as JSON with Basic Auth credentials.
|
||||||
- **Host**: Registers the ManagementActor and ClusterClientReceptionist on central nodes during startup.
|
- **Host**: Registers the ManagementActor and ClusterClientReceptionist on central nodes during startup.
|
||||||
- **Central UI**: Shares the same underlying services and repositories. The ManagementActor and Central UI are parallel interfaces to the same operations.
|
- **Central UI**: Shares the same underlying services and repositories. The ManagementActor and Central UI are parallel interfaces to the same operations.
|
||||||
- **Communication Layer**: Deployment commands and remote site queries flow through communication actors.
|
- **Communication Layer**: Deployment commands and remote site queries flow through communication actors.
|
||||||
- **Configuration Database (via IAuditService)**: All configuration changes are audited.
|
- **Configuration Database (via IAuditService)**: All configuration changes are audited.
|
||||||
- **Security & Auth**: The ManagementActor enforces authorization using user identity passed in messages. The CLI is responsible for authenticating the user and including their identity in every request.
|
- **Security & Auth**: The ManagementActor enforces authorization using user identity passed in messages. For HTTP API access, the Management endpoint authenticates the user via LDAP and resolves roles before dispatching to the ManagementActor.
|
||||||
|
|||||||
@@ -471,19 +471,19 @@ Sites log operational events locally, including:
|
|||||||
|
|
||||||
### 13.1 Management Service
|
### 13.1 Management Service
|
||||||
- The central cluster exposes a **ManagementActor** that provides programmatic access to all administrative operations — the same operations available through the Central UI.
|
- The central cluster exposes a **ManagementActor** that provides programmatic access to all administrative operations — the same operations available through the Central UI.
|
||||||
- The ManagementActor registers with Akka.NET **ClusterClientReceptionist**, allowing external tools to communicate with it via ClusterClient without joining the cluster.
|
- The ManagementActor registers with Akka.NET **ClusterClientReceptionist** for cross-cluster access, and is also exposed via an HTTP Management API endpoint (`POST /management`) with Basic Auth, LDAP authentication, and role resolution — enabling external tools like the CLI to interact without Akka.NET dependencies.
|
||||||
- The ManagementActor enforces the **same role-based authorization** as the Central UI. Every incoming message carries the authenticated user's identity and roles.
|
- The ManagementActor enforces the **same role-based authorization** as the Central UI. Every incoming message carries the authenticated user's identity and roles.
|
||||||
- All mutating operations performed through the Management Service are **audit logged** via IAuditService, identical to operations performed through the Central UI.
|
- All mutating operations performed through the Management Service are **audit logged** via IAuditService, identical to operations performed through the Central UI.
|
||||||
- The ManagementActor runs on the **active central node** and fails over with it. ClusterClient handles reconnection transparently.
|
- The ManagementActor runs on **every central node** (stateless). For HTTP API access, any central node can handle any request without sticky sessions.
|
||||||
|
|
||||||
### 13.2 CLI
|
### 13.2 CLI
|
||||||
- The system provides a standalone **command-line tool** (`scadalink`) for scripting and automating administrative operations.
|
- The system provides a standalone **command-line tool** (`scadalink`) for scripting and automating administrative operations.
|
||||||
- The CLI connects to the ManagementActor via Akka.NET **ClusterClient** — it does not join the cluster as a full member and does not use HTTP/REST.
|
- The CLI connects to the Central Host's HTTP Management API (`POST /management`) — it sends commands as JSON with HTTP Basic Auth credentials. The server handles LDAP authentication, role resolution, and ManagementActor dispatch.
|
||||||
- The CLI authenticates the user against **LDAP/AD** (direct bind, same mechanism as the Central UI) and includes the authenticated identity in every message sent to the ManagementActor.
|
- The CLI sends user credentials via HTTP Basic Auth. The server authenticates against **LDAP/AD** and resolves roles before dispatching commands to the ManagementActor.
|
||||||
- CLI commands mirror all Management Service operations: templates, instances, sites, data connections, deployments, external systems, notifications, security (API keys and role mappings), audit log queries, and health status.
|
- CLI commands mirror all Management Service operations: templates, instances, sites, data connections, deployments, external systems, notifications, security (API keys and role mappings), audit log queries, and health status.
|
||||||
- Output is **JSON by default** (machine-readable, suitable for scripting) with an optional `--format table` flag for human-readable tabular output.
|
- Output is **JSON by default** (machine-readable, suitable for scripting) with an optional `--format table` flag for human-readable tabular output.
|
||||||
- Configuration is resolved from command-line options, **environment variables** (`SCADALINK_CONTACT_POINTS`, `SCADALINK_LDAP_SERVER`, etc.), or a **configuration file** (`~/.scadalink/config.json`).
|
- Configuration is resolved from command-line options, **environment variables** (`SCADALINK_MANAGEMENT_URL`, `SCADALINK_FORMAT`), or a **configuration file** (`~/.scadalink/config.json`).
|
||||||
- The CLI is a separate executable from the Host binary — it is deployed on any Windows machine with network access to the central cluster.
|
- The CLI is a separate executable from the Host binary — it is deployed on any machine with HTTP access to a central node.
|
||||||
|
|
||||||
## 14. General Conventions
|
## 14. General Conventions
|
||||||
|
|
||||||
|
|||||||
@@ -185,11 +185,11 @@ curl -s http://localhost:9002/health/ready | python3 -m json.tool
|
|||||||
|
|
||||||
### CLI Access
|
### CLI Access
|
||||||
|
|
||||||
Connect the ScadaLink CLI to the central cluster. With OrbStack, the contact point hostname must match the container's Akka `NodeHostname` config, so use the container name directly (OrbStack resolves container names via DNS):
|
The CLI connects to the Central Host's HTTP management API. With the Docker setup, the Central UI (and management API) is available at `http://localhost:9001`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dotnet run --project src/ScadaLink.CLI -- \
|
dotnet run --project src/ScadaLink.CLI -- \
|
||||||
--contact-points akka.tcp://scadalink@scadalink-central-a:8081 \
|
--url http://localhost:9001 \
|
||||||
--username multi-role --password password \
|
--username multi-role --password password \
|
||||||
template list
|
template list
|
||||||
```
|
```
|
||||||
@@ -200,19 +200,11 @@ A recommended `~/.scadalink/config.json` for the Docker test environment:
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"contactPoints": ["akka.tcp://scadalink@scadalink-central-a:8081"],
|
"managementUrl": "http://localhost:9001"
|
||||||
"ldap": {
|
|
||||||
"server": "localhost",
|
|
||||||
"port": 3893,
|
|
||||||
"useTls": false,
|
|
||||||
"searchBase": "dc=scadalink,dc=local",
|
|
||||||
"serviceAccountDn": "cn=admin,dc=scadalink,dc=local",
|
|
||||||
"serviceAccountPassword": "password"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
With this config file in place, the contact points and LDAP settings are automatic:
|
With this config file in place, the URL is automatic:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dotnet run --project src/ScadaLink.CLI -- \
|
dotnet run --project src/ScadaLink.CLI -- \
|
||||||
|
|||||||
@@ -4,13 +4,7 @@ namespace ScadaLink.CLI;
|
|||||||
|
|
||||||
public class CliConfig
|
public class CliConfig
|
||||||
{
|
{
|
||||||
public List<string> ContactPoints { get; set; } = new();
|
public string? ManagementUrl { get; set; }
|
||||||
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 string DefaultFormat { get; set; } = "json";
|
||||||
|
|
||||||
public static CliConfig Load()
|
public static CliConfig Load()
|
||||||
@@ -28,51 +22,28 @@ public class CliConfig
|
|||||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||||
if (fileConfig != null)
|
if (fileConfig != null)
|
||||||
{
|
{
|
||||||
if (fileConfig.ContactPoints?.Count > 0) config.ContactPoints = fileConfig.ContactPoints;
|
if (!string.IsNullOrEmpty(fileConfig.ManagementUrl))
|
||||||
if (fileConfig.Ldap != null)
|
config.ManagementUrl = fileConfig.ManagementUrl;
|
||||||
{
|
if (!string.IsNullOrEmpty(fileConfig.DefaultFormat))
|
||||||
config.LdapServer = fileConfig.Ldap.Server;
|
config.DefaultFormat = fileConfig.DefaultFormat;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override from environment variables
|
// Override from environment variables
|
||||||
var envContacts = Environment.GetEnvironmentVariable("SCADALINK_CONTACT_POINTS");
|
var envUrl = Environment.GetEnvironmentVariable("SCADALINK_MANAGEMENT_URL");
|
||||||
if (!string.IsNullOrEmpty(envContacts))
|
if (!string.IsNullOrEmpty(envUrl))
|
||||||
config.ContactPoints = envContacts.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList();
|
config.ManagementUrl = envUrl;
|
||||||
|
|
||||||
var envLdap = Environment.GetEnvironmentVariable("SCADALINK_LDAP_SERVER");
|
|
||||||
if (!string.IsNullOrEmpty(envLdap)) config.LdapServer = envLdap;
|
|
||||||
|
|
||||||
var envFormat = Environment.GetEnvironmentVariable("SCADALINK_FORMAT");
|
var envFormat = Environment.GetEnvironmentVariable("SCADALINK_FORMAT");
|
||||||
if (!string.IsNullOrEmpty(envFormat)) config.DefaultFormat = envFormat;
|
if (!string.IsNullOrEmpty(envFormat))
|
||||||
|
config.DefaultFormat = envFormat;
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CliConfigFile
|
private class CliConfigFile
|
||||||
{
|
{
|
||||||
public List<string>? ContactPoints { get; set; }
|
public string? ManagementUrl { get; set; }
|
||||||
public LdapConfig? Ldap { get; set; }
|
|
||||||
public string? DefaultFormat { get; set; }
|
public string? DefaultFormat { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class LdapConfig
|
|
||||||
{
|
|
||||||
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; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
using System.Collections.Immutable;
|
|
||||||
using Akka.Actor;
|
|
||||||
using Akka.Cluster.Tools.Client;
|
|
||||||
using Akka.Configuration;
|
|
||||||
using ScadaLink.Commons.Messages.Management;
|
|
||||||
|
|
||||||
namespace ScadaLink.CLI;
|
|
||||||
|
|
||||||
public class ClusterConnection : IAsyncDisposable
|
|
||||||
{
|
|
||||||
private ActorSystem? _system;
|
|
||||||
private IActorRef? _clusterClient;
|
|
||||||
|
|
||||||
public async Task ConnectAsync(IReadOnlyList<string> contactPoints, TimeSpan timeout)
|
|
||||||
{
|
|
||||||
var seedNodes = string.Join(",", contactPoints.Select(cp => $"\"{cp}\""));
|
|
||||||
var config = ConfigurationFactory.ParseString($@"
|
|
||||||
akka {{
|
|
||||||
actor.provider = remote
|
|
||||||
remote.dot-netty.tcp {{
|
|
||||||
hostname = ""127.0.0.1""
|
|
||||||
port = 0
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
");
|
|
||||||
|
|
||||||
_system = ActorSystem.Create("scadalink-cli", config);
|
|
||||||
|
|
||||||
var initialContacts = contactPoints
|
|
||||||
.Select(cp => $"{cp}/system/receptionist")
|
|
||||||
.Select(path => ActorPath.Parse(path))
|
|
||||||
.ToImmutableHashSet();
|
|
||||||
|
|
||||||
var clientSettings = ClusterClientSettings.Create(_system)
|
|
||||||
.WithInitialContacts(initialContacts);
|
|
||||||
|
|
||||||
_clusterClient = _system.ActorOf(ClusterClient.Props(clientSettings), "cluster-client");
|
|
||||||
|
|
||||||
// Wait for connection by sending a ping
|
|
||||||
// ClusterClient doesn't have a direct "connected" signal, so we rely on the first Ask succeeding
|
|
||||||
await Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<object> AskManagementAsync(ManagementEnvelope envelope, TimeSpan timeout)
|
|
||||||
{
|
|
||||||
if (_clusterClient == null) throw new InvalidOperationException("Not connected");
|
|
||||||
|
|
||||||
var response = await _clusterClient.Ask(
|
|
||||||
new ClusterClient.Send("/user/management", envelope),
|
|
||||||
timeout);
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async ValueTask DisposeAsync()
|
|
||||||
{
|
|
||||||
if (_system != null)
|
|
||||||
{
|
|
||||||
await CoordinatedShutdown.Get(_system).Run(CoordinatedShutdown.ClrExitReason.Instance);
|
|
||||||
_system = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,31 +6,31 @@ namespace ScadaLink.CLI.Commands;
|
|||||||
|
|
||||||
public static class ApiMethodCommands
|
public static class ApiMethodCommands
|
||||||
{
|
{
|
||||||
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var command = new Command("api-method") { Description = "Manage inbound API methods" };
|
var command = new Command("api-method") { Description = "Manage inbound API methods" };
|
||||||
|
|
||||||
command.Add(BuildList(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildList(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildGet(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildGet(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildCreate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildUpdate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildUpdate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildDelete(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildList(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var cmd = new Command("list") { Description = "List all API methods" };
|
var cmd = new Command("list") { Description = "List all API methods" };
|
||||||
cmd.SetAction(async (ParseResult result) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListApiMethodsCommand());
|
result, urlOption, formatOption, usernameOption, passwordOption, new ListApiMethodsCommand());
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildGet(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "API method ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "API method ID", Required = true };
|
||||||
var cmd = new Command("get") { Description = "Get an API method by ID" };
|
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);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetApiMethodCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new GetApiMethodCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildCreate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var nameOption = new Option<string>("--name") { Description = "Method name", Required = true };
|
var nameOption = new Option<string>("--name") { Description = "Method name", Required = true };
|
||||||
var scriptOption = new Option<string>("--script") { Description = "Script code", 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 parameters = result.GetValue(parametersOption);
|
||||||
var returnDef = result.GetValue(returnDefOption);
|
var returnDef = result.GetValue(returnDefOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new CreateApiMethodCommand(name, script, timeout, parameters, returnDef));
|
new CreateApiMethodCommand(name, script, timeout, parameters, returnDef));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildUpdate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "API method ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "API method ID", Required = true };
|
||||||
var scriptOption = new Option<string>("--script") { Description = "Script code", 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 parameters = result.GetValue(parametersOption);
|
||||||
var returnDef = result.GetValue(returnDefOption);
|
var returnDef = result.GetValue(returnDefOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new UpdateApiMethodCommand(id, script, timeout, parameters, returnDef));
|
new UpdateApiMethodCommand(id, script, timeout, parameters, returnDef));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildDelete(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "API method ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "API method ID", Required = true };
|
||||||
var cmd = new Command("delete") { Description = "Delete an API method" };
|
var cmd = new Command("delete") { Description = "Delete an API method" };
|
||||||
@@ -111,7 +111,7 @@ public static class ApiMethodCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteApiMethodCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new DeleteApiMethodCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,16 +6,16 @@ namespace ScadaLink.CLI.Commands;
|
|||||||
|
|
||||||
public static class AuditLogCommands
|
public static class AuditLogCommands
|
||||||
{
|
{
|
||||||
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var command = new Command("audit-log") { Description = "Query audit logs" };
|
var command = new Command("audit-log") { Description = "Query audit logs" };
|
||||||
|
|
||||||
command.Add(BuildQuery(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildQuery(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildQuery(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildQuery(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var userOption = new Option<string?>("--user") { Description = "Filter by username" };
|
var userOption = new Option<string?>("--user") { Description = "Filter by username" };
|
||||||
var entityTypeOption = new Option<string?>("--entity-type") { Description = "Filter by entity type" };
|
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 page = result.GetValue(pageOption);
|
||||||
var pageSize = result.GetValue(pageSizeOption);
|
var pageSize = result.GetValue(pageSizeOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new QueryAuditLogCommand(user, entityType, action, from, to, page, pageSize));
|
new QueryAuditLogCommand(user, entityType, action, from, to, page, pageSize));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
|
|||||||
@@ -1,46 +1,37 @@
|
|||||||
using System.CommandLine;
|
using System.CommandLine;
|
||||||
using System.CommandLine.Parsing;
|
using System.CommandLine.Parsing;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using ScadaLink.Commons.Messages.Management;
|
using ScadaLink.Commons.Messages.Management;
|
||||||
using ScadaLink.Security;
|
|
||||||
|
|
||||||
namespace ScadaLink.CLI.Commands;
|
namespace ScadaLink.CLI.Commands;
|
||||||
|
|
||||||
internal static class CommandHelpers
|
internal static class CommandHelpers
|
||||||
{
|
{
|
||||||
internal static string NewCorrelationId() => Guid.NewGuid().ToString("N");
|
|
||||||
|
|
||||||
internal static async Task<int> ExecuteCommandAsync(
|
internal static async Task<int> ExecuteCommandAsync(
|
||||||
ParseResult result,
|
ParseResult result,
|
||||||
Option<string> contactPointsOption,
|
Option<string> urlOption,
|
||||||
Option<string> formatOption,
|
Option<string> formatOption,
|
||||||
Option<string> usernameOption,
|
Option<string> usernameOption,
|
||||||
Option<string> passwordOption,
|
Option<string> passwordOption,
|
||||||
object command)
|
object command)
|
||||||
{
|
{
|
||||||
var contactPointsRaw = result.GetValue(contactPointsOption);
|
|
||||||
var format = result.GetValue(formatOption) ?? "json";
|
var format = result.GetValue(formatOption) ?? "json";
|
||||||
|
|
||||||
var config = CliConfig.Load();
|
var config = CliConfig.Load();
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(contactPointsRaw))
|
// Resolve management URL
|
||||||
{
|
var url = result.GetValue(urlOption);
|
||||||
if (config.ContactPoints.Count > 0)
|
if (string.IsNullOrWhiteSpace(url))
|
||||||
contactPointsRaw = string.Join(",", config.ContactPoints);
|
url = config.ManagementUrl;
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(contactPointsRaw))
|
if (string.IsNullOrWhiteSpace(url))
|
||||||
{
|
{
|
||||||
OutputFormatter.WriteError("No contact points specified. Use --contact-points or set SCADALINK_CONTACT_POINTS.", "NO_CONTACT_POINTS");
|
OutputFormatter.WriteError(
|
||||||
|
"No management URL specified. Use --url, set SCADALINK_MANAGEMENT_URL, or add 'managementUrl' to ~/.scadalink/config.json.",
|
||||||
|
"NO_URL");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var contactPoints = contactPointsRaw.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
// Validate credentials
|
||||||
|
|
||||||
// Authenticate via LDAP
|
|
||||||
var username = result.GetValue(usernameOption);
|
var username = result.GetValue(usernameOption);
|
||||||
var password = result.GetValue(passwordOption);
|
var password = result.GetValue(passwordOption);
|
||||||
|
|
||||||
@@ -52,99 +43,36 @@ internal static class CommandHelpers
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authenticate against LDAP
|
// Derive command name from type
|
||||||
var securityOptions = new SecurityOptions
|
var commandName = ManagementCommandRegistry.GetCommandName(command.GetType());
|
||||||
{
|
|
||||||
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(
|
// Send via HTTP
|
||||||
Options.Create(securityOptions),
|
using var client = new ManagementHttpClient(url, username, password);
|
||||||
NullLogger<LdapAuthService>.Instance);
|
var response = await client.SendCommandAsync(commandName, command, TimeSpan.FromSeconds(30));
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
// 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, format);
|
return HandleResponse(response, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static int HandleResponse(object response, string format)
|
internal static int HandleResponse(ManagementResponse response, string format)
|
||||||
{
|
{
|
||||||
switch (response)
|
if (response.JsonData != null)
|
||||||
{
|
{
|
||||||
case ManagementSuccess success:
|
if (string.Equals(format, "table", StringComparison.OrdinalIgnoreCase))
|
||||||
if (string.Equals(format, "table", StringComparison.OrdinalIgnoreCase))
|
{
|
||||||
{
|
WriteAsTable(response.JsonData);
|
||||||
WriteAsTable(success.JsonData);
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
Console.WriteLine(response.JsonData);
|
||||||
Console.WriteLine(success.JsonData);
|
}
|
||||||
}
|
return 0;
|
||||||
return 0;
|
|
||||||
|
|
||||||
case ManagementError error:
|
|
||||||
OutputFormatter.WriteError(error.Error, error.ErrorCode);
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
case ManagementUnauthorized unauth:
|
|
||||||
OutputFormatter.WriteError(unauth.Message, "UNAUTHORIZED");
|
|
||||||
return 2;
|
|
||||||
|
|
||||||
default:
|
|
||||||
OutputFormatter.WriteError($"Unexpected response type: {response.GetType().Name}", "UNEXPECTED_RESPONSE");
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errorCode = response.ErrorCode ?? "ERROR";
|
||||||
|
var error = response.Error ?? "Unknown error";
|
||||||
|
|
||||||
|
OutputFormatter.WriteError(error, errorCode);
|
||||||
|
return response.StatusCode == 403 ? 2 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void WriteAsTable(string json)
|
private static void WriteAsTable(string json)
|
||||||
@@ -161,7 +89,6 @@ internal static class CommandHelpers
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract headers from first object's property names
|
|
||||||
var headers = items[0].ValueKind == JsonValueKind.Object
|
var headers = items[0].ValueKind == JsonValueKind.Object
|
||||||
? items[0].EnumerateObject().Select(p => p.Name).ToArray()
|
? items[0].EnumerateObject().Select(p => p.Name).ToArray()
|
||||||
: new[] { "Value" };
|
: new[] { "Value" };
|
||||||
@@ -182,7 +109,6 @@ internal static class CommandHelpers
|
|||||||
}
|
}
|
||||||
else if (root.ValueKind == JsonValueKind.Object)
|
else if (root.ValueKind == JsonValueKind.Object)
|
||||||
{
|
{
|
||||||
// Single object: render as key-value pairs
|
|
||||||
var headers = new[] { "Property", "Value" };
|
var headers = new[] { "Property", "Value" };
|
||||||
var rows = root.EnumerateObject().Select(p =>
|
var rows = root.EnumerateObject().Select(p =>
|
||||||
new[] { p.Name, p.Value.ValueKind == JsonValueKind.Null ? "" : p.Value.ToString() });
|
new[] { p.Name, p.Value.ValueKind == JsonValueKind.Null ? "" : p.Value.ToString() });
|
||||||
|
|||||||
@@ -6,22 +6,22 @@ namespace ScadaLink.CLI.Commands;
|
|||||||
|
|
||||||
public static class DataConnectionCommands
|
public static class DataConnectionCommands
|
||||||
{
|
{
|
||||||
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var command = new Command("data-connection") { Description = "Manage data connections" };
|
var command = new Command("data-connection") { Description = "Manage data connections" };
|
||||||
|
|
||||||
command.Add(BuildList(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildList(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildGet(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildGet(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildCreate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildUpdate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildUpdate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildDelete(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildAssign(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildAssign(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildUnassign(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildUnassign(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildGet(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Data connection ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Data connection ID", Required = true };
|
||||||
var cmd = new Command("get") { Description = "Get a data connection by ID" };
|
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);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetDataConnectionCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new GetDataConnectionCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildUpdate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Data connection ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Data connection ID", Required = true };
|
||||||
var nameOption = new Option<string>("--name") { Description = "Connection name", 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 protocol = result.GetValue(protocolOption)!;
|
||||||
var config = result.GetValue(configOption);
|
var config = result.GetValue(configOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new UpdateDataConnectionCommand(id, name, protocol, config));
|
new UpdateDataConnectionCommand(id, name, protocol, config));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildUnassign(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildUnassign(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--assignment-id") { Description = "Assignment ID", Required = true };
|
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" };
|
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);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new UnassignDataConnectionFromSiteCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new UnassignDataConnectionFromSiteCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildList(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var cmd = new Command("list") { Description = "List all data connections" };
|
var cmd = new Command("list") { Description = "List all data connections" };
|
||||||
cmd.SetAction(async (ParseResult result) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListDataConnectionsCommand());
|
result, urlOption, formatOption, usernameOption, passwordOption, new ListDataConnectionsCommand());
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildCreate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var nameOption = new Option<string>("--name") { Description = "Connection name", Required = true };
|
var nameOption = new Option<string>("--name") { Description = "Connection name", Required = true };
|
||||||
var protocolOption = new Option<string>("--protocol") { Description = "Protocol (e.g. OpcUa)", 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 protocol = result.GetValue(protocolOption)!;
|
||||||
var config = result.GetValue(configOption);
|
var config = result.GetValue(configOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new CreateDataConnectionCommand(name, protocol, config));
|
new CreateDataConnectionCommand(name, protocol, config));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildDelete(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Data connection ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Data connection ID", Required = true };
|
||||||
var cmd = new Command("delete") { Description = "Delete a data connection" };
|
var cmd = new Command("delete") { Description = "Delete a data connection" };
|
||||||
@@ -116,12 +116,12 @@ public static class DataConnectionCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteDataConnectionCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new DeleteDataConnectionCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildAssign(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildAssign(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var connectionIdOption = new Option<int>("--connection-id") { Description = "Data connection ID", Required = true };
|
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 };
|
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 connectionId = result.GetValue(connectionIdOption);
|
||||||
var siteId = result.GetValue(siteIdOption);
|
var siteId = result.GetValue(siteIdOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new AssignDataConnectionToSiteCommand(connectionId, siteId));
|
new AssignDataConnectionToSiteCommand(connectionId, siteId));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
|
|||||||
@@ -6,31 +6,31 @@ namespace ScadaLink.CLI.Commands;
|
|||||||
|
|
||||||
public static class DbConnectionCommands
|
public static class DbConnectionCommands
|
||||||
{
|
{
|
||||||
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var command = new Command("db-connection") { Description = "Manage database connections" };
|
var command = new Command("db-connection") { Description = "Manage database connections" };
|
||||||
|
|
||||||
command.Add(BuildList(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildList(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildGet(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildGet(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildCreate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildUpdate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildUpdate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildDelete(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildList(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var cmd = new Command("list") { Description = "List all database connections" };
|
var cmd = new Command("list") { Description = "List all database connections" };
|
||||||
cmd.SetAction(async (ParseResult result) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListDatabaseConnectionsCommand());
|
result, urlOption, formatOption, usernameOption, passwordOption, new ListDatabaseConnectionsCommand());
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildGet(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Database connection ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Database connection ID", Required = true };
|
||||||
var cmd = new Command("get") { Description = "Get a database connection by ID" };
|
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);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetDatabaseConnectionCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new GetDatabaseConnectionCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildCreate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var nameOption = new Option<string>("--name") { Description = "Connection name", Required = true };
|
var nameOption = new Option<string>("--name") { Description = "Connection name", Required = true };
|
||||||
var connStrOption = new Option<string>("--connection-string") { Description = "Connection string", 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 name = result.GetValue(nameOption)!;
|
||||||
var connStr = result.GetValue(connStrOption)!;
|
var connStr = result.GetValue(connStrOption)!;
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new CreateDatabaseConnectionDefCommand(name, connStr));
|
new CreateDatabaseConnectionDefCommand(name, connStr));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildUpdate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Database connection ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Database connection ID", Required = true };
|
||||||
var nameOption = new Option<string>("--name") { Description = "Connection name", 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 name = result.GetValue(nameOption)!;
|
||||||
var connStr = result.GetValue(connStrOption)!;
|
var connStr = result.GetValue(connStrOption)!;
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new UpdateDatabaseConnectionDefCommand(id, name, connStr));
|
new UpdateDatabaseConnectionDefCommand(id, name, connStr));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildDelete(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Database connection ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Database connection ID", Required = true };
|
||||||
var cmd = new Command("delete") { Description = "Delete a database connection" };
|
var cmd = new Command("delete") { Description = "Delete a database connection" };
|
||||||
@@ -94,7 +94,7 @@ public static class DbConnectionCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteDatabaseConnectionDefCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new DeleteDatabaseConnectionDefCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,16 +6,16 @@ namespace ScadaLink.CLI.Commands;
|
|||||||
|
|
||||||
public static class DebugCommands
|
public static class DebugCommands
|
||||||
{
|
{
|
||||||
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var command = new Command("debug") { Description = "Runtime debugging" };
|
var command = new Command("debug") { Description = "Runtime debugging" };
|
||||||
|
|
||||||
command.Add(BuildSnapshot(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildSnapshot(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildSnapshot(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildSnapshot(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
|
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" };
|
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) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new DebugSnapshotCommand(result.GetValue(idOption)));
|
new DebugSnapshotCommand(result.GetValue(idOption)));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
|
|||||||
@@ -6,18 +6,18 @@ namespace ScadaLink.CLI.Commands;
|
|||||||
|
|
||||||
public static class DeployCommands
|
public static class DeployCommands
|
||||||
{
|
{
|
||||||
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var command = new Command("deploy") { Description = "Deployment operations" };
|
var command = new Command("deploy") { Description = "Deployment operations" };
|
||||||
|
|
||||||
command.Add(BuildInstance(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildInstance(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildArtifacts(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildArtifacts(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildStatus(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildStatus(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildInstance(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildInstance(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
|
||||||
var cmd = new Command("instance") { Description = "Deploy a single instance" };
|
var cmd = new Command("instance") { Description = "Deploy a single instance" };
|
||||||
@@ -26,12 +26,12 @@ public static class DeployCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new MgmtDeployInstanceCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new MgmtDeployInstanceCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildArtifacts(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildArtifacts(Option<string> urlOption, 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 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)" };
|
var cmd = new Command("artifacts") { Description = "Deploy artifacts to site(s)" };
|
||||||
@@ -40,12 +40,12 @@ public static class DeployCommands
|
|||||||
{
|
{
|
||||||
var siteId = result.GetValue(siteIdOption);
|
var siteId = result.GetValue(siteIdOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new MgmtDeployArtifactsCommand(siteId));
|
result, urlOption, formatOption, usernameOption, passwordOption, new MgmtDeployArtifactsCommand(siteId));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildStatus(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildStatus(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var instanceIdOption = new Option<int?>("--instance-id") { Description = "Filter by instance ID" };
|
var instanceIdOption = new Option<int?>("--instance-id") { Description = "Filter by instance ID" };
|
||||||
var statusOption = new Option<string?>("--status") { Description = "Filter by status" };
|
var statusOption = new Option<string?>("--status") { Description = "Filter by status" };
|
||||||
@@ -66,7 +66,7 @@ public static class DeployCommands
|
|||||||
var page = result.GetValue(pageOption);
|
var page = result.GetValue(pageOption);
|
||||||
var pageSize = result.GetValue(pageSizeOption);
|
var pageSize = result.GetValue(pageSizeOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new QueryDeploymentsCommand(instanceId, status, page, pageSize));
|
new QueryDeploymentsCommand(instanceId, status, page, pageSize));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
|
|||||||
@@ -6,21 +6,21 @@ namespace ScadaLink.CLI.Commands;
|
|||||||
|
|
||||||
public static class ExternalSystemCommands
|
public static class ExternalSystemCommands
|
||||||
{
|
{
|
||||||
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var command = new Command("external-system") { Description = "Manage external systems" };
|
var command = new Command("external-system") { Description = "Manage external systems" };
|
||||||
|
|
||||||
command.Add(BuildList(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildList(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildGet(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildGet(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildCreate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildUpdate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildUpdate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildDelete(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildMethodGroup(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildMethodGroup(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildGet(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "External system ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "External system ID", Required = true };
|
||||||
var cmd = new Command("get") { Description = "Get an external system by ID" };
|
var cmd = new Command("get") { Description = "Get an external system by ID" };
|
||||||
@@ -29,76 +29,76 @@ public static class ExternalSystemCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetExternalSystemCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new GetExternalSystemCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildUpdate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "External system ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "External system ID", Required = true };
|
||||||
var nameOption = new Option<string>("--name") { Description = "System name", Required = true };
|
var nameOption = new Option<string>("--name") { Description = "System name", Required = true };
|
||||||
var urlOption = new Option<string>("--endpoint-url") { Description = "Endpoint URL", Required = true };
|
var endpointUrlOption = new Option<string>("--endpoint-url") { Description = "Endpoint URL", Required = true };
|
||||||
var authTypeOption = new Option<string>("--auth-type") { Description = "Auth type", Required = true };
|
var authTypeOption = new Option<string>("--auth-type") { Description = "Auth type", Required = true };
|
||||||
var authConfigOption = new Option<string?>("--auth-config") { Description = "Auth configuration JSON" };
|
var authConfigOption = new Option<string?>("--auth-config") { Description = "Auth configuration JSON" };
|
||||||
|
|
||||||
var cmd = new Command("update") { Description = "Update an external system" };
|
var cmd = new Command("update") { Description = "Update an external system" };
|
||||||
cmd.Add(idOption);
|
cmd.Add(idOption);
|
||||||
cmd.Add(nameOption);
|
cmd.Add(nameOption);
|
||||||
cmd.Add(urlOption);
|
cmd.Add(endpointUrlOption);
|
||||||
cmd.Add(authTypeOption);
|
cmd.Add(authTypeOption);
|
||||||
cmd.Add(authConfigOption);
|
cmd.Add(authConfigOption);
|
||||||
cmd.SetAction(async (ParseResult result) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
var name = result.GetValue(nameOption)!;
|
var name = result.GetValue(nameOption)!;
|
||||||
var url = result.GetValue(urlOption)!;
|
var endpointUrl = result.GetValue(endpointUrlOption)!;
|
||||||
var authType = result.GetValue(authTypeOption)!;
|
var authType = result.GetValue(authTypeOption)!;
|
||||||
var authConfig = result.GetValue(authConfigOption);
|
var authConfig = result.GetValue(authConfigOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new UpdateExternalSystemCommand(id, name, url, authType, authConfig));
|
new UpdateExternalSystemCommand(id, name, endpointUrl, authType, authConfig));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildList(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var cmd = new Command("list") { Description = "List all external systems" };
|
var cmd = new Command("list") { Description = "List all external systems" };
|
||||||
cmd.SetAction(async (ParseResult result) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListExternalSystemsCommand());
|
result, urlOption, formatOption, usernameOption, passwordOption, new ListExternalSystemsCommand());
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildCreate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var nameOption = new Option<string>("--name") { Description = "System name", Required = true };
|
var nameOption = new Option<string>("--name") { Description = "System name", Required = true };
|
||||||
var urlOption = new Option<string>("--endpoint-url") { Description = "Endpoint URL", Required = true };
|
var endpointUrlOption = new Option<string>("--endpoint-url") { Description = "Endpoint URL", Required = true };
|
||||||
var authTypeOption = new Option<string>("--auth-type") { Description = "Auth type (ApiKey, BasicAuth)", Required = true };
|
var authTypeOption = new Option<string>("--auth-type") { Description = "Auth type (ApiKey, BasicAuth)", Required = true };
|
||||||
var authConfigOption = new Option<string?>("--auth-config") { Description = "Auth configuration JSON" };
|
var authConfigOption = new Option<string?>("--auth-config") { Description = "Auth configuration JSON" };
|
||||||
|
|
||||||
var cmd = new Command("create") { Description = "Create an external system" };
|
var cmd = new Command("create") { Description = "Create an external system" };
|
||||||
cmd.Add(nameOption);
|
cmd.Add(nameOption);
|
||||||
cmd.Add(urlOption);
|
cmd.Add(endpointUrlOption);
|
||||||
cmd.Add(authTypeOption);
|
cmd.Add(authTypeOption);
|
||||||
cmd.Add(authConfigOption);
|
cmd.Add(authConfigOption);
|
||||||
cmd.SetAction(async (ParseResult result) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
var name = result.GetValue(nameOption)!;
|
var name = result.GetValue(nameOption)!;
|
||||||
var url = result.GetValue(urlOption)!;
|
var endpointUrl = result.GetValue(endpointUrlOption)!;
|
||||||
var authType = result.GetValue(authTypeOption)!;
|
var authType = result.GetValue(authTypeOption)!;
|
||||||
var authConfig = result.GetValue(authConfigOption);
|
var authConfig = result.GetValue(authConfigOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new CreateExternalSystemCommand(name, url, authType, authConfig));
|
new CreateExternalSystemCommand(name, endpointUrl, authType, authConfig));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildDelete(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "External system ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "External system ID", Required = true };
|
||||||
var cmd = new Command("delete") { Description = "Delete an external system" };
|
var cmd = new Command("delete") { Description = "Delete an external system" };
|
||||||
@@ -107,25 +107,25 @@ public static class ExternalSystemCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteExternalSystemCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new DeleteExternalSystemCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Method subcommands --
|
// -- Method subcommands --
|
||||||
|
|
||||||
private static Command BuildMethodGroup(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildMethodGroup(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var group = new Command("method") { Description = "Manage external system methods" };
|
var group = new Command("method") { Description = "Manage external system methods" };
|
||||||
group.Add(BuildMethodList(contactPointsOption, formatOption, usernameOption, passwordOption));
|
group.Add(BuildMethodList(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
group.Add(BuildMethodGet(contactPointsOption, formatOption, usernameOption, passwordOption));
|
group.Add(BuildMethodGet(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
group.Add(BuildMethodCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
group.Add(BuildMethodCreate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
group.Add(BuildMethodUpdate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
group.Add(BuildMethodUpdate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
group.Add(BuildMethodDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
|
group.Add(BuildMethodDelete(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildMethodList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildMethodList(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var sysIdOption = new Option<int>("--external-system-id") { Description = "External system ID", Required = true };
|
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" };
|
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) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new ListExternalSystemMethodsCommand(result.GetValue(sysIdOption)));
|
new ListExternalSystemMethodsCommand(result.GetValue(sysIdOption)));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildMethodGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildMethodGet(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Method ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Method ID", Required = true };
|
||||||
var cmd = new Command("get") { Description = "Get an external system method by ID" };
|
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) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new GetExternalSystemMethodCommand(result.GetValue(idOption)));
|
new GetExternalSystemMethodCommand(result.GetValue(idOption)));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildMethodCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildMethodCreate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var sysIdOption = new Option<int>("--external-system-id") { Description = "External system ID", Required = true };
|
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 };
|
var nameOption = new Option<string>("--name") { Description = "Method name", Required = true };
|
||||||
@@ -172,7 +172,7 @@ public static class ExternalSystemCommands
|
|||||||
cmd.SetAction(async (ParseResult result) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new CreateExternalSystemMethodCommand(
|
new CreateExternalSystemMethodCommand(
|
||||||
result.GetValue(sysIdOption),
|
result.GetValue(sysIdOption),
|
||||||
result.GetValue(nameOption)!,
|
result.GetValue(nameOption)!,
|
||||||
@@ -184,7 +184,7 @@ public static class ExternalSystemCommands
|
|||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildMethodUpdate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildMethodUpdate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Method ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Method ID", Required = true };
|
||||||
var nameOption = new Option<string?>("--name") { Description = "Method name" };
|
var nameOption = new Option<string?>("--name") { Description = "Method name" };
|
||||||
@@ -203,7 +203,7 @@ public static class ExternalSystemCommands
|
|||||||
cmd.SetAction(async (ParseResult result) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new UpdateExternalSystemMethodCommand(
|
new UpdateExternalSystemMethodCommand(
|
||||||
result.GetValue(idOption),
|
result.GetValue(idOption),
|
||||||
result.GetValue(nameOption),
|
result.GetValue(nameOption),
|
||||||
@@ -215,7 +215,7 @@ public static class ExternalSystemCommands
|
|||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildMethodDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildMethodDelete(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Method ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Method ID", Required = true };
|
||||||
var cmd = new Command("delete") { Description = "Delete an external system method" };
|
var cmd = new Command("delete") { Description = "Delete an external system method" };
|
||||||
@@ -223,7 +223,7 @@ public static class ExternalSystemCommands
|
|||||||
cmd.SetAction(async (ParseResult result) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new DeleteExternalSystemMethodCommand(result.GetValue(idOption)));
|
new DeleteExternalSystemMethodCommand(result.GetValue(idOption)));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
|
|||||||
@@ -6,30 +6,30 @@ namespace ScadaLink.CLI.Commands;
|
|||||||
|
|
||||||
public static class HealthCommands
|
public static class HealthCommands
|
||||||
{
|
{
|
||||||
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var command = new Command("health") { Description = "Health monitoring" };
|
var command = new Command("health") { Description = "Health monitoring" };
|
||||||
|
|
||||||
command.Add(BuildSummary(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildSummary(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildSite(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildSite(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildEventLog(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildEventLog(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildParkedMessages(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildParkedMessages(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildSummary(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildSummary(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var cmd = new Command("summary") { Description = "Get system health summary" };
|
var cmd = new Command("summary") { Description = "Get system health summary" };
|
||||||
cmd.SetAction(async (ParseResult result) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetHealthSummaryCommand());
|
result, urlOption, formatOption, usernameOption, passwordOption, new GetHealthSummaryCommand());
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildSite(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildSite(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var identifierOption = new Option<string>("--identifier") { Description = "Site identifier", Required = true };
|
var identifierOption = new Option<string>("--identifier") { Description = "Site identifier", Required = true };
|
||||||
var cmd = new Command("site") { Description = "Get health for a specific site" };
|
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)!;
|
var identifier = result.GetValue(identifierOption)!;
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetSiteHealthCommand(identifier));
|
result, urlOption, formatOption, usernameOption, passwordOption, new GetSiteHealthCommand(identifier));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildEventLog(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildEventLog(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var siteOption = new Option<string>("--site") { Description = "Site identifier", Required = true };
|
var siteOption = new Option<string>("--site") { Description = "Site identifier", Required = true };
|
||||||
var eventTypeOption = new Option<string?>("--event-type") { Description = "Filter by event type" };
|
var eventTypeOption = new Option<string?>("--event-type") { Description = "Filter by event type" };
|
||||||
@@ -70,7 +70,7 @@ public static class HealthCommands
|
|||||||
cmd.SetAction(async (ParseResult result) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new QueryEventLogsCommand(
|
new QueryEventLogsCommand(
|
||||||
result.GetValue(siteOption)!,
|
result.GetValue(siteOption)!,
|
||||||
result.GetValue(eventTypeOption),
|
result.GetValue(eventTypeOption),
|
||||||
@@ -85,7 +85,7 @@ public static class HealthCommands
|
|||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildParkedMessages(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildParkedMessages(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var siteOption = new Option<string>("--site") { Description = "Site identifier", Required = true };
|
var siteOption = new Option<string>("--site") { Description = "Site identifier", Required = true };
|
||||||
var pageOption = new Option<int>("--page") { Description = "Page number" };
|
var pageOption = new Option<int>("--page") { Description = "Page number" };
|
||||||
@@ -100,7 +100,7 @@ public static class HealthCommands
|
|||||||
cmd.SetAction(async (ParseResult result) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new QueryParkedMessagesCommand(
|
new QueryParkedMessagesCommand(
|
||||||
result.GetValue(siteOption)!,
|
result.GetValue(siteOption)!,
|
||||||
result.GetValue(pageOption),
|
result.GetValue(pageOption),
|
||||||
|
|||||||
@@ -6,26 +6,26 @@ namespace ScadaLink.CLI.Commands;
|
|||||||
|
|
||||||
public static class InstanceCommands
|
public static class InstanceCommands
|
||||||
{
|
{
|
||||||
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var command = new Command("instance") { Description = "Manage instances" };
|
var command = new Command("instance") { Description = "Manage instances" };
|
||||||
|
|
||||||
command.Add(BuildList(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildList(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildGet(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildGet(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildCreate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildSetBindings(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildSetBindings(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildSetOverrides(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildSetOverrides(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildSetArea(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildSetArea(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildDiff(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildDiff(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildDeploy(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildDeploy(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildEnable(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildEnable(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildDisable(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildDisable(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildDelete(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildGet(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
|
||||||
var cmd = new Command("get") { Description = "Get an instance by ID" };
|
var cmd = new Command("get") { Description = "Get an instance by ID" };
|
||||||
@@ -34,12 +34,12 @@ public static class InstanceCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetInstanceCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new GetInstanceCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildSetBindings(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildSetBindings(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
|
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 };
|
var bindingsOption = new Option<string>("--bindings") { Description = "JSON array of [attributeName, dataConnectionId] pairs", Required = true };
|
||||||
@@ -56,13 +56,13 @@ public static class InstanceCommands
|
|||||||
var bindings = pairs.Select(p =>
|
var bindings = pairs.Select(p =>
|
||||||
(p[0].ToString()!, int.Parse(p[1].ToString()!))).ToList();
|
(p[0].ToString()!, int.Parse(p[1].ToString()!))).ToList();
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new SetConnectionBindingsCommand(id, bindings));
|
new SetConnectionBindingsCommand(id, bindings));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildList(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var siteIdOption = new Option<int?>("--site-id") { Description = "Filter by site ID" };
|
var siteIdOption = new Option<int?>("--site-id") { Description = "Filter by site ID" };
|
||||||
var templateIdOption = new Option<int?>("--template-id") { Description = "Filter by template ID" };
|
var templateIdOption = new Option<int?>("--template-id") { Description = "Filter by template ID" };
|
||||||
@@ -78,13 +78,13 @@ public static class InstanceCommands
|
|||||||
var templateId = result.GetValue(templateIdOption);
|
var templateId = result.GetValue(templateIdOption);
|
||||||
var search = result.GetValue(searchOption);
|
var search = result.GetValue(searchOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new ListInstancesCommand(siteId, templateId, search));
|
new ListInstancesCommand(siteId, templateId, search));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildCreate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var nameOption = new Option<string>("--name") { Description = "Unique instance name", Required = true };
|
var nameOption = new Option<string>("--name") { Description = "Unique instance name", Required = true };
|
||||||
var templateIdOption = new Option<int>("--template-id") { Description = "Template ID", Required = true };
|
var templateIdOption = new Option<int>("--template-id") { Description = "Template ID", Required = true };
|
||||||
@@ -103,13 +103,13 @@ public static class InstanceCommands
|
|||||||
var siteId = result.GetValue(siteIdOption);
|
var siteId = result.GetValue(siteIdOption);
|
||||||
var areaId = result.GetValue(areaIdOption);
|
var areaId = result.GetValue(areaIdOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new CreateInstanceCommand(name, templateId, siteId, areaId));
|
new CreateInstanceCommand(name, templateId, siteId, areaId));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildDeploy(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildDeploy(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
|
||||||
var cmd = new Command("deploy") { Description = "Deploy an instance" };
|
var cmd = new Command("deploy") { Description = "Deploy an instance" };
|
||||||
@@ -118,12 +118,12 @@ public static class InstanceCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new MgmtDeployInstanceCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new MgmtDeployInstanceCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildEnable(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildEnable(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
|
||||||
var cmd = new Command("enable") { Description = "Enable an instance" };
|
var cmd = new Command("enable") { Description = "Enable an instance" };
|
||||||
@@ -132,12 +132,12 @@ public static class InstanceCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new MgmtEnableInstanceCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new MgmtEnableInstanceCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildDisable(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildDisable(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
|
||||||
var cmd = new Command("disable") { Description = "Disable an instance" };
|
var cmd = new Command("disable") { Description = "Disable an instance" };
|
||||||
@@ -146,12 +146,12 @@ public static class InstanceCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new MgmtDisableInstanceCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new MgmtDisableInstanceCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildDelete(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
|
||||||
var cmd = new Command("delete") { Description = "Delete an instance" };
|
var cmd = new Command("delete") { Description = "Delete an instance" };
|
||||||
@@ -160,12 +160,12 @@ public static class InstanceCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new MgmtDeleteInstanceCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new MgmtDeleteInstanceCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildSetOverrides(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildSetOverrides(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
|
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 overridesOption = new Option<string>("--overrides") { Description = "JSON object of attribute name -> value pairs, e.g. {\"Speed\": \"100\", \"Mode\": null}", Required = true };
|
||||||
@@ -180,13 +180,13 @@ public static class InstanceCommands
|
|||||||
var overrides = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string?>>(overridesJson)
|
var overrides = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string?>>(overridesJson)
|
||||||
?? throw new InvalidOperationException("Invalid overrides JSON");
|
?? throw new InvalidOperationException("Invalid overrides JSON");
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new SetInstanceOverridesCommand(id, overrides));
|
new SetInstanceOverridesCommand(id, overrides));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildSetArea(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildSetArea(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
|
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 areaIdOption = new Option<int?>("--area-id") { Description = "Area ID (omit to clear area assignment)" };
|
||||||
@@ -199,13 +199,13 @@ public static class InstanceCommands
|
|||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
var areaId = result.GetValue(areaIdOption);
|
var areaId = result.GetValue(areaIdOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new SetInstanceAreaCommand(id, areaId));
|
new SetInstanceAreaCommand(id, areaId));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildDiff(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildDiff(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Instance ID", Required = true };
|
||||||
|
|
||||||
@@ -215,7 +215,7 @@ public static class InstanceCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new GetDeploymentDiffCommand(id));
|
new GetDeploymentDiffCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
|
|||||||
@@ -6,21 +6,21 @@ namespace ScadaLink.CLI.Commands;
|
|||||||
|
|
||||||
public static class NotificationCommands
|
public static class NotificationCommands
|
||||||
{
|
{
|
||||||
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var command = new Command("notification") { Description = "Manage notification lists" };
|
var command = new Command("notification") { Description = "Manage notification lists" };
|
||||||
|
|
||||||
command.Add(BuildList(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildList(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildGet(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildGet(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildCreate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildUpdate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildUpdate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildDelete(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildSmtp(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildSmtp(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildGet(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Notification list ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Notification list ID", Required = true };
|
||||||
var cmd = new Command("get") { Description = "Get a notification list by ID" };
|
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);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetNotificationListCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new GetNotificationListCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildUpdate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Notification list ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Notification list ID", Required = true };
|
||||||
var nameOption = new Option<string>("--name") { Description = "List name", 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 emailsRaw = result.GetValue(emailsOption)!;
|
||||||
var emails = emailsRaw.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList();
|
var emails = emailsRaw.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList();
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new UpdateNotificationListCommand(id, name, emails));
|
new UpdateNotificationListCommand(id, name, emails));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildSmtp(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildSmtp(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var group = new Command("smtp") { Description = "Manage SMTP configuration" };
|
var group = new Command("smtp") { Description = "Manage SMTP configuration" };
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ public static class NotificationCommands
|
|||||||
listCmd.SetAction(async (ParseResult result) =>
|
listCmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListSmtpConfigsCommand());
|
result, urlOption, formatOption, usernameOption, passwordOption, new ListSmtpConfigsCommand());
|
||||||
});
|
});
|
||||||
group.Add(listCmd);
|
group.Add(listCmd);
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ public static class NotificationCommands
|
|||||||
var authMode = result.GetValue(authModeOption)!;
|
var authMode = result.GetValue(authModeOption)!;
|
||||||
var from = result.GetValue(fromOption)!;
|
var from = result.GetValue(fromOption)!;
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new UpdateSmtpConfigCommand(id, server, port, authMode, from));
|
new UpdateSmtpConfigCommand(id, server, port, authMode, from));
|
||||||
});
|
});
|
||||||
group.Add(updateCmd);
|
group.Add(updateCmd);
|
||||||
@@ -96,18 +96,18 @@ public static class NotificationCommands
|
|||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildList(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var cmd = new Command("list") { Description = "List all notification lists" };
|
var cmd = new Command("list") { Description = "List all notification lists" };
|
||||||
cmd.SetAction(async (ParseResult result) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListNotificationListsCommand());
|
result, urlOption, formatOption, usernameOption, passwordOption, new ListNotificationListsCommand());
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildCreate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var nameOption = new Option<string>("--name") { Description = "Notification list name", Required = true };
|
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 };
|
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 emailsRaw = result.GetValue(emailsOption)!;
|
||||||
var emails = emailsRaw.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList();
|
var emails = emailsRaw.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList();
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new CreateNotificationListCommand(name, emails));
|
new CreateNotificationListCommand(name, emails));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildDelete(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Notification list ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Notification list ID", Required = true };
|
||||||
var cmd = new Command("delete") { Description = "Delete a notification list" };
|
var cmd = new Command("delete") { Description = "Delete a notification list" };
|
||||||
@@ -136,7 +136,7 @@ public static class NotificationCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteNotificationListCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new DeleteNotificationListCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,18 +6,18 @@ namespace ScadaLink.CLI.Commands;
|
|||||||
|
|
||||||
public static class SecurityCommands
|
public static class SecurityCommands
|
||||||
{
|
{
|
||||||
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var command = new Command("security") { Description = "Manage security settings" };
|
var command = new Command("security") { Description = "Manage security settings" };
|
||||||
|
|
||||||
command.Add(BuildApiKey(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildApiKey(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildRoleMapping(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildRoleMapping(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildScopeRule(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildScopeRule(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildApiKey(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildApiKey(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var group = new Command("api-key") { Description = "Manage API keys" };
|
var group = new Command("api-key") { Description = "Manage API keys" };
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ public static class SecurityCommands
|
|||||||
listCmd.SetAction(async (ParseResult result) =>
|
listCmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListApiKeysCommand());
|
result, urlOption, formatOption, usernameOption, passwordOption, new ListApiKeysCommand());
|
||||||
});
|
});
|
||||||
group.Add(listCmd);
|
group.Add(listCmd);
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ public static class SecurityCommands
|
|||||||
{
|
{
|
||||||
var name = result.GetValue(nameOption)!;
|
var name = result.GetValue(nameOption)!;
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new CreateApiKeyCommand(name));
|
result, urlOption, formatOption, usernameOption, passwordOption, new CreateApiKeyCommand(name));
|
||||||
});
|
});
|
||||||
group.Add(createCmd);
|
group.Add(createCmd);
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ public static class SecurityCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteApiKeyCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new DeleteApiKeyCommand(id));
|
||||||
});
|
});
|
||||||
group.Add(deleteCmd);
|
group.Add(deleteCmd);
|
||||||
|
|
||||||
@@ -61,14 +61,14 @@ public static class SecurityCommands
|
|||||||
var id = result.GetValue(updateIdOption);
|
var id = result.GetValue(updateIdOption);
|
||||||
var enabled = result.GetValue(enabledOption);
|
var enabled = result.GetValue(enabledOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new UpdateApiKeyCommand(id, enabled));
|
result, urlOption, formatOption, usernameOption, passwordOption, new UpdateApiKeyCommand(id, enabled));
|
||||||
});
|
});
|
||||||
group.Add(updateCmd);
|
group.Add(updateCmd);
|
||||||
|
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildRoleMapping(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildRoleMapping(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var group = new Command("role-mapping") { Description = "Manage LDAP role mappings" };
|
var group = new Command("role-mapping") { Description = "Manage LDAP role mappings" };
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ public static class SecurityCommands
|
|||||||
listCmd.SetAction(async (ParseResult result) =>
|
listCmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListRoleMappingsCommand());
|
result, urlOption, formatOption, usernameOption, passwordOption, new ListRoleMappingsCommand());
|
||||||
});
|
});
|
||||||
group.Add(listCmd);
|
group.Add(listCmd);
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ public static class SecurityCommands
|
|||||||
var ldapGroup = result.GetValue(ldapGroupOption)!;
|
var ldapGroup = result.GetValue(ldapGroupOption)!;
|
||||||
var role = result.GetValue(roleOption)!;
|
var role = result.GetValue(roleOption)!;
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new CreateRoleMappingCommand(ldapGroup, role));
|
new CreateRoleMappingCommand(ldapGroup, role));
|
||||||
});
|
});
|
||||||
group.Add(createCmd);
|
group.Add(createCmd);
|
||||||
@@ -102,7 +102,7 @@ public static class SecurityCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteRoleMappingCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new DeleteRoleMappingCommand(id));
|
||||||
});
|
});
|
||||||
group.Add(deleteCmd);
|
group.Add(deleteCmd);
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ public static class SecurityCommands
|
|||||||
var ldapGroup = result.GetValue(updateLdapGroupOption)!;
|
var ldapGroup = result.GetValue(updateLdapGroupOption)!;
|
||||||
var role = result.GetValue(updateRoleOption)!;
|
var role = result.GetValue(updateRoleOption)!;
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new UpdateRoleMappingCommand(id, ldapGroup, role));
|
new UpdateRoleMappingCommand(id, ldapGroup, role));
|
||||||
});
|
});
|
||||||
group.Add(updateCmd);
|
group.Add(updateCmd);
|
||||||
@@ -127,7 +127,7 @@ public static class SecurityCommands
|
|||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildScopeRule(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildScopeRule(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var group = new Command("scope-rule") { Description = "Manage LDAP scope rules" };
|
var group = new Command("scope-rule") { Description = "Manage LDAP scope rules" };
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ public static class SecurityCommands
|
|||||||
{
|
{
|
||||||
var mappingId = result.GetValue(mappingIdOption);
|
var mappingId = result.GetValue(mappingIdOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListScopeRulesCommand(mappingId));
|
result, urlOption, formatOption, usernameOption, passwordOption, new ListScopeRulesCommand(mappingId));
|
||||||
});
|
});
|
||||||
group.Add(listCmd);
|
group.Add(listCmd);
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ public static class SecurityCommands
|
|||||||
var mappingId = result.GetValue(addMappingIdOption);
|
var mappingId = result.GetValue(addMappingIdOption);
|
||||||
var siteId = result.GetValue(siteIdOption);
|
var siteId = result.GetValue(siteIdOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new AddScopeRuleCommand(mappingId, siteId));
|
result, urlOption, formatOption, usernameOption, passwordOption, new AddScopeRuleCommand(mappingId, siteId));
|
||||||
});
|
});
|
||||||
group.Add(addCmd);
|
group.Add(addCmd);
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ public static class SecurityCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(deleteIdOption);
|
var id = result.GetValue(deleteIdOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteScopeRuleCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new DeleteScopeRuleCommand(id));
|
||||||
});
|
});
|
||||||
group.Add(deleteCmd);
|
group.Add(deleteCmd);
|
||||||
|
|
||||||
|
|||||||
@@ -6,31 +6,31 @@ namespace ScadaLink.CLI.Commands;
|
|||||||
|
|
||||||
public static class SharedScriptCommands
|
public static class SharedScriptCommands
|
||||||
{
|
{
|
||||||
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var command = new Command("shared-script") { Description = "Manage shared scripts" };
|
var command = new Command("shared-script") { Description = "Manage shared scripts" };
|
||||||
|
|
||||||
command.Add(BuildList(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildList(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildGet(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildGet(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildCreate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildUpdate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildUpdate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildDelete(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildList(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var cmd = new Command("list") { Description = "List all shared scripts" };
|
var cmd = new Command("list") { Description = "List all shared scripts" };
|
||||||
cmd.SetAction(async (ParseResult result) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListSharedScriptsCommand());
|
result, urlOption, formatOption, usernameOption, passwordOption, new ListSharedScriptsCommand());
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildGet(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Shared script ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Shared script ID", Required = true };
|
||||||
var cmd = new Command("get") { Description = "Get a shared script by ID" };
|
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);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetSharedScriptCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new GetSharedScriptCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildCreate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var nameOption = new Option<string>("--name") { Description = "Script name", Required = true };
|
var nameOption = new Option<string>("--name") { Description = "Script name", Required = true };
|
||||||
var codeOption = new Option<string>("--code") { Description = "Script code", Required = true };
|
var codeOption = new Option<string>("--code") { Description = "Script code", Required = true };
|
||||||
@@ -63,13 +63,13 @@ public static class SharedScriptCommands
|
|||||||
var parameters = result.GetValue(parametersOption);
|
var parameters = result.GetValue(parametersOption);
|
||||||
var returnDef = result.GetValue(returnDefOption);
|
var returnDef = result.GetValue(returnDefOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new CreateSharedScriptCommand(name, code, parameters, returnDef));
|
new CreateSharedScriptCommand(name, code, parameters, returnDef));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildUpdate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Shared script ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Shared script ID", Required = true };
|
||||||
var nameOption = new Option<string>("--name") { Description = "Script name", 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 parameters = result.GetValue(parametersOption);
|
||||||
var returnDef = result.GetValue(returnDefOption);
|
var returnDef = result.GetValue(returnDefOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new UpdateSharedScriptCommand(id, name, code, parameters, returnDef));
|
new UpdateSharedScriptCommand(id, name, code, parameters, returnDef));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildDelete(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Shared script ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Shared script ID", Required = true };
|
||||||
var cmd = new Command("delete") { Description = "Delete a shared script" };
|
var cmd = new Command("delete") { Description = "Delete a shared script" };
|
||||||
@@ -106,7 +106,7 @@ public static class SharedScriptCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteSharedScriptCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new DeleteSharedScriptCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,22 +6,22 @@ namespace ScadaLink.CLI.Commands;
|
|||||||
|
|
||||||
public static class SiteCommands
|
public static class SiteCommands
|
||||||
{
|
{
|
||||||
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var command = new Command("site") { Description = "Manage sites" };
|
var command = new Command("site") { Description = "Manage sites" };
|
||||||
|
|
||||||
command.Add(BuildList(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildList(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildGet(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildGet(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildCreate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildUpdate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildUpdate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildDelete(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildDeployArtifacts(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildDeployArtifacts(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildArea(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildArea(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildGet(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Site ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Site ID", Required = true };
|
||||||
var cmd = new Command("get") { Description = "Get a site by ID" };
|
var cmd = new Command("get") { Description = "Get a site by ID" };
|
||||||
@@ -30,23 +30,23 @@ public static class SiteCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetSiteCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new GetSiteCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildList(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var cmd = new Command("list") { Description = "List all sites" };
|
var cmd = new Command("list") { Description = "List all sites" };
|
||||||
cmd.SetAction(async (ParseResult result) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListSitesCommand());
|
result, urlOption, formatOption, usernameOption, passwordOption, new ListSitesCommand());
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildCreate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var nameOption = new Option<string>("--name") { Description = "Site name", Required = true };
|
var nameOption = new Option<string>("--name") { Description = "Site name", Required = true };
|
||||||
var identifierOption = new Option<string>("--identifier") { Description = "Site identifier", 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 nodeA = result.GetValue(nodeAOption);
|
||||||
var nodeB = result.GetValue(nodeBOption);
|
var nodeB = result.GetValue(nodeBOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new CreateSiteCommand(name, identifier, desc, nodeA, nodeB));
|
new CreateSiteCommand(name, identifier, desc, nodeA, nodeB));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildUpdate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Site ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Site ID", Required = true };
|
||||||
var nameOption = new Option<string>("--name") { Description = "Site name", 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 nodeA = result.GetValue(nodeAOption);
|
||||||
var nodeB = result.GetValue(nodeBOption);
|
var nodeB = result.GetValue(nodeBOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new UpdateSiteCommand(id, name, desc, nodeA, nodeB));
|
new UpdateSiteCommand(id, name, desc, nodeA, nodeB));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildDelete(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Site ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Site ID", Required = true };
|
||||||
var cmd = new Command("delete") { Description = "Delete a site" };
|
var cmd = new Command("delete") { Description = "Delete a site" };
|
||||||
@@ -111,12 +111,12 @@ public static class SiteCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteSiteCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new DeleteSiteCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildArea(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildArea(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var group = new Command("area") { Description = "Manage areas" };
|
var group = new Command("area") { Description = "Manage areas" };
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ public static class SiteCommands
|
|||||||
{
|
{
|
||||||
var siteId = result.GetValue(siteIdOption);
|
var siteId = result.GetValue(siteIdOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListAreasCommand(siteId));
|
result, urlOption, formatOption, usernameOption, passwordOption, new ListAreasCommand(siteId));
|
||||||
});
|
});
|
||||||
group.Add(listCmd);
|
group.Add(listCmd);
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ public static class SiteCommands
|
|||||||
var name = result.GetValue(nameOption)!;
|
var name = result.GetValue(nameOption)!;
|
||||||
var parentId = result.GetValue(parentOption);
|
var parentId = result.GetValue(parentOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new CreateAreaCommand(siteId, name, parentId));
|
new CreateAreaCommand(siteId, name, parentId));
|
||||||
});
|
});
|
||||||
group.Add(createCmd);
|
group.Add(createCmd);
|
||||||
@@ -159,7 +159,7 @@ public static class SiteCommands
|
|||||||
var id = result.GetValue(updateIdOption);
|
var id = result.GetValue(updateIdOption);
|
||||||
var name = result.GetValue(updateNameOption)!;
|
var name = result.GetValue(updateNameOption)!;
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new UpdateAreaCommand(id, name));
|
result, urlOption, formatOption, usernameOption, passwordOption, new UpdateAreaCommand(id, name));
|
||||||
});
|
});
|
||||||
group.Add(updateCmd);
|
group.Add(updateCmd);
|
||||||
|
|
||||||
@@ -170,14 +170,14 @@ public static class SiteCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(deleteIdOption);
|
var id = result.GetValue(deleteIdOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteAreaCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new DeleteAreaCommand(id));
|
||||||
});
|
});
|
||||||
group.Add(deleteCmd);
|
group.Add(deleteCmd);
|
||||||
|
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildDeployArtifacts(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildDeployArtifacts(Option<string> urlOption, 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 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)" };
|
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);
|
var siteId = result.GetValue(siteIdOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new MgmtDeployArtifactsCommand(siteId));
|
result, urlOption, formatOption, usernameOption, passwordOption, new MgmtDeployArtifactsCommand(siteId));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,36 +6,36 @@ namespace ScadaLink.CLI.Commands;
|
|||||||
|
|
||||||
public static class TemplateCommands
|
public static class TemplateCommands
|
||||||
{
|
{
|
||||||
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var command = new Command("template") { Description = "Manage templates" };
|
var command = new Command("template") { Description = "Manage templates" };
|
||||||
|
|
||||||
command.Add(BuildList(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildList(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildGet(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildGet(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildCreate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildCreate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildUpdate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildUpdate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildValidate(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildValidate(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildDelete(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildDelete(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildAttribute(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildAttribute(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildAlarm(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildAlarm(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildScript(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildScript(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
command.Add(BuildComposition(contactPointsOption, formatOption, usernameOption, passwordOption));
|
command.Add(BuildComposition(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildList(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildList(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var cmd = new Command("list") { Description = "List all templates" };
|
var cmd = new Command("list") { Description = "List all templates" };
|
||||||
cmd.SetAction(async (ParseResult result) =>
|
cmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ListTemplatesCommand());
|
result, urlOption, formatOption, usernameOption, passwordOption, new ListTemplatesCommand());
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildGet(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildGet(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Template ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Template ID", Required = true };
|
||||||
var cmd = new Command("get") { Description = "Get a template by ID" };
|
var cmd = new Command("get") { Description = "Get a template by ID" };
|
||||||
@@ -44,12 +44,12 @@ public static class TemplateCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new GetTemplateCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new GetTemplateCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildCreate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildCreate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var nameOption = new Option<string>("--name") { Description = "Template name", Required = true };
|
var nameOption = new Option<string>("--name") { Description = "Template name", Required = true };
|
||||||
var descOption = new Option<string?>("--description") { Description = "Template description" };
|
var descOption = new Option<string?>("--description") { Description = "Template description" };
|
||||||
@@ -65,13 +65,13 @@ public static class TemplateCommands
|
|||||||
var desc = result.GetValue(descOption);
|
var desc = result.GetValue(descOption);
|
||||||
var parentId = result.GetValue(parentOption);
|
var parentId = result.GetValue(parentOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new CreateTemplateCommand(name, desc, parentId));
|
new CreateTemplateCommand(name, desc, parentId));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildUpdate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildUpdate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Template ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Template ID", Required = true };
|
||||||
var nameOption = new Option<string>("--name") { Description = "Template name", 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 desc = result.GetValue(descOption);
|
||||||
var parentId = result.GetValue(parentOption);
|
var parentId = result.GetValue(parentOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new UpdateTemplateCommand(id, name, desc, parentId));
|
new UpdateTemplateCommand(id, name, desc, parentId));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildValidate(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildValidate(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Template ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Template ID", Required = true };
|
||||||
var cmd = new Command("validate") { Description = "Validate a template" };
|
var cmd = new Command("validate") { Description = "Validate a template" };
|
||||||
@@ -105,12 +105,12 @@ public static class TemplateCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new ValidateTemplateCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new ValidateTemplateCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildDelete(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildDelete(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var idOption = new Option<int>("--id") { Description = "Template ID", Required = true };
|
var idOption = new Option<int>("--id") { Description = "Template ID", Required = true };
|
||||||
var cmd = new Command("delete") { Description = "Delete a template" };
|
var cmd = new Command("delete") { Description = "Delete a template" };
|
||||||
@@ -119,12 +119,12 @@ public static class TemplateCommands
|
|||||||
{
|
{
|
||||||
var id = result.GetValue(idOption);
|
var id = result.GetValue(idOption);
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption, new DeleteTemplateCommand(id));
|
result, urlOption, formatOption, usernameOption, passwordOption, new DeleteTemplateCommand(id));
|
||||||
});
|
});
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildAttribute(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildAttribute(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var group = new Command("attribute") { Description = "Manage template attributes" };
|
var group = new Command("attribute") { Description = "Manage template attributes" };
|
||||||
|
|
||||||
@@ -148,7 +148,7 @@ public static class TemplateCommands
|
|||||||
addCmd.SetAction(async (ParseResult result) =>
|
addCmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new AddTemplateAttributeCommand(
|
new AddTemplateAttributeCommand(
|
||||||
result.GetValue(templateIdOption),
|
result.GetValue(templateIdOption),
|
||||||
result.GetValue(nameOption)!,
|
result.GetValue(nameOption)!,
|
||||||
@@ -180,7 +180,7 @@ public static class TemplateCommands
|
|||||||
updateCmd.SetAction(async (ParseResult result) =>
|
updateCmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new UpdateTemplateAttributeCommand(
|
new UpdateTemplateAttributeCommand(
|
||||||
result.GetValue(updateIdOption),
|
result.GetValue(updateIdOption),
|
||||||
result.GetValue(updateNameOption)!,
|
result.GetValue(updateNameOption)!,
|
||||||
@@ -198,7 +198,7 @@ public static class TemplateCommands
|
|||||||
deleteCmd.SetAction(async (ParseResult result) =>
|
deleteCmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new DeleteTemplateAttributeCommand(result.GetValue(deleteIdOption)));
|
new DeleteTemplateAttributeCommand(result.GetValue(deleteIdOption)));
|
||||||
});
|
});
|
||||||
group.Add(deleteCmd);
|
group.Add(deleteCmd);
|
||||||
@@ -206,7 +206,7 @@ public static class TemplateCommands
|
|||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildAlarm(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildAlarm(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var group = new Command("alarm") { Description = "Manage template alarms" };
|
var group = new Command("alarm") { Description = "Manage template alarms" };
|
||||||
|
|
||||||
@@ -230,7 +230,7 @@ public static class TemplateCommands
|
|||||||
addCmd.SetAction(async (ParseResult result) =>
|
addCmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new AddTemplateAlarmCommand(
|
new AddTemplateAlarmCommand(
|
||||||
result.GetValue(templateIdOption),
|
result.GetValue(templateIdOption),
|
||||||
result.GetValue(nameOption)!,
|
result.GetValue(nameOption)!,
|
||||||
@@ -262,7 +262,7 @@ public static class TemplateCommands
|
|||||||
updateCmd.SetAction(async (ParseResult result) =>
|
updateCmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new UpdateTemplateAlarmCommand(
|
new UpdateTemplateAlarmCommand(
|
||||||
result.GetValue(updateIdOption),
|
result.GetValue(updateIdOption),
|
||||||
result.GetValue(updateNameOption)!,
|
result.GetValue(updateNameOption)!,
|
||||||
@@ -280,7 +280,7 @@ public static class TemplateCommands
|
|||||||
deleteCmd.SetAction(async (ParseResult result) =>
|
deleteCmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new DeleteTemplateAlarmCommand(result.GetValue(deleteIdOption)));
|
new DeleteTemplateAlarmCommand(result.GetValue(deleteIdOption)));
|
||||||
});
|
});
|
||||||
group.Add(deleteCmd);
|
group.Add(deleteCmd);
|
||||||
@@ -288,7 +288,7 @@ public static class TemplateCommands
|
|||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildScript(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildScript(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var group = new Command("script") { Description = "Manage template scripts" };
|
var group = new Command("script") { Description = "Manage template scripts" };
|
||||||
|
|
||||||
@@ -315,7 +315,7 @@ public static class TemplateCommands
|
|||||||
addCmd.SetAction(async (ParseResult result) =>
|
addCmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new AddTemplateScriptCommand(
|
new AddTemplateScriptCommand(
|
||||||
result.GetValue(templateIdOption),
|
result.GetValue(templateIdOption),
|
||||||
result.GetValue(nameOption)!,
|
result.GetValue(nameOption)!,
|
||||||
@@ -351,7 +351,7 @@ public static class TemplateCommands
|
|||||||
updateCmd.SetAction(async (ParseResult result) =>
|
updateCmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new UpdateTemplateScriptCommand(
|
new UpdateTemplateScriptCommand(
|
||||||
result.GetValue(updateIdOption),
|
result.GetValue(updateIdOption),
|
||||||
result.GetValue(updateNameOption)!,
|
result.GetValue(updateNameOption)!,
|
||||||
@@ -370,7 +370,7 @@ public static class TemplateCommands
|
|||||||
deleteCmd.SetAction(async (ParseResult result) =>
|
deleteCmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new DeleteTemplateScriptCommand(result.GetValue(deleteIdOption)));
|
new DeleteTemplateScriptCommand(result.GetValue(deleteIdOption)));
|
||||||
});
|
});
|
||||||
group.Add(deleteCmd);
|
group.Add(deleteCmd);
|
||||||
@@ -378,7 +378,7 @@ public static class TemplateCommands
|
|||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command BuildComposition(Option<string> contactPointsOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
private static Command BuildComposition(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||||
{
|
{
|
||||||
var group = new Command("composition") { Description = "Manage template compositions" };
|
var group = new Command("composition") { Description = "Manage template compositions" };
|
||||||
|
|
||||||
@@ -393,7 +393,7 @@ public static class TemplateCommands
|
|||||||
addCmd.SetAction(async (ParseResult result) =>
|
addCmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new AddTemplateCompositionCommand(
|
new AddTemplateCompositionCommand(
|
||||||
result.GetValue(templateIdOption),
|
result.GetValue(templateIdOption),
|
||||||
result.GetValue(instanceNameOption)!,
|
result.GetValue(instanceNameOption)!,
|
||||||
@@ -407,7 +407,7 @@ public static class TemplateCommands
|
|||||||
deleteCmd.SetAction(async (ParseResult result) =>
|
deleteCmd.SetAction(async (ParseResult result) =>
|
||||||
{
|
{
|
||||||
return await CommandHelpers.ExecuteCommandAsync(
|
return await CommandHelpers.ExecuteCommandAsync(
|
||||||
result, contactPointsOption, formatOption, usernameOption, passwordOption,
|
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||||
new DeleteTemplateCompositionCommand(result.GetValue(deleteIdOption)));
|
new DeleteTemplateCompositionCommand(result.GetValue(deleteIdOption)));
|
||||||
});
|
});
|
||||||
group.Add(deleteCmd);
|
group.Add(deleteCmd);
|
||||||
|
|||||||
69
src/ScadaLink.CLI/ManagementHttpClient.cs
Normal file
69
src/ScadaLink.CLI/ManagementHttpClient.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace ScadaLink.CLI;
|
||||||
|
|
||||||
|
public class ManagementHttpClient : IDisposable
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
|
||||||
|
public ManagementHttpClient(string baseUrl, string username, string password)
|
||||||
|
{
|
||||||
|
_httpClient = new HttpClient { BaseAddress = new Uri(baseUrl.TrimEnd('/') + "/") };
|
||||||
|
var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"));
|
||||||
|
_httpClient.DefaultRequestHeaders.Authorization =
|
||||||
|
new AuthenticationHeaderValue("Basic", credentials);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ManagementResponse> SendCommandAsync(string commandName, object payload, TimeSpan timeout)
|
||||||
|
{
|
||||||
|
using var cts = new CancellationTokenSource(timeout);
|
||||||
|
|
||||||
|
var body = JsonSerializer.Serialize(new { command = commandName, payload },
|
||||||
|
new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
|
||||||
|
|
||||||
|
var content = new StringContent(body, Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
HttpResponseMessage httpResponse;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
httpResponse = await _httpClient.PostAsync("management", content, cts.Token);
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
return new ManagementResponse(504, null, "Request timed out.", "TIMEOUT");
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
return new ManagementResponse(0, null, $"Connection failed: {ex.Message}", "CONNECTION_FAILED");
|
||||||
|
}
|
||||||
|
|
||||||
|
var responseBody = await httpResponse.Content.ReadAsStringAsync(cts.Token);
|
||||||
|
|
||||||
|
if (httpResponse.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return new ManagementResponse((int)httpResponse.StatusCode, responseBody, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse error response
|
||||||
|
string? error = null;
|
||||||
|
string? code = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var doc = JsonDocument.Parse(responseBody);
|
||||||
|
error = doc.RootElement.TryGetProperty("error", out var e) ? e.GetString() : responseBody;
|
||||||
|
code = doc.RootElement.TryGetProperty("code", out var c) ? c.GetString() : null;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
error = responseBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ManagementResponse((int)httpResponse.StatusCode, null, error, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => _httpClient.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public record ManagementResponse(int StatusCode, string? JsonData, string? Error, string? ErrorCode);
|
||||||
@@ -4,32 +4,32 @@ using ScadaLink.CLI.Commands;
|
|||||||
|
|
||||||
var rootCommand = new RootCommand("ScadaLink CLI — manage the ScadaLink SCADA system");
|
var rootCommand = new RootCommand("ScadaLink CLI — manage the ScadaLink SCADA system");
|
||||||
|
|
||||||
var contactPointsOption = new Option<string>("--contact-points") { Description = "Comma-separated cluster contact points", Recursive = true };
|
var urlOption = new Option<string>("--url") { Description = "Management API URL", Recursive = true };
|
||||||
var usernameOption = new Option<string>("--username") { Description = "LDAP username", Recursive = true };
|
var usernameOption = new Option<string>("--username") { Description = "LDAP username", Recursive = true };
|
||||||
var passwordOption = new Option<string>("--password") { Description = "LDAP password", Recursive = true };
|
var passwordOption = new Option<string>("--password") { Description = "LDAP password", Recursive = true };
|
||||||
var formatOption = new Option<string>("--format") { Description = "Output format (json or table)", Recursive = true };
|
var formatOption = new Option<string>("--format") { Description = "Output format (json or table)", Recursive = true };
|
||||||
formatOption.DefaultValueFactory = _ => "json";
|
formatOption.DefaultValueFactory = _ => "json";
|
||||||
|
|
||||||
rootCommand.Add(contactPointsOption);
|
rootCommand.Add(urlOption);
|
||||||
rootCommand.Add(usernameOption);
|
rootCommand.Add(usernameOption);
|
||||||
rootCommand.Add(passwordOption);
|
rootCommand.Add(passwordOption);
|
||||||
rootCommand.Add(formatOption);
|
rootCommand.Add(formatOption);
|
||||||
|
|
||||||
// Register command groups
|
// Register command groups
|
||||||
rootCommand.Add(TemplateCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
|
rootCommand.Add(TemplateCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
rootCommand.Add(InstanceCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
|
rootCommand.Add(InstanceCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
rootCommand.Add(SiteCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
|
rootCommand.Add(SiteCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
rootCommand.Add(DeployCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
|
rootCommand.Add(DeployCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
rootCommand.Add(DataConnectionCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
|
rootCommand.Add(DataConnectionCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
rootCommand.Add(ExternalSystemCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
|
rootCommand.Add(ExternalSystemCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
rootCommand.Add(NotificationCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
|
rootCommand.Add(NotificationCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
rootCommand.Add(SecurityCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
|
rootCommand.Add(SecurityCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
rootCommand.Add(AuditLogCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
|
rootCommand.Add(AuditLogCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
rootCommand.Add(HealthCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
|
rootCommand.Add(HealthCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
rootCommand.Add(DebugCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
|
rootCommand.Add(DebugCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
rootCommand.Add(SharedScriptCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
|
rootCommand.Add(SharedScriptCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
rootCommand.Add(DbConnectionCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
|
rootCommand.Add(DbConnectionCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
rootCommand.Add(ApiMethodCommands.Build(contactPointsOption, formatOption, usernameOption, passwordOption));
|
rootCommand.Add(ApiMethodCommands.Build(urlOption, formatOption, usernameOption, passwordOption));
|
||||||
|
|
||||||
rootCommand.SetAction(_ =>
|
rootCommand.SetAction(_ =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# ScadaLink CLI
|
# ScadaLink CLI
|
||||||
|
|
||||||
Command-line tool for managing the ScadaLink SCADA system. Connects to a Central node via Akka.NET ClusterClient and routes commands through the ManagementActor.
|
Command-line tool for managing the ScadaLink SCADA system. Connects to a Central node via HTTP and routes commands through the Management API endpoint (`POST /management`), which dispatches to the ManagementActor. Authentication is handled server-side via LDAP.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -12,27 +12,23 @@ The output binary is `scadalink` (or `scadalink.exe` on Windows).
|
|||||||
|
|
||||||
## Connection
|
## Connection
|
||||||
|
|
||||||
Every command requires a connection to a running Central node. Contact points can be supplied three ways, evaluated in this priority order:
|
Every command requires a connection to a running Central node. The management URL can be supplied three ways, evaluated in this priority order:
|
||||||
|
|
||||||
1. `--contact-points` flag on the command line
|
1. `--url` flag on the command line
|
||||||
2. `SCADALINK_CONTACT_POINTS` environment variable
|
2. `SCADALINK_MANAGEMENT_URL` environment variable
|
||||||
3. `contactPoints` array in `~/.scadalink/config.json`
|
3. `managementUrl` field in `~/.scadalink/config.json`
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points akka.tcp://scadalink@central-host:8081 <command>
|
scadalink --url http://central-host:5000 <command>
|
||||||
```
|
```
|
||||||
|
|
||||||
For a two-node HA cluster, supply both nodes comma-separated:
|
**Docker:** With the Docker setup, the Central UI (and management API) is available at `http://localhost:9001`:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points akka.tcp://scadalink@node1:8081,akka.tcp://scadalink@node2:8082 <command>
|
scadalink --url http://localhost:9001 <command>
|
||||||
```
|
```
|
||||||
|
|
||||||
**Docker (OrbStack):** When running against Docker containers, the contact point hostname must match the central container's Akka `NodeHostname` config. With OrbStack, add `/etc/hosts` entries mapping the container names to their IPs (see `docker/README.md`). Example:
|
For HA failover, use a load balancer URL. The management API is stateless (Basic Auth per request), so any central node can handle any request without sticky sessions.
|
||||||
|
|
||||||
```sh
|
|
||||||
scadalink --contact-points akka.tcp://scadalink@scadalink-central-a:8081 <command>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Global Options
|
## Global Options
|
||||||
|
|
||||||
@@ -40,7 +36,7 @@ These options are accepted by the root command and inherited by all subcommands.
|
|||||||
|
|
||||||
| Option | Description |
|
| Option | Description |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| `--contact-points <value>` | Comma-separated Akka cluster contact point URIs |
|
| `--url <value>` | Management API URL (e.g., `http://localhost:9001`) |
|
||||||
| `--username <value>` | LDAP username for authentication |
|
| `--username <value>` | LDAP username for authentication |
|
||||||
| `--password <value>` | LDAP password for authentication |
|
| `--password <value>` | LDAP password for authentication |
|
||||||
| `--format <json\|table>` | Output format (default: `json`) |
|
| `--format <json\|table>` | Output format (default: `json`) |
|
||||||
@@ -51,29 +47,17 @@ These options are accepted by the root command and inherited by all subcommands.
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"contactPoints": ["akka.tcp://scadalink@central-host:8081"],
|
"managementUrl": "http://localhost:9001"
|
||||||
"ldap": {
|
|
||||||
"server": "ldap.company.com",
|
|
||||||
"port": 636,
|
|
||||||
"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.
|
For the Docker test environment, see `docker/README.md` for a ready-to-use config.
|
||||||
|
|
||||||
## Environment Variables
|
## Environment Variables
|
||||||
|
|
||||||
| Variable | Description |
|
| Variable | Description |
|
||||||
|----------|-------------|
|
|----------|-------------|
|
||||||
| `SCADALINK_CONTACT_POINTS` | Comma-separated contact point URIs (overrides config file) |
|
| `SCADALINK_MANAGEMENT_URL` | Management API URL (overrides config file) |
|
||||||
| `SCADALINK_LDAP_SERVER` | LDAP server hostname (overrides config file) |
|
|
||||||
| `SCADALINK_FORMAT` | Default output format (overrides config file) |
|
| `SCADALINK_FORMAT` | Default output format (overrides config file) |
|
||||||
|
|
||||||
## Output
|
## Output
|
||||||
@@ -103,7 +87,7 @@ Exit codes:
|
|||||||
List all templates with their full attribute, alarm, script, and composition definitions.
|
List all templates with their full attribute, alarm, script, and composition definitions.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> template list
|
scadalink --url <url> template list
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `template get`
|
#### `template get`
|
||||||
@@ -111,7 +95,7 @@ scadalink --contact-points <uri> template list
|
|||||||
Get a single template by ID.
|
Get a single template by ID.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> template get --id <int>
|
scadalink --url <url> template get --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -123,7 +107,7 @@ scadalink --contact-points <uri> template get --id <int>
|
|||||||
Create a new template, optionally inheriting from a parent.
|
Create a new template, optionally inheriting from a parent.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> template create --name <string> [--description <string>] [--parent-id <int>]
|
scadalink --url <url> template create --name <string> [--description <string>] [--parent-id <int>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -137,7 +121,7 @@ scadalink --contact-points <uri> template create --name <string> [--description
|
|||||||
Update an existing template's name, description, or parent.
|
Update an existing template's name, description, or parent.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> template update --id <int> [--name <string>] [--description <string>] [--parent-id <int>]
|
scadalink --url <url> template update --id <int> [--name <string>] [--description <string>] [--parent-id <int>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -152,7 +136,7 @@ scadalink --contact-points <uri> template update --id <int> [--name <string>] [-
|
|||||||
Delete a template by ID.
|
Delete a template by ID.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> template delete --id <int>
|
scadalink --url <url> template delete --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -164,7 +148,7 @@ scadalink --contact-points <uri> template delete --id <int>
|
|||||||
Run pre-deployment validation on a template (flattening, naming collisions, script compilation).
|
Run pre-deployment validation on a template (flattening, naming collisions, script compilation).
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> template validate --id <int>
|
scadalink --url <url> template validate --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -176,7 +160,7 @@ scadalink --contact-points <uri> template validate --id <int>
|
|||||||
Add an attribute to a template.
|
Add an attribute to a template.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> template attribute add --template-id <int> --name <string> --data-type <string> [--default-value <string>] [--tag-path <string>]
|
scadalink --url <url> template attribute add --template-id <int> --name <string> --data-type <string> [--default-value <string>] [--tag-path <string>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -192,7 +176,7 @@ scadalink --contact-points <uri> template attribute add --template-id <int> --na
|
|||||||
Update an attribute on a template.
|
Update an attribute on a template.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> template attribute update --template-id <int> --name <string> [--data-type <string>] [--default-value <string>] [--tag-path <string>]
|
scadalink --url <url> template attribute update --template-id <int> --name <string> [--data-type <string>] [--default-value <string>] [--tag-path <string>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -208,7 +192,7 @@ scadalink --contact-points <uri> template attribute update --template-id <int> -
|
|||||||
Remove an attribute from a template.
|
Remove an attribute from a template.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> template attribute delete --template-id <int> --name <string>
|
scadalink --url <url> template attribute delete --template-id <int> --name <string>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -221,7 +205,7 @@ scadalink --contact-points <uri> template attribute delete --template-id <int> -
|
|||||||
Add an alarm definition to a template.
|
Add an alarm definition to a template.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> template alarm add --template-id <int> --name <string> --trigger-attribute <string> --condition <string> --setpoint <string> [--severity <string>] [--notification-list <string>]
|
scadalink --url <url> template alarm add --template-id <int> --name <string> --trigger-attribute <string> --condition <string> --setpoint <string> [--severity <string>] [--notification-list <string>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -239,7 +223,7 @@ scadalink --contact-points <uri> template alarm add --template-id <int> --name <
|
|||||||
Update an alarm definition on a template.
|
Update an alarm definition on a template.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> template alarm update --template-id <int> --name <string> [--condition <string>] [--setpoint <string>] [--severity <string>] [--notification-list <string>]
|
scadalink --url <url> template alarm update --template-id <int> --name <string> [--condition <string>] [--setpoint <string>] [--severity <string>] [--notification-list <string>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -256,7 +240,7 @@ scadalink --contact-points <uri> template alarm update --template-id <int> --nam
|
|||||||
Remove an alarm definition from a template.
|
Remove an alarm definition from a template.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> template alarm delete --template-id <int> --name <string>
|
scadalink --url <url> template alarm delete --template-id <int> --name <string>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -269,7 +253,7 @@ scadalink --contact-points <uri> template alarm delete --template-id <int> --nam
|
|||||||
Add a script to a template.
|
Add a script to a template.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> template script add --template-id <int> --name <string> --trigger-type <string> [--trigger-attribute <string>] [--interval <int>] --code <string>
|
scadalink --url <url> template script add --template-id <int> --name <string> --trigger-type <string> [--trigger-attribute <string>] [--interval <int>] --code <string>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -286,7 +270,7 @@ scadalink --contact-points <uri> template script add --template-id <int> --name
|
|||||||
Update a script on a template.
|
Update a script on a template.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> template script update --template-id <int> --name <string> [--trigger-type <string>] [--trigger-attribute <string>] [--interval <int>] [--code <string>]
|
scadalink --url <url> template script update --template-id <int> --name <string> [--trigger-type <string>] [--trigger-attribute <string>] [--interval <int>] [--code <string>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -303,7 +287,7 @@ scadalink --contact-points <uri> template script update --template-id <int> --na
|
|||||||
Remove a script from a template.
|
Remove a script from a template.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> template script delete --template-id <int> --name <string>
|
scadalink --url <url> template script delete --template-id <int> --name <string>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -316,7 +300,7 @@ scadalink --contact-points <uri> template script delete --template-id <int> --na
|
|||||||
Add a feature module composition to a template.
|
Add a feature module composition to a template.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> template composition add --template-id <int> --module-template-id <int> --instance-name <string>
|
scadalink --url <url> template composition add --template-id <int> --module-template-id <int> --instance-name <string>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -330,7 +314,7 @@ scadalink --contact-points <uri> template composition add --template-id <int> --
|
|||||||
Remove a feature module composition from a template.
|
Remove a feature module composition from a template.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> template composition delete --template-id <int> --instance-name <string>
|
scadalink --url <url> template composition delete --template-id <int> --instance-name <string>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -347,7 +331,7 @@ scadalink --contact-points <uri> template composition delete --template-id <int>
|
|||||||
Get a single instance by ID.
|
Get a single instance by ID.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> instance get --id <int>
|
scadalink --url <url> instance get --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -359,7 +343,7 @@ scadalink --contact-points <uri> instance get --id <int>
|
|||||||
List instances, with optional filters.
|
List instances, with optional filters.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> instance list [--site-id <int>] [--template-id <int>] [--search <string>]
|
scadalink --url <url> instance list [--site-id <int>] [--template-id <int>] [--search <string>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -373,7 +357,7 @@ scadalink --contact-points <uri> instance list [--site-id <int>] [--template-id
|
|||||||
Create a new instance of a template at a site.
|
Create a new instance of a template at a site.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> instance create --name <string> --template-id <int> --site-id <int> [--area-id <int>]
|
scadalink --url <url> instance create --name <string> --template-id <int> --site-id <int> [--area-id <int>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -388,7 +372,7 @@ scadalink --contact-points <uri> instance create --name <string> --template-id <
|
|||||||
Deploy an instance to its site. Acquires the per-instance operation lock.
|
Deploy an instance to its site. Acquires the per-instance operation lock.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> instance deploy --id <int>
|
scadalink --url <url> instance deploy --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -400,7 +384,7 @@ scadalink --contact-points <uri> instance deploy --id <int>
|
|||||||
Enable a previously disabled instance.
|
Enable a previously disabled instance.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> instance enable --id <int>
|
scadalink --url <url> instance enable --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -412,7 +396,7 @@ scadalink --contact-points <uri> instance enable --id <int>
|
|||||||
Disable a running instance without deleting it.
|
Disable a running instance without deleting it.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> instance disable --id <int>
|
scadalink --url <url> instance disable --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -424,7 +408,7 @@ scadalink --contact-points <uri> instance disable --id <int>
|
|||||||
Delete an instance. The instance must be disabled first.
|
Delete an instance. The instance must be disabled first.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> instance delete --id <int>
|
scadalink --url <url> instance delete --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -436,7 +420,7 @@ scadalink --contact-points <uri> instance delete --id <int>
|
|||||||
Set data connection bindings for an instance's attributes.
|
Set data connection bindings for an instance's attributes.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> instance set-bindings --id <int> --bindings <json>
|
scadalink --url <url> instance set-bindings --id <int> --bindings <json>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -453,7 +437,7 @@ scadalink --contact-points <uri> instance set-bindings --id <int> --bindings <js
|
|||||||
Get a single site by ID.
|
Get a single site by ID.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> site get --id <int>
|
scadalink --url <url> site get --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -465,7 +449,7 @@ scadalink --contact-points <uri> site get --id <int>
|
|||||||
List all registered sites.
|
List all registered sites.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> site list
|
scadalink --url <url> site list
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `site create`
|
#### `site create`
|
||||||
@@ -473,7 +457,7 @@ scadalink --contact-points <uri> site list
|
|||||||
Register a new site.
|
Register a new site.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> site create --name <string> --identifier <string> [--description <string>]
|
scadalink --url <url> site create --name <string> --identifier <string> [--description <string>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -487,7 +471,7 @@ scadalink --contact-points <uri> site create --name <string> --identifier <strin
|
|||||||
Delete a site. Fails if any instances are assigned to it.
|
Delete a site. Fails if any instances are assigned to it.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> site delete --id <int>
|
scadalink --url <url> site delete --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -499,7 +483,7 @@ scadalink --contact-points <uri> site delete --id <int>
|
|||||||
Push compiled artifacts to one or all sites.
|
Push compiled artifacts to one or all sites.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> site deploy-artifacts [--site-id <int>]
|
scadalink --url <url> site deploy-artifacts [--site-id <int>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -511,7 +495,7 @@ scadalink --contact-points <uri> site deploy-artifacts [--site-id <int>]
|
|||||||
List all areas for a site.
|
List all areas for a site.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> site area list --site-id <int>
|
scadalink --url <url> site area list --site-id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -523,7 +507,7 @@ scadalink --contact-points <uri> site area list --site-id <int>
|
|||||||
Create an area within a site.
|
Create an area within a site.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> site area create --site-id <int> --name <string> [--parent-area-id <int>]
|
scadalink --url <url> site area create --site-id <int> --name <string> [--parent-area-id <int>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -537,7 +521,7 @@ scadalink --contact-points <uri> site area create --site-id <int> --name <string
|
|||||||
Update an area's name or parent.
|
Update an area's name or parent.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> site area update --id <int> [--name <string>] [--parent-area-id <int>]
|
scadalink --url <url> site area update --id <int> [--name <string>] [--parent-area-id <int>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -551,7 +535,7 @@ scadalink --contact-points <uri> site area update --id <int> [--name <string>] [
|
|||||||
Delete an area. Fails if any instances are assigned to it.
|
Delete an area. Fails if any instances are assigned to it.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> site area delete --id <int>
|
scadalink --url <url> site area delete --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -567,7 +551,7 @@ scadalink --contact-points <uri> site area delete --id <int>
|
|||||||
Deploy a single instance (same as `instance deploy`).
|
Deploy a single instance (same as `instance deploy`).
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> deploy instance --id <int>
|
scadalink --url <url> deploy instance --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -579,7 +563,7 @@ scadalink --contact-points <uri> deploy instance --id <int>
|
|||||||
Deploy compiled artifacts to one or all sites (same as `site deploy-artifacts`).
|
Deploy compiled artifacts to one or all sites (same as `site deploy-artifacts`).
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> deploy artifacts [--site-id <int>]
|
scadalink --url <url> deploy artifacts [--site-id <int>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -591,7 +575,7 @@ scadalink --contact-points <uri> deploy artifacts [--site-id <int>]
|
|||||||
Query deployment records, with optional filters and pagination.
|
Query deployment records, with optional filters and pagination.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> deploy status [--instance-id <int>] [--status <string>] [--page <int>] [--page-size <int>]
|
scadalink --url <url> deploy status [--instance-id <int>] [--status <string>] [--page <int>] [--page-size <int>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Default | Description |
|
| Option | Required | Default | Description |
|
||||||
@@ -610,7 +594,7 @@ scadalink --contact-points <uri> deploy status [--instance-id <int>] [--status <
|
|||||||
Get a single data connection by ID.
|
Get a single data connection by ID.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> data-connection get --id <int>
|
scadalink --url <url> data-connection get --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -622,7 +606,7 @@ scadalink --contact-points <uri> data-connection get --id <int>
|
|||||||
List all configured data connections.
|
List all configured data connections.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> data-connection list
|
scadalink --url <url> data-connection list
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `data-connection create`
|
#### `data-connection create`
|
||||||
@@ -630,7 +614,7 @@ scadalink --contact-points <uri> data-connection list
|
|||||||
Create a new data connection definition.
|
Create a new data connection definition.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> data-connection create --name <string> --protocol <string> [--configuration <json>]
|
scadalink --url <url> data-connection create --name <string> --protocol <string> [--configuration <json>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -644,7 +628,7 @@ scadalink --contact-points <uri> data-connection create --name <string> --protoc
|
|||||||
Update a data connection definition.
|
Update a data connection definition.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> data-connection update --id <int> [--name <string>] [--protocol <string>] [--configuration <json>]
|
scadalink --url <url> data-connection update --id <int> [--name <string>] [--protocol <string>] [--configuration <json>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -659,7 +643,7 @@ scadalink --contact-points <uri> data-connection update --id <int> [--name <stri
|
|||||||
Delete a data connection.
|
Delete a data connection.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> data-connection delete --id <int>
|
scadalink --url <url> data-connection delete --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -671,7 +655,7 @@ scadalink --contact-points <uri> data-connection delete --id <int>
|
|||||||
Assign a data connection to a site.
|
Assign a data connection to a site.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> data-connection assign --connection-id <int> --site-id <int>
|
scadalink --url <url> data-connection assign --connection-id <int> --site-id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -684,7 +668,7 @@ scadalink --contact-points <uri> data-connection assign --connection-id <int> --
|
|||||||
Remove a data connection assignment from a site.
|
Remove a data connection assignment from a site.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> data-connection unassign --connection-id <int> --site-id <int>
|
scadalink --url <url> data-connection unassign --connection-id <int> --site-id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -701,7 +685,7 @@ scadalink --contact-points <uri> data-connection unassign --connection-id <int>
|
|||||||
Get a single external system definition by ID.
|
Get a single external system definition by ID.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> external-system get --id <int>
|
scadalink --url <url> external-system get --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -713,7 +697,7 @@ scadalink --contact-points <uri> external-system get --id <int>
|
|||||||
List all external system definitions.
|
List all external system definitions.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> external-system list
|
scadalink --url <url> external-system list
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `external-system create`
|
#### `external-system create`
|
||||||
@@ -721,7 +705,7 @@ scadalink --contact-points <uri> external-system list
|
|||||||
Register an external HTTP system that scripts can call.
|
Register an external HTTP system that scripts can call.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> external-system create --name <string> --endpoint-url <url> --auth-type <string> [--auth-config <json>]
|
scadalink --url <url> external-system create --name <string> --endpoint-url <url> --auth-type <string> [--auth-config <json>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -736,7 +720,7 @@ scadalink --contact-points <uri> external-system create --name <string> --endpoi
|
|||||||
Update an external system definition.
|
Update an external system definition.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> external-system update --id <int> [--name <string>] [--endpoint-url <url>] [--auth-type <string>] [--auth-config <json>]
|
scadalink --url <url> external-system update --id <int> [--name <string>] [--endpoint-url <url>] [--auth-type <string>] [--auth-config <json>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -752,7 +736,7 @@ scadalink --contact-points <uri> external-system update --id <int> [--name <stri
|
|||||||
Delete an external system definition.
|
Delete an external system definition.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> external-system delete --id <int>
|
scadalink --url <url> external-system delete --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -768,7 +752,7 @@ scadalink --contact-points <uri> external-system delete --id <int>
|
|||||||
Get a single notification list by ID.
|
Get a single notification list by ID.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> notification get --id <int>
|
scadalink --url <url> notification get --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -780,7 +764,7 @@ scadalink --contact-points <uri> notification get --id <int>
|
|||||||
List all notification lists.
|
List all notification lists.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> notification list
|
scadalink --url <url> notification list
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `notification create`
|
#### `notification create`
|
||||||
@@ -788,7 +772,7 @@ scadalink --contact-points <uri> notification list
|
|||||||
Create a notification list with one or more recipients.
|
Create a notification list with one or more recipients.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> notification create --name <string> --emails <email1,email2,...>
|
scadalink --url <url> notification create --name <string> --emails <email1,email2,...>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -801,7 +785,7 @@ scadalink --contact-points <uri> notification create --name <string> --emails <e
|
|||||||
Update a notification list's name or recipients.
|
Update a notification list's name or recipients.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> notification update --id <int> [--name <string>] [--emails <email1,email2,...>]
|
scadalink --url <url> notification update --id <int> [--name <string>] [--emails <email1,email2,...>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -815,7 +799,7 @@ scadalink --contact-points <uri> notification update --id <int> [--name <string>
|
|||||||
Delete a notification list.
|
Delete a notification list.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> notification delete --id <int>
|
scadalink --url <url> notification delete --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -827,7 +811,7 @@ scadalink --contact-points <uri> notification delete --id <int>
|
|||||||
Show the current SMTP configuration.
|
Show the current SMTP configuration.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> notification smtp list
|
scadalink --url <url> notification smtp list
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `notification smtp update`
|
#### `notification smtp update`
|
||||||
@@ -835,7 +819,7 @@ scadalink --contact-points <uri> notification smtp list
|
|||||||
Update the SMTP configuration.
|
Update the SMTP configuration.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> notification smtp update --host <string> --port <int> --auth-type <string> [--username <string>] [--password <string>] [--from-address <string>]
|
scadalink --url <url> notification smtp update --host <string> --port <int> --auth-type <string> [--username <string>] [--password <string>] [--from-address <string>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -856,7 +840,7 @@ scadalink --contact-points <uri> notification smtp update --host <string> --port
|
|||||||
List all inbound API keys.
|
List all inbound API keys.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> security api-key list
|
scadalink --url <url> security api-key list
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `security api-key create`
|
#### `security api-key create`
|
||||||
@@ -864,7 +848,7 @@ scadalink --contact-points <uri> security api-key list
|
|||||||
Create a new inbound API key. The generated key value is returned in the response and not stored in plaintext — save it immediately.
|
Create a new inbound API key. The generated key value is returned in the response and not stored in plaintext — save it immediately.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> security api-key create --name <string>
|
scadalink --url <url> security api-key create --name <string>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -876,7 +860,7 @@ scadalink --contact-points <uri> security api-key create --name <string>
|
|||||||
Update an API key's name or enabled status.
|
Update an API key's name or enabled status.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> security api-key update --id <int> [--name <string>] [--enabled <bool>]
|
scadalink --url <url> security api-key update --id <int> [--name <string>] [--enabled <bool>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -890,7 +874,7 @@ scadalink --contact-points <uri> security api-key update --id <int> [--name <str
|
|||||||
Revoke and delete an API key.
|
Revoke and delete an API key.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> security api-key delete --id <int>
|
scadalink --url <url> security api-key delete --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -902,7 +886,7 @@ scadalink --contact-points <uri> security api-key delete --id <int>
|
|||||||
List all LDAP group → role mappings.
|
List all LDAP group → role mappings.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> security role-mapping list
|
scadalink --url <url> security role-mapping list
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `security role-mapping create`
|
#### `security role-mapping create`
|
||||||
@@ -910,7 +894,7 @@ scadalink --contact-points <uri> security role-mapping list
|
|||||||
Map an LDAP group to a ScadaLink role.
|
Map an LDAP group to a ScadaLink role.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> security role-mapping create --ldap-group <string> --role <string>
|
scadalink --url <url> security role-mapping create --ldap-group <string> --role <string>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -923,7 +907,7 @@ scadalink --contact-points <uri> security role-mapping create --ldap-group <stri
|
|||||||
Update an LDAP role mapping.
|
Update an LDAP role mapping.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> security role-mapping update --id <int> [--ldap-group <string>] [--role <string>]
|
scadalink --url <url> security role-mapping update --id <int> [--ldap-group <string>] [--role <string>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -937,7 +921,7 @@ scadalink --contact-points <uri> security role-mapping update --id <int> [--ldap
|
|||||||
Remove an LDAP role mapping.
|
Remove an LDAP role mapping.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> security role-mapping delete --id <int>
|
scadalink --url <url> security role-mapping delete --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -949,7 +933,7 @@ scadalink --contact-points <uri> security role-mapping delete --id <int>
|
|||||||
List all site scope rules for role mappings.
|
List all site scope rules for role mappings.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> security scope-rule list [--role-mapping-id <int>]
|
scadalink --url <url> security scope-rule list [--role-mapping-id <int>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -961,7 +945,7 @@ scadalink --contact-points <uri> security scope-rule list [--role-mapping-id <in
|
|||||||
Add a site scope rule to a role mapping, restricting it to a specific site.
|
Add a site scope rule to a role mapping, restricting it to a specific site.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> security scope-rule add --role-mapping-id <int> --site-id <int>
|
scadalink --url <url> security scope-rule add --role-mapping-id <int> --site-id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -974,7 +958,7 @@ scadalink --contact-points <uri> security scope-rule add --role-mapping-id <int>
|
|||||||
Remove a site scope rule from a role mapping.
|
Remove a site scope rule from a role mapping.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> security scope-rule delete --id <int>
|
scadalink --url <url> security scope-rule delete --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -990,7 +974,7 @@ scadalink --contact-points <uri> security scope-rule delete --id <int>
|
|||||||
Return the current health state for all known sites as a JSON object keyed by site identifier.
|
Return the current health state for all known sites as a JSON object keyed by site identifier.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> health summary
|
scadalink --url <url> health summary
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `health site`
|
#### `health site`
|
||||||
@@ -998,7 +982,7 @@ scadalink --contact-points <uri> health summary
|
|||||||
Return the health state for a single site.
|
Return the health state for a single site.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> health site --identifier <string>
|
scadalink --url <url> health site --identifier <string>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -1010,7 +994,7 @@ scadalink --contact-points <uri> health site --identifier <string>
|
|||||||
Query the site event log for a specific site. Events are fetched remotely from the site's local SQLite store.
|
Query the site event log for a specific site. Events are fetched remotely from the site's local SQLite store.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> health event-log --site-identifier <string> [--from <datetime>] [--to <datetime>] [--search <string>] [--page <int>] [--page-size <int>]
|
scadalink --url <url> health event-log --site-identifier <string> [--from <datetime>] [--to <datetime>] [--search <string>] [--page <int>] [--page-size <int>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Default | Description |
|
| Option | Required | Default | Description |
|
||||||
@@ -1027,7 +1011,7 @@ scadalink --contact-points <uri> health event-log --site-identifier <string> [--
|
|||||||
Query parked (dead-letter) messages at a specific site.
|
Query parked (dead-letter) messages at a specific site.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> health parked-messages --site-identifier <string> [--page <int>] [--page-size <int>]
|
scadalink --url <url> health parked-messages --site-identifier <string> [--page <int>] [--page-size <int>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Default | Description |
|
| Option | Required | Default | Description |
|
||||||
@@ -1045,7 +1029,7 @@ scadalink --contact-points <uri> health parked-messages --site-identifier <strin
|
|||||||
Request a point-in-time snapshot of a running instance's current attribute values and alarm states from the site. The instance must be deployed and enabled.
|
Request a point-in-time snapshot of a running instance's current attribute values and alarm states from the site. The instance must be deployed and enabled.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> debug snapshot --id <int>
|
scadalink --url <url> debug snapshot --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -1063,7 +1047,7 @@ The command resolves the instance's site internally and routes the request to th
|
|||||||
Query the central audit log with optional filters and pagination.
|
Query the central audit log with optional filters and pagination.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> audit-log query [options]
|
scadalink --url <url> audit-log query [options]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Default | Description |
|
| Option | Required | Default | Description |
|
||||||
@@ -1085,7 +1069,7 @@ scadalink --contact-points <uri> audit-log query [options]
|
|||||||
List all shared script definitions.
|
List all shared script definitions.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> shared-script list
|
scadalink --url <url> shared-script list
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `shared-script get`
|
#### `shared-script get`
|
||||||
@@ -1093,7 +1077,7 @@ scadalink --contact-points <uri> shared-script list
|
|||||||
Get a single shared script by ID.
|
Get a single shared script by ID.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> shared-script get --id <int>
|
scadalink --url <url> shared-script get --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -1105,7 +1089,7 @@ scadalink --contact-points <uri> shared-script get --id <int>
|
|||||||
Create a new shared script.
|
Create a new shared script.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> shared-script create --name <string> --code <string>
|
scadalink --url <url> shared-script create --name <string> --code <string>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -1118,7 +1102,7 @@ scadalink --contact-points <uri> shared-script create --name <string> --code <st
|
|||||||
Update a shared script's name or code.
|
Update a shared script's name or code.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> shared-script update --id <int> [--name <string>] [--code <string>]
|
scadalink --url <url> shared-script update --id <int> [--name <string>] [--code <string>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -1132,7 +1116,7 @@ scadalink --contact-points <uri> shared-script update --id <int> [--name <string
|
|||||||
Delete a shared script.
|
Delete a shared script.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> shared-script delete --id <int>
|
scadalink --url <url> shared-script delete --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -1148,7 +1132,7 @@ scadalink --contact-points <uri> shared-script delete --id <int>
|
|||||||
List all database connection definitions.
|
List all database connection definitions.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> db-connection list
|
scadalink --url <url> db-connection list
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `db-connection get`
|
#### `db-connection get`
|
||||||
@@ -1156,7 +1140,7 @@ scadalink --contact-points <uri> db-connection list
|
|||||||
Get a single database connection by ID.
|
Get a single database connection by ID.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> db-connection get --id <int>
|
scadalink --url <url> db-connection get --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -1168,7 +1152,7 @@ scadalink --contact-points <uri> db-connection get --id <int>
|
|||||||
Create a new database connection definition.
|
Create a new database connection definition.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> db-connection create --name <string> --connection-string <string> [--provider <string>]
|
scadalink --url <url> db-connection create --name <string> --connection-string <string> [--provider <string>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -1182,7 +1166,7 @@ scadalink --contact-points <uri> db-connection create --name <string> --connecti
|
|||||||
Update a database connection definition.
|
Update a database connection definition.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> db-connection update --id <int> [--name <string>] [--connection-string <string>] [--provider <string>]
|
scadalink --url <url> db-connection update --id <int> [--name <string>] [--connection-string <string>] [--provider <string>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -1197,7 +1181,7 @@ scadalink --contact-points <uri> db-connection update --id <int> [--name <string
|
|||||||
Delete a database connection definition.
|
Delete a database connection definition.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> db-connection delete --id <int>
|
scadalink --url <url> db-connection delete --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -1213,7 +1197,7 @@ scadalink --contact-points <uri> db-connection delete --id <int>
|
|||||||
List all inbound API method definitions.
|
List all inbound API method definitions.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> api-method list
|
scadalink --url <url> api-method list
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `api-method get`
|
#### `api-method get`
|
||||||
@@ -1221,7 +1205,7 @@ scadalink --contact-points <uri> api-method list
|
|||||||
Get a single inbound API method by ID.
|
Get a single inbound API method by ID.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> api-method get --id <int>
|
scadalink --url <url> api-method get --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -1233,7 +1217,7 @@ scadalink --contact-points <uri> api-method get --id <int>
|
|||||||
Create a new inbound API method.
|
Create a new inbound API method.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> api-method create --name <string> --code <string> [--description <string>]
|
scadalink --url <url> api-method create --name <string> --code <string> [--description <string>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -1247,7 +1231,7 @@ scadalink --contact-points <uri> api-method create --name <string> --code <strin
|
|||||||
Update an inbound API method.
|
Update an inbound API method.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> api-method update --id <int> [--name <string>] [--code <string>] [--description <string>]
|
scadalink --url <url> api-method update --id <int> [--name <string>] [--code <string>] [--description <string>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
@@ -1262,7 +1246,7 @@ scadalink --contact-points <uri> api-method update --id <int> [--name <string>]
|
|||||||
Delete an inbound API method.
|
Delete an inbound API method.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scadalink --contact-points <uri> api-method delete --id <int>
|
scadalink --url <url> api-method delete --id <int>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Required | Description |
|
| Option | Required | Description |
|
||||||
|
|||||||
@@ -11,16 +11,9 @@
|
|||||||
<InternalsVisibleTo Include="ScadaLink.CLI.Tests" />
|
<InternalsVisibleTo Include="ScadaLink.CLI.Tests" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<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="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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../ScadaLink.Commons/ScadaLink.Commons.csproj" />
|
<ProjectReference Include="../ScadaLink.Commons/ScadaLink.Commons.csproj" />
|
||||||
<ProjectReference Include="../ScadaLink.Security/ScadaLink.Security.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Collections.Frozen;
|
||||||
|
|
||||||
|
namespace ScadaLink.Commons.Messages.Management;
|
||||||
|
|
||||||
|
public static class ManagementCommandRegistry
|
||||||
|
{
|
||||||
|
private static readonly FrozenDictionary<string, Type> Commands = BuildRegistry();
|
||||||
|
|
||||||
|
public static Type? Resolve(string commandName)
|
||||||
|
{
|
||||||
|
return Commands.GetValueOrDefault(commandName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetCommandName(Type commandType)
|
||||||
|
{
|
||||||
|
var name = commandType.Name;
|
||||||
|
return name.EndsWith("Command", StringComparison.Ordinal)
|
||||||
|
? name[..^"Command".Length]
|
||||||
|
: name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FrozenDictionary<string, Type> BuildRegistry()
|
||||||
|
{
|
||||||
|
var ns = typeof(ManagementEnvelope).Namespace!;
|
||||||
|
var assembly = typeof(ManagementEnvelope).Assembly;
|
||||||
|
|
||||||
|
return assembly.GetTypes()
|
||||||
|
.Where(t => t.Namespace == ns
|
||||||
|
&& t.Name.EndsWith("Command", StringComparison.Ordinal)
|
||||||
|
&& !t.IsAbstract)
|
||||||
|
.ToFrozenDictionary(
|
||||||
|
t => t.Name[..^"Command".Length],
|
||||||
|
t => t,
|
||||||
|
StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -483,11 +483,18 @@ public class DataConnectionActor : UntypedActor, IWithStash, IWithTimers
|
|||||||
|
|
||||||
private void ReSubscribeAll()
|
private void ReSubscribeAll()
|
||||||
{
|
{
|
||||||
_log.Info("[{0}] Re-subscribing {1} tags after reconnect", _connectionName, _subscriptionIds.Count);
|
// Derive tag list from _subscriptionsByInstance (durable source of truth),
|
||||||
|
// not _subscriptionIds which gets cleared and is only repopulated on success.
|
||||||
|
var allTags = _subscriptionsByInstance.Values
|
||||||
|
.SelectMany(tags => tags)
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
_log.Info("[{0}] Re-subscribing {1} tags after reconnect", _connectionName, allTags.Count);
|
||||||
|
|
||||||
var self = Self;
|
var self = Self;
|
||||||
var allTags = _subscriptionIds.Keys.ToList();
|
|
||||||
_subscriptionIds.Clear();
|
_subscriptionIds.Clear();
|
||||||
|
_unresolvedTags.Clear();
|
||||||
_resolvedTags = 0;
|
_resolvedTags = 0;
|
||||||
|
|
||||||
foreach (var tagPath in allTags)
|
foreach (var tagPath in allTags)
|
||||||
@@ -535,6 +542,16 @@ public class DataConnectionActor : UntypedActor, IWithStash, IWithTimers
|
|||||||
{
|
{
|
||||||
_log.Debug("[{0}] Tag resolution still failing for {1}: {2}",
|
_log.Debug("[{0}] Tag resolution still failing for {1}: {2}",
|
||||||
_connectionName, msg.TagPath, msg.Error);
|
_connectionName, msg.TagPath, msg.Error);
|
||||||
|
|
||||||
|
// Track as unresolved so periodic retry picks it up
|
||||||
|
if (_unresolvedTags.Add(msg.TagPath))
|
||||||
|
{
|
||||||
|
Timers.StartPeriodicTimer(
|
||||||
|
"tag-resolution-retry",
|
||||||
|
new RetryTagResolution(),
|
||||||
|
_options.TagResolutionRetryInterval,
|
||||||
|
_options.TagResolutionRetryInterval);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleTagValueReceived(TagValueReceived msg)
|
private void HandleTagValueReceived(TagValueReceived msg)
|
||||||
|
|||||||
@@ -192,6 +192,8 @@ akka {{
|
|||||||
Props.Create(() => new ScadaLink.ManagementService.ManagementActor(_serviceProvider, mgmtLogger)),
|
Props.Create(() => new ScadaLink.ManagementService.ManagementActor(_serviceProvider, mgmtLogger)),
|
||||||
"management");
|
"management");
|
||||||
ClusterClientReceptionist.Get(_actorSystem).RegisterService(mgmtActor);
|
ClusterClientReceptionist.Get(_actorSystem).RegisterService(mgmtActor);
|
||||||
|
var mgmtHolder = _serviceProvider.GetRequiredService<ScadaLink.ManagementService.ManagementActorHolder>();
|
||||||
|
mgmtHolder.ActorRef = mgmtActor;
|
||||||
_logger.LogInformation("ManagementActor registered with ClusterClientReceptionist");
|
_logger.LogInformation("ManagementActor registered with ClusterClientReceptionist");
|
||||||
|
|
||||||
_logger.LogInformation("Central actors registered. CentralCommunicationActor created.");
|
_logger.LogInformation("Central actors registered. CentralCommunicationActor created.");
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ try
|
|||||||
app.MapStaticAssets();
|
app.MapStaticAssets();
|
||||||
app.MapCentralUI<ScadaLink.Host.Components.App>();
|
app.MapCentralUI<ScadaLink.Host.Components.App>();
|
||||||
app.MapInboundAPI();
|
app.MapInboundAPI();
|
||||||
|
app.MapManagementAPI();
|
||||||
|
|
||||||
// Compile and register all Inbound API method scripts at startup
|
// Compile and register all Inbound API method scripts at startup
|
||||||
using (var scope = app.Services.CreateScope())
|
using (var scope = app.Services.CreateScope())
|
||||||
|
|||||||
8
src/ScadaLink.ManagementService/ManagementActorHolder.cs
Normal file
8
src/ScadaLink.ManagementService/ManagementActorHolder.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Akka.Actor;
|
||||||
|
|
||||||
|
namespace ScadaLink.ManagementService;
|
||||||
|
|
||||||
|
public class ManagementActorHolder
|
||||||
|
{
|
||||||
|
public IActorRef? ActorRef { get; set; }
|
||||||
|
}
|
||||||
151
src/ScadaLink.ManagementService/ManagementEndpoints.cs
Normal file
151
src/ScadaLink.ManagementService/ManagementEndpoints.cs
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Akka.Actor;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using ScadaLink.Commons.Messages.Management;
|
||||||
|
using ScadaLink.Security;
|
||||||
|
|
||||||
|
namespace ScadaLink.ManagementService;
|
||||||
|
|
||||||
|
public static class ManagementEndpoints
|
||||||
|
{
|
||||||
|
private static readonly TimeSpan AskTimeout = TimeSpan.FromSeconds(30);
|
||||||
|
|
||||||
|
public static IEndpointRouteBuilder MapManagementAPI(this IEndpointRouteBuilder endpoints)
|
||||||
|
{
|
||||||
|
endpoints.MapPost("/management", (Delegate)HandleRequest);
|
||||||
|
return endpoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> HandleRequest(HttpContext context)
|
||||||
|
{
|
||||||
|
var logger = context.RequestServices.GetRequiredService<ILogger<ManagementActorHolder>>();
|
||||||
|
|
||||||
|
// 1. Decode Basic Auth
|
||||||
|
var authHeader = context.Request.Headers.Authorization.ToString();
|
||||||
|
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return Results.Json(new { error = "Authorization header required (Basic scheme).", code = "AUTH_FAILED" }, statusCode: 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
string username, password;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var decoded = Encoding.UTF8.GetString(Convert.FromBase64String(authHeader["Basic ".Length..]));
|
||||||
|
var colon = decoded.IndexOf(':');
|
||||||
|
if (colon < 0) throw new FormatException();
|
||||||
|
username = decoded[..colon];
|
||||||
|
password = decoded[(colon + 1)..];
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return Results.Json(new { error = "Malformed Basic Auth header.", code = "AUTH_FAILED" }, statusCode: 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
|
||||||
|
{
|
||||||
|
return Results.Json(new { error = "Username and password are required.", code = "AUTH_FAILED" }, statusCode: 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. LDAP authentication
|
||||||
|
var ldapAuth = context.RequestServices.GetRequiredService<LdapAuthService>();
|
||||||
|
var authResult = await ldapAuth.AuthenticateAsync(username, password);
|
||||||
|
if (!authResult.Success)
|
||||||
|
{
|
||||||
|
return Results.Json(
|
||||||
|
new { error = authResult.ErrorMessage ?? "Authentication failed.", code = "AUTH_FAILED" },
|
||||||
|
statusCode: 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Role resolution
|
||||||
|
var roleMapper = context.RequestServices.GetRequiredService<RoleMapper>();
|
||||||
|
var mappingResult = await roleMapper.MapGroupsToRolesAsync(
|
||||||
|
authResult.Groups ?? (IReadOnlyList<string>)Array.Empty<string>());
|
||||||
|
|
||||||
|
var permittedSiteIds = mappingResult.IsSystemWideDeployment
|
||||||
|
? Array.Empty<string>()
|
||||||
|
: mappingResult.PermittedSiteIds.ToArray();
|
||||||
|
|
||||||
|
var authenticatedUser = new AuthenticatedUser(
|
||||||
|
authResult.Username!,
|
||||||
|
authResult.DisplayName!,
|
||||||
|
mappingResult.Roles.ToArray(),
|
||||||
|
permittedSiteIds);
|
||||||
|
|
||||||
|
// 4. Parse command from request body
|
||||||
|
JsonDocument doc;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
doc = await JsonDocument.ParseAsync(context.Request.Body);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return Results.Json(new { error = "Invalid JSON body.", code = "BAD_REQUEST" }, statusCode: 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!doc.RootElement.TryGetProperty("command", out var commandNameElement))
|
||||||
|
{
|
||||||
|
return Results.Json(new { error = "Missing 'command' field.", code = "BAD_REQUEST" }, statusCode: 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandName = commandNameElement.GetString();
|
||||||
|
if (string.IsNullOrWhiteSpace(commandName))
|
||||||
|
{
|
||||||
|
return Results.Json(new { error = "Empty 'command' field.", code = "BAD_REQUEST" }, statusCode: 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandType = ManagementCommandRegistry.Resolve(commandName);
|
||||||
|
if (commandType == null)
|
||||||
|
{
|
||||||
|
return Results.Json(new { error = $"Unknown command: '{commandName}'.", code = "BAD_REQUEST" }, statusCode: 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
object command;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var payloadElement = doc.RootElement.TryGetProperty("payload", out var p)
|
||||||
|
? p
|
||||||
|
: JsonDocument.Parse("{}").RootElement;
|
||||||
|
command = JsonSerializer.Deserialize(payloadElement.GetRawText(), commandType,
|
||||||
|
new JsonSerializerOptions { PropertyNameCaseInsensitive = true })!;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Results.Json(new { error = $"Failed to deserialize payload: {ex.Message}", code = "BAD_REQUEST" }, statusCode: 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Dispatch to ManagementActor
|
||||||
|
var holder = context.RequestServices.GetRequiredService<ManagementActorHolder>();
|
||||||
|
if (holder.ActorRef == null)
|
||||||
|
{
|
||||||
|
return Results.Json(new { error = "Management service not ready.", code = "SERVICE_UNAVAILABLE" }, statusCode: 503);
|
||||||
|
}
|
||||||
|
|
||||||
|
var correlationId = Guid.NewGuid().ToString("N");
|
||||||
|
var envelope = new ManagementEnvelope(authenticatedUser, command, correlationId);
|
||||||
|
|
||||||
|
object response;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
response = await holder.ActorRef.Ask(envelope, AskTimeout);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError(ex, "ManagementActor Ask timed out or failed (CorrelationId={CorrelationId})", correlationId);
|
||||||
|
return Results.Json(new { error = "Request timed out.", code = "TIMEOUT" }, statusCode: 504);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Map response
|
||||||
|
return response switch
|
||||||
|
{
|
||||||
|
ManagementSuccess success => Results.Text(success.JsonData, "application/json", statusCode: 200),
|
||||||
|
ManagementError error => Results.Json(new { error = error.Error, code = error.ErrorCode }, statusCode: 400),
|
||||||
|
ManagementUnauthorized unauth => Results.Json(new { error = unauth.Message, code = "UNAUTHORIZED" }, statusCode: 403),
|
||||||
|
_ => Results.Json(new { error = "Unexpected response.", code = "INTERNAL_ERROR" }, statusCode: 500)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,12 +5,12 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Akka" Version="1.5.62" />
|
<PackageReference Include="Akka" Version="1.5.62" />
|
||||||
<PackageReference Include="Akka.Cluster.Tools" Version="1.5.62" />
|
<PackageReference Include="Akka.Cluster.Tools" Version="1.5.62" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.5" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.5" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.5" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../ScadaLink.Commons/ScadaLink.Commons.csproj" />
|
<ProjectReference Include="../ScadaLink.Commons/ScadaLink.Commons.csproj" />
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ public static class ServiceCollectionExtensions
|
|||||||
{
|
{
|
||||||
public static IServiceCollection AddManagementService(this IServiceCollection services)
|
public static IServiceCollection AddManagementService(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
services.AddSingleton<ManagementActorHolder>();
|
||||||
services.AddOptions<ManagementServiceOptions>()
|
services.AddOptions<ManagementServiceOptions>()
|
||||||
.BindConfiguration("ScadaLink:ManagementService");
|
.BindConfiguration("ScadaLink:ManagementService");
|
||||||
return services;
|
return services;
|
||||||
|
|||||||
@@ -5,68 +5,43 @@ namespace ScadaLink.CLI.Tests;
|
|||||||
public class CliConfigTests
|
public class CliConfigTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Load_DefaultValues_WhenNoConfigExists()
|
public void Load_DefaultFormat_IsJson()
|
||||||
{
|
{
|
||||||
// Clear environment variables that might affect the test
|
var origUrl = Environment.GetEnvironmentVariable("SCADALINK_MANAGEMENT_URL");
|
||||||
var origContact = Environment.GetEnvironmentVariable("SCADALINK_CONTACT_POINTS");
|
|
||||||
var origLdap = Environment.GetEnvironmentVariable("SCADALINK_LDAP_SERVER");
|
|
||||||
var origFormat = Environment.GetEnvironmentVariable("SCADALINK_FORMAT");
|
var origFormat = Environment.GetEnvironmentVariable("SCADALINK_FORMAT");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Environment.SetEnvironmentVariable("SCADALINK_CONTACT_POINTS", null);
|
Environment.SetEnvironmentVariable("SCADALINK_MANAGEMENT_URL", null);
|
||||||
Environment.SetEnvironmentVariable("SCADALINK_LDAP_SERVER", null);
|
|
||||||
Environment.SetEnvironmentVariable("SCADALINK_FORMAT", null);
|
Environment.SetEnvironmentVariable("SCADALINK_FORMAT", null);
|
||||||
|
|
||||||
var config = CliConfig.Load();
|
var config = CliConfig.Load();
|
||||||
|
|
||||||
Assert.Equal(636, config.LdapPort);
|
// DefaultFormat is always "json" unless overridden by config file or env var
|
||||||
Assert.True(config.LdapUseTls);
|
|
||||||
Assert.Equal("json", config.DefaultFormat);
|
Assert.Equal("json", config.DefaultFormat);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
Environment.SetEnvironmentVariable("SCADALINK_CONTACT_POINTS", origContact);
|
Environment.SetEnvironmentVariable("SCADALINK_MANAGEMENT_URL", origUrl);
|
||||||
Environment.SetEnvironmentVariable("SCADALINK_LDAP_SERVER", origLdap);
|
|
||||||
Environment.SetEnvironmentVariable("SCADALINK_FORMAT", origFormat);
|
Environment.SetEnvironmentVariable("SCADALINK_FORMAT", origFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Load_ContactPoints_FromEnvironment()
|
public void Load_ManagementUrl_FromEnvironment()
|
||||||
{
|
{
|
||||||
var orig = Environment.GetEnvironmentVariable("SCADALINK_CONTACT_POINTS");
|
var orig = Environment.GetEnvironmentVariable("SCADALINK_MANAGEMENT_URL");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Environment.SetEnvironmentVariable("SCADALINK_CONTACT_POINTS", "host1:8080,host2:8080");
|
Environment.SetEnvironmentVariable("SCADALINK_MANAGEMENT_URL", "http://central:5000");
|
||||||
|
|
||||||
var config = CliConfig.Load();
|
var config = CliConfig.Load();
|
||||||
|
|
||||||
Assert.Equal(2, config.ContactPoints.Count);
|
Assert.Equal("http://central:5000", config.ManagementUrl);
|
||||||
Assert.Equal("host1:8080", config.ContactPoints[0]);
|
|
||||||
Assert.Equal("host2:8080", config.ContactPoints[1]);
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
Environment.SetEnvironmentVariable("SCADALINK_CONTACT_POINTS", orig);
|
Environment.SetEnvironmentVariable("SCADALINK_MANAGEMENT_URL", orig);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Load_LdapServer_FromEnvironment()
|
|
||||||
{
|
|
||||||
var orig = Environment.GetEnvironmentVariable("SCADALINK_LDAP_SERVER");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Environment.SetEnvironmentVariable("SCADALINK_LDAP_SERVER", "ldap.example.com");
|
|
||||||
|
|
||||||
var config = CliConfig.Load();
|
|
||||||
|
|
||||||
Assert.Equal("ldap.example.com", config.LdapServer);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Environment.SetEnvironmentVariable("SCADALINK_LDAP_SERVER", orig);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
|
using ScadaLink.CLI;
|
||||||
using ScadaLink.CLI.Commands;
|
using ScadaLink.CLI.Commands;
|
||||||
using ScadaLink.Commons.Messages.Management;
|
|
||||||
|
|
||||||
namespace ScadaLink.CLI.Tests;
|
namespace ScadaLink.CLI.Tests;
|
||||||
|
|
||||||
public class CommandHelpersTests
|
public class CommandHelpersTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void HandleResponse_ManagementSuccess_JsonFormat_ReturnsZero()
|
public void HandleResponse_Success_JsonFormat_ReturnsZero()
|
||||||
{
|
{
|
||||||
var writer = new StringWriter();
|
var writer = new StringWriter();
|
||||||
Console.SetOut(writer);
|
Console.SetOut(writer);
|
||||||
|
|
||||||
var response = new ManagementSuccess("corr-1", "{\"id\":1,\"name\":\"test\"}");
|
var response = new ManagementResponse(200, "{\"id\":1,\"name\":\"test\"}", null, null);
|
||||||
var exitCode = CommandHelpers.HandleResponse(response, "json");
|
var exitCode = CommandHelpers.HandleResponse(response, "json");
|
||||||
|
|
||||||
Assert.Equal(0, exitCode);
|
Assert.Equal(0, exitCode);
|
||||||
@@ -21,13 +21,13 @@ public class CommandHelpersTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void HandleResponse_ManagementSuccess_TableFormat_ArrayData_ReturnsZero()
|
public void HandleResponse_Success_TableFormat_ArrayData_ReturnsZero()
|
||||||
{
|
{
|
||||||
var writer = new StringWriter();
|
var writer = new StringWriter();
|
||||||
Console.SetOut(writer);
|
Console.SetOut(writer);
|
||||||
|
|
||||||
var json = "[{\"Id\":1,\"Name\":\"Alpha\"},{\"Id\":2,\"Name\":\"Beta\"}]";
|
var json = "[{\"Id\":1,\"Name\":\"Alpha\"},{\"Id\":2,\"Name\":\"Beta\"}]";
|
||||||
var response = new ManagementSuccess("corr-1", json);
|
var response = new ManagementResponse(200, json, null, null);
|
||||||
var exitCode = CommandHelpers.HandleResponse(response, "table");
|
var exitCode = CommandHelpers.HandleResponse(response, "table");
|
||||||
|
|
||||||
Assert.Equal(0, exitCode);
|
Assert.Equal(0, exitCode);
|
||||||
@@ -41,13 +41,13 @@ public class CommandHelpersTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void HandleResponse_ManagementSuccess_TableFormat_ObjectData_ReturnsZero()
|
public void HandleResponse_Success_TableFormat_ObjectData_ReturnsZero()
|
||||||
{
|
{
|
||||||
var writer = new StringWriter();
|
var writer = new StringWriter();
|
||||||
Console.SetOut(writer);
|
Console.SetOut(writer);
|
||||||
|
|
||||||
var json = "{\"Id\":1,\"Name\":\"Alpha\",\"Status\":\"Active\"}";
|
var json = "{\"Id\":1,\"Name\":\"Alpha\",\"Status\":\"Active\"}";
|
||||||
var response = new ManagementSuccess("corr-1", json);
|
var response = new ManagementResponse(200, json, null, null);
|
||||||
var exitCode = CommandHelpers.HandleResponse(response, "table");
|
var exitCode = CommandHelpers.HandleResponse(response, "table");
|
||||||
|
|
||||||
Assert.Equal(0, exitCode);
|
Assert.Equal(0, exitCode);
|
||||||
@@ -61,12 +61,12 @@ public class CommandHelpersTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void HandleResponse_ManagementSuccess_TableFormat_EmptyArray_ShowsNoResults()
|
public void HandleResponse_Success_TableFormat_EmptyArray_ShowsNoResults()
|
||||||
{
|
{
|
||||||
var writer = new StringWriter();
|
var writer = new StringWriter();
|
||||||
Console.SetOut(writer);
|
Console.SetOut(writer);
|
||||||
|
|
||||||
var response = new ManagementSuccess("corr-1", "[]");
|
var response = new ManagementResponse(200, "[]", null, null);
|
||||||
var exitCode = CommandHelpers.HandleResponse(response, "table");
|
var exitCode = CommandHelpers.HandleResponse(response, "table");
|
||||||
|
|
||||||
Assert.Equal(0, exitCode);
|
Assert.Equal(0, exitCode);
|
||||||
@@ -76,12 +76,12 @@ public class CommandHelpersTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void HandleResponse_ManagementError_ReturnsOne()
|
public void HandleResponse_Error_ReturnsOne()
|
||||||
{
|
{
|
||||||
var errWriter = new StringWriter();
|
var errWriter = new StringWriter();
|
||||||
Console.SetError(errWriter);
|
Console.SetError(errWriter);
|
||||||
|
|
||||||
var response = new ManagementError("corr-1", "Something failed", "FAIL_CODE");
|
var response = new ManagementResponse(400, null, "Something failed", "FAIL_CODE");
|
||||||
var exitCode = CommandHelpers.HandleResponse(response, "json");
|
var exitCode = CommandHelpers.HandleResponse(response, "json");
|
||||||
|
|
||||||
Assert.Equal(1, exitCode);
|
Assert.Equal(1, exitCode);
|
||||||
@@ -91,12 +91,12 @@ public class CommandHelpersTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void HandleResponse_ManagementUnauthorized_ReturnsTwo()
|
public void HandleResponse_Unauthorized_ReturnsTwo()
|
||||||
{
|
{
|
||||||
var errWriter = new StringWriter();
|
var errWriter = new StringWriter();
|
||||||
Console.SetError(errWriter);
|
Console.SetError(errWriter);
|
||||||
|
|
||||||
var response = new ManagementUnauthorized("corr-1", "Access denied");
|
var response = new ManagementResponse(403, null, "Access denied", "UNAUTHORIZED");
|
||||||
var exitCode = CommandHelpers.HandleResponse(response, "json");
|
var exitCode = CommandHelpers.HandleResponse(response, "json");
|
||||||
|
|
||||||
Assert.Equal(2, exitCode);
|
Assert.Equal(2, exitCode);
|
||||||
@@ -106,26 +106,47 @@ public class CommandHelpersTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void HandleResponse_UnexpectedType_ReturnsOne()
|
public void HandleResponse_AuthFailure_ReturnsOne()
|
||||||
{
|
{
|
||||||
var errWriter = new StringWriter();
|
var errWriter = new StringWriter();
|
||||||
Console.SetError(errWriter);
|
Console.SetError(errWriter);
|
||||||
|
|
||||||
var exitCode = CommandHelpers.HandleResponse("unexpected", "json");
|
var response = new ManagementResponse(401, null, "Invalid credentials", "AUTH_FAILED");
|
||||||
|
var exitCode = CommandHelpers.HandleResponse(response, "json");
|
||||||
|
|
||||||
Assert.Equal(1, exitCode);
|
Assert.Equal(1, exitCode);
|
||||||
Assert.Contains("Unexpected response type", errWriter.ToString());
|
Assert.Contains("Invalid credentials", errWriter.ToString());
|
||||||
|
|
||||||
Console.SetError(new StreamWriter(Console.OpenStandardError()) { AutoFlush = true });
|
Console.SetError(new StreamWriter(Console.OpenStandardError()) { AutoFlush = true });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void NewCorrelationId_ReturnsNonEmpty32CharHex()
|
public void HandleResponse_ConnectionFailure_ReturnsOne()
|
||||||
{
|
{
|
||||||
var id = CommandHelpers.NewCorrelationId();
|
var errWriter = new StringWriter();
|
||||||
|
Console.SetError(errWriter);
|
||||||
|
|
||||||
Assert.NotNull(id);
|
var response = new ManagementResponse(0, null, "Connection failed: No such host", "CONNECTION_FAILED");
|
||||||
Assert.Equal(32, id.Length);
|
var exitCode = CommandHelpers.HandleResponse(response, "json");
|
||||||
Assert.True(id.All(c => "0123456789abcdef".Contains(c)));
|
|
||||||
|
Assert.Equal(1, exitCode);
|
||||||
|
Assert.Contains("Connection failed", errWriter.ToString());
|
||||||
|
|
||||||
|
Console.SetError(new StreamWriter(Console.OpenStandardError()) { AutoFlush = true });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void HandleResponse_Timeout_ReturnsOne()
|
||||||
|
{
|
||||||
|
var errWriter = new StringWriter();
|
||||||
|
Console.SetError(errWriter);
|
||||||
|
|
||||||
|
var response = new ManagementResponse(504, null, "Request timed out.", "TIMEOUT");
|
||||||
|
var exitCode = CommandHelpers.HandleResponse(response, "json");
|
||||||
|
|
||||||
|
Assert.Equal(1, exitCode);
|
||||||
|
Assert.Contains("timed out", errWriter.ToString());
|
||||||
|
|
||||||
|
Console.SetError(new StreamWriter(Console.OpenStandardError()) { AutoFlush = true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,6 +157,8 @@ public class ArchitecturalConstraintTests
|
|||||||
.Where(t => t.Namespace != null
|
.Where(t => t.Namespace != null
|
||||||
&& t.Namespace.Contains(".Messages.")
|
&& t.Namespace.Contains(".Messages.")
|
||||||
&& !t.IsEnum && !t.IsInterface
|
&& !t.IsEnum && !t.IsInterface
|
||||||
|
&& !(t.IsAbstract && t.IsSealed) // exclude static classes (utilities)
|
||||||
|
&& !t.Name.StartsWith("<") // exclude compiler-generated types
|
||||||
&& (t.IsClass || (t.IsValueType && !t.IsPrimitive)));
|
&& (t.IsClass || (t.IsValueType && !t.IsPrimitive)));
|
||||||
|
|
||||||
foreach (var type in messageTypes)
|
foreach (var type in messageTypes)
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ public class MessageConventionTests
|
|||||||
&& t.Namespace.Contains(".Messages.")
|
&& t.Namespace.Contains(".Messages.")
|
||||||
&& !t.IsEnum
|
&& !t.IsEnum
|
||||||
&& !t.IsInterface
|
&& !t.IsInterface
|
||||||
|
&& !(t.IsAbstract && t.IsSealed) // exclude static classes (utilities)
|
||||||
|
&& !t.Name.StartsWith("<") // exclude compiler-generated types
|
||||||
&& (t.IsClass || (t.IsValueType && !t.IsPrimitive)));
|
&& (t.IsClass || (t.IsValueType && !t.IsPrimitive)));
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
Reference in New Issue
Block a user