docs(transport): document CLI surface, blocker-scan heuristic, Admin import gating
Reflect this session's implementation work in the Transport (#24) component spec: - New 'CLI' section covering bundle export / preview / import commands, the base64-over-JSON wire format, the 200 MB request-body cap, and the 5-minute per-command timeout. Authorization table + Interactions section updated to mention ManagementActor handlers. - Import wizard nav placement corrected from Design to Admin (already the case in code; the spec lagged). - Blocker-scan heuristic boundaries documented under Import Flow: the '.' skip, the DataSourceReference exclusion, and the KnownNonReferenceNames denylist. Both DetectBlockersAsync and RunSemanticValidationAsync Pass 1 share the filter.
This commit is contained in:
@@ -155,7 +155,7 @@ Authorization: `RequireDesign` on both the Razor page and `IBundleExporter.Expor
|
||||
|
||||
## Import Flow
|
||||
|
||||
### UI — 5-Step Wizard (Design nav group)
|
||||
### UI — 5-Step Wizard (Admin nav group)
|
||||
|
||||
**Step 1 — Upload.** Drag-and-drop or browse. On selection, the manifest is parsed and displayed (source env, exporter, timestamp, content count, SHA-256, encrypted yes/no). The manifest hash is validated against the `content` blob.
|
||||
|
||||
@@ -171,6 +171,8 @@ Bulk "Apply to all" at the top (Overwrite / Skip / Rename), overridable per row.
|
||||
|
||||
Bundle references that cannot be satisfied in either the bundle or the target DB (e.g., a template references a shared script that is neither in the bundle nor pre-existing) appear as **blocker rows** — Apply is disabled until they are resolved (typically by skipping the dependent artifact or re-exporting with dependencies).
|
||||
|
||||
**Blocker-scan heuristic boundaries.** The scanner walks `TemplateScript.Code`, `TemplateAttribute.Value`, and `ApiMethod.Script` looking for top-level `Identifier(` or `Identifier.` tokens. To keep the heuristic usable on real script bodies it (a) skips identifiers preceded by `.` (member access — `obj.Method()` does not flag `Method`); (b) does NOT scan `TemplateAttribute.DataSourceReference` (an OPC UA address path, never script source); and (c) filters out a small `KnownNonReferenceNames` denylist of .NET stdlib types (`Convert`, `DateTimeOffset`, `ToString`, `Dispose`, `UtcNow`, …), ScadaLink runtime API roots (`Notify`, `Database`, `ExternalSystem`, `Scripts`, `Instance`, `Parameters`, `Attributes`, `Route`, …), and common SQL keywords that appear inside string literals (`COUNT`, `SELECT`, `FROM`, …). Both the diff-step `DetectBlockersAsync` and the Apply-time `RunSemanticValidationAsync` Pass 1 share this filter, so the diff preview and the Apply gate agree.
|
||||
|
||||
**Step 4 — Confirm.** Final summary plus a "N instances will become stale" warning enumerating affected instances. User types the source environment name to confirm (typo-resistant gate at the prod boundary).
|
||||
|
||||
**Step 5 — Result.** Counts, link to the `BundleImported` audit row, link to the Deployments page filtered to the newly stale instances.
|
||||
@@ -264,13 +266,35 @@ Import flows through the same audited repository methods the UI and CLI use, so
|
||||
|
||||
**Transactional guarantee:** the EF transaction wraps both the entity writes and the audit log writes (existing pattern). A rollback removes both. The `BundleImported` summary row is the final write inside the transaction, so partial audit trails are impossible. A failed import emits a single `BundleImportFailed` row outside the rolled-back transaction.
|
||||
|
||||
## CLI
|
||||
|
||||
Three commands surface the same Transport operations as the Central UI wizards, designed for test automation. The bundle bytes travel as base64 inside the existing `/management` JSON envelope — no new HTTP endpoints — and the per-request body cap is raised to 200 MB to cover the 100 MB raw-bundle ceiling once base64-inflated.
|
||||
|
||||
```bash
|
||||
scadalink bundle export --output FILE --passphrase X [--all | --templates A,B ...] \
|
||||
[--shared-scripts ...] [--external-systems ...] [--db-connections ...] \
|
||||
[--notification-lists ...] [--smtp-configs ...] [--api-keys ...] \
|
||||
[--api-methods ...] [--include-dependencies] [--source-environment NAME]
|
||||
|
||||
scadalink bundle preview --input FILE --passphrase X
|
||||
# prints PreviewBundleResult JSON: per-row items + add/modified/identical/blocker counts
|
||||
|
||||
scadalink bundle import --input FILE --passphrase X [--on-conflict skip|overwrite|rename]
|
||||
# one-shot load + preview + apply with a single global policy for Modified rows
|
||||
# Identical → Skip, New → Add, Blocker → abort
|
||||
```
|
||||
|
||||
Selection uses entity **names** rather than IDs so scripts are portable across environments. The CLI per-command timeout is 5 minutes (vs the default 30 s for other commands) to comfortably cover large bundles. CLI commands route through `ManagementActor`'s `ExportBundleCommand` / `PreviewBundleCommand` / `ImportBundleCommand` handlers, which delegate to the same `IBundleExporter` / `IBundleImporter` services as the UI.
|
||||
|
||||
Exit codes follow the project convention: `0` = success, `1` = command failure (validation, blockers, wrong passphrase), `2` = authorization failure.
|
||||
|
||||
## Authorization
|
||||
|
||||
| Operation | Required role | Enforced at |
|
||||
|---|---|---|
|
||||
| Open Export page | `RequireDesign` | Razor page authorize attribute |
|
||||
| Open Export page / `bundle export` CLI | `RequireDesign` | Razor page authorize attribute + `ManagementActor.GetRequiredRole` |
|
||||
| `IBundleExporter.ExportAsync` | `RequireDesign` | Service entrypoint |
|
||||
| Open Import page | `RequireAdmin` | Razor page authorize attribute |
|
||||
| Open Import page / `bundle preview` + `bundle import` CLI | `RequireAdmin` | Razor page authorize attribute + `ManagementActor.GetRequiredRole` |
|
||||
| `IBundleImporter.LoadAsync` / `PreviewAsync` / `ApplyAsync` | `RequireAdmin` | Service entrypoint |
|
||||
| Configuration Audit Log "Bundle Import" filter | `RequireAdmin` or `Audit` | Existing audit page logic |
|
||||
|
||||
@@ -282,7 +306,8 @@ Import flows through the same audited repository methods the UI and CLI use, so
|
||||
|
||||
## Interactions
|
||||
|
||||
- **Central UI** — Hosts the Export Bundle (`/design/transport/export`) and Import Bundle (`/design/transport/import`) wizard pages under the Design nav group. The result page links to the Deployments page and to the filtered Configuration Audit Log Viewer.
|
||||
- **Central UI** — Hosts the Export Bundle (`/design/transport/export`) page under the Design nav group and the Import Bundle (`/design/transport/import`) page under the Admin nav group. The import result page links to the Deployments page and to the filtered Configuration Audit Log Viewer.
|
||||
- **Management Service / CLI** — `ManagementActor` registers three Transport command handlers (`ExportBundleCommand`, `PreviewBundleCommand`, `ImportBundleCommand`) and the CLI ships `bundle export` / `bundle preview` / `bundle import` subcommands. Bundle bytes ride the existing `/management` JSON envelope as base64.
|
||||
- **Deployment Manager** — Never directly invoked by Transport. Transport-driven template changes propagate to deployed instances through the existing revision-hash drift detection in `DeploymentService.CompareAsync`; the Deployments page surfaces affected instances as stale automatically.
|
||||
- **Security & Auth** — Provides `RequireDesign` and `RequireAdmin` policies from `ScadaLink.Security`, enforced at both the page and service layers.
|
||||
- **Audit Log (Configuration)** — Writes `BundleExported` / `BundleImported` / `BundleImportFailed` / `UnencryptedBundleExport` / `BundleImportUnlockFailed` rows via `IAuditService`, plus per-import name-resolution warnings `BundleImportAlarmScriptUnresolved` and `BundleImportCompositionUnresolved`; per-entity rows from audited repositories are correlated by `BundleImportId` via `IAuditCorrelationContext`.
|
||||
|
||||
Reference in New Issue
Block a user