refactor: rename ScadaLink → ZB.MOM.WW.ScadaBridge (code + projects + namespaces)
Solution + 23 src projects + 26 test projects renamed; folders, csproj, namespaces, and ScadaLinkDbContext/ScadaBridgeDbContext class updated. ActorSystem "scadalink" → "scadabridge", Akka seed-node URLs migrated. SQL roles/logins, LDAP domains, CLI command name, and CLI config dir (~/.scadalink → ~/.scadabridge) also renamed. Build green; 5 Host.Tests fail awaiting SQL login rename in next commit. Pre-existing StaleTagMonitor timing flakes unchanged. Rename script committed at tools/rename-to-scadabridge.sh.
This commit is contained in:
+173
-173
@@ -4,19 +4,19 @@
|
||||
|
||||
**Goal:** Ship Component #24 (Transport) — file-based, encrypted bundle export/import of templates, system artifacts, and central-only configuration via the Central UI — as a single shipping slice.
|
||||
|
||||
**Architecture:** New `ScadaLink.Transport` project peer to Template Engine + Deployment Manager. Two new Central UI pages under the Design nav group. Persistence flows through existing audited repositories with a new scoped `IAuditCorrelationContext` carrying `BundleImportId`. AES-256-GCM + PBKDF2-SHA256 (600 000 iterations) for content encryption. No site changes.
|
||||
**Architecture:** New `ZB.MOM.WW.ScadaBridge.Transport` project peer to Template Engine + Deployment Manager. Two new Central UI pages under the Design nav group. Persistence flows through existing audited repositories with a new scoped `IAuditCorrelationContext` carrying `BundleImportId`. AES-256-GCM + PBKDF2-SHA256 (600 000 iterations) for content encryption. No site changes.
|
||||
|
||||
**Tech Stack:** .NET 10, EF Core, ASP.NET Core / Blazor Server, xUnit + NSubstitute + FluentAssertions, in-memory EF for integration tests, existing `TreeView.razor` component extended with a checkbox-selection mode.
|
||||
|
||||
**Source design doc:** `docs/plans/2026-05-24-transport-design.md` (already committed).
|
||||
|
||||
**Key codebase facts that shape this plan:**
|
||||
- Audit entity is `AuditLogEntry` (`src/ScadaLink.Commons/Entities/Audit/AuditLogEntry.cs`), DbSet `_context.AuditLogEntries`.
|
||||
- Audit entity is `AuditLogEntry` (`src/ZB.MOM.WW.ScadaBridge.Commons/Entities/Audit/AuditLogEntry.cs`), DbSet `_context.AuditLogEntries`.
|
||||
- `IAuditService.LogAsync(user, action, entityType, entityId, entityName, afterState, ct)` — no existing correlation parameter; we add a scoped `IAuditCorrelationContext` rather than changing every repository signature.
|
||||
- **No persisted `DeployedRevisionHash` on Instance.** `DeploymentService.CompareAsync` computes stale-vs-fresh live by comparing `snapshot.RevisionHash` to the current flattened-config hash. Overwriting a template naturally changes the hash, so the Deployments page surfaces affected instances automatically — **no explicit stale-mark write is required.**
|
||||
- `TreeView.razor` exists at `src/ScadaLink.CentralUI/Components/Shared/TreeView.razor` with single-select navigation. We extend it with a checkbox / multi-select mode rather than building a parallel widget.
|
||||
- DI extension convention: `AddTransport()` in `ServiceCollectionExtensions.cs`, registered in `src/ScadaLink.Host/Program.cs` under "Central-only components".
|
||||
- Test integration pattern: `ScadaLinkWebApplicationFactory : WebApplicationFactory<Program>` with in-memory EF.
|
||||
- `TreeView.razor` exists at `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/TreeView.razor` with single-select navigation. We extend it with a checkbox / multi-select mode rather than building a parallel widget.
|
||||
- DI extension convention: `AddTransport()` in `ServiceCollectionExtensions.cs`, registered in `src/ZB.MOM.WW.ScadaBridge.Host/Program.cs` under "Central-only components".
|
||||
- Test integration pattern: `ScadaBridgeWebApplicationFactory : WebApplicationFactory<Program>` with in-memory EF.
|
||||
|
||||
**No feature flag** — backward compatible additive change; users who never open the new pages see no behavior difference.
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
| 3 | EF migration `AddBundleImportIdToAuditLog` | trivial | ~2 min |
|
||||
| 4 | `IAuditCorrelationContext` scoped service | small | ~3 min |
|
||||
| 5 | `AuditService` reads correlation context | small | ~3 min |
|
||||
| 6 | `ScadaLink.Transport` project skeleton + slnx + DI | small | ~3 min |
|
||||
| 6 | `ZB.MOM.WW.ScadaBridge.Transport` project skeleton + slnx + DI | small | ~3 min |
|
||||
| 7 | `TransportOptions` | trivial | ~2 min |
|
||||
| 8 | `BundleSecretEncryptor` (AES-256-GCM + PBKDF2) + tests | standard | ~4 min |
|
||||
| 9 | `ManifestBuilder` + manifest schema validation | standard | ~4 min |
|
||||
@@ -66,16 +66,16 @@
|
||||
**Parallelizable with:** Task 1, Task 2, Task 4, Task 19
|
||||
|
||||
**Files:**
|
||||
- Create: `src/ScadaLink.Commons/Types/Transport/BundleManifest.cs`
|
||||
- Create: `src/ScadaLink.Commons/Types/Transport/BundleSummary.cs`
|
||||
- Create: `src/ScadaLink.Commons/Types/Transport/EncryptionMetadata.cs`
|
||||
- Create: `src/ScadaLink.Commons/Types/Transport/ManifestContentEntry.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/BundleManifest.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/BundleSummary.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/EncryptionMetadata.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/ManifestContentEntry.cs`
|
||||
|
||||
**Step 1: Write the DTOs**
|
||||
|
||||
`BundleManifest.cs`:
|
||||
```csharp
|
||||
namespace ScadaLink.Commons.Types.Transport;
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Transport;
|
||||
|
||||
public sealed record BundleManifest(
|
||||
int BundleFormatVersion,
|
||||
@@ -83,7 +83,7 @@ public sealed record BundleManifest(
|
||||
DateTimeOffset CreatedAtUtc,
|
||||
string SourceEnvironment,
|
||||
string ExportedBy,
|
||||
string ScadaLinkVersion,
|
||||
string ScadaBridgeVersion,
|
||||
string ContentHash,
|
||||
EncryptionMetadata? Encryption,
|
||||
BundleSummary Summary,
|
||||
@@ -92,7 +92,7 @@ public sealed record BundleManifest(
|
||||
|
||||
`EncryptionMetadata.cs`:
|
||||
```csharp
|
||||
namespace ScadaLink.Commons.Types.Transport;
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Transport;
|
||||
|
||||
public sealed record EncryptionMetadata(
|
||||
string Algorithm, // "AES-256-GCM"
|
||||
@@ -104,7 +104,7 @@ public sealed record EncryptionMetadata(
|
||||
|
||||
`BundleSummary.cs`:
|
||||
```csharp
|
||||
namespace ScadaLink.Commons.Types.Transport;
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Transport;
|
||||
|
||||
public sealed record BundleSummary(
|
||||
int Templates,
|
||||
@@ -120,7 +120,7 @@ public sealed record BundleSummary(
|
||||
|
||||
`ManifestContentEntry.cs`:
|
||||
```csharp
|
||||
namespace ScadaLink.Commons.Types.Transport;
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Transport;
|
||||
|
||||
public sealed record ManifestContentEntry(
|
||||
string Type,
|
||||
@@ -131,13 +131,13 @@ public sealed record ManifestContentEntry(
|
||||
|
||||
**Step 2: Build**
|
||||
|
||||
Run: `dotnet build src/ScadaLink.Commons/ScadaLink.Commons.csproj`
|
||||
Run: `dotnet build src/ZB.MOM.WW.ScadaBridge.Commons/ZB.MOM.WW.ScadaBridge.Commons.csproj`
|
||||
Expected: build succeeds.
|
||||
|
||||
**Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ScadaLink.Commons/Types/Transport/
|
||||
git add src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/
|
||||
git commit -m "feat(transport): add bundle manifest DTOs in Commons"
|
||||
```
|
||||
|
||||
@@ -150,21 +150,21 @@ git commit -m "feat(transport): add bundle manifest DTOs in Commons"
|
||||
**Parallelizable with:** Task 0, Task 2, Task 4, Task 19
|
||||
|
||||
**Files:**
|
||||
- Create: `src/ScadaLink.Commons/Types/Transport/ExportSelection.cs`
|
||||
- Create: `src/ScadaLink.Commons/Types/Transport/ImportPreview.cs`
|
||||
- Create: `src/ScadaLink.Commons/Types/Transport/ImportResolution.cs`
|
||||
- Create: `src/ScadaLink.Commons/Types/Transport/ImportResult.cs`
|
||||
- Create: `src/ScadaLink.Commons/Types/Transport/BundleSession.cs`
|
||||
- Create: `src/ScadaLink.Commons/Interfaces/Transport/IBundleExporter.cs`
|
||||
- Create: `src/ScadaLink.Commons/Interfaces/Transport/IBundleImporter.cs`
|
||||
- Create: `src/ScadaLink.Commons/Interfaces/Transport/IBundleSessionStore.cs`
|
||||
- Create: `src/ScadaLink.Commons/Interfaces/Transport/IAuditCorrelationContext.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/ExportSelection.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/ImportPreview.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/ImportResolution.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/ImportResult.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/BundleSession.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Commons/Interfaces/Transport/IBundleExporter.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Commons/Interfaces/Transport/IBundleImporter.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Commons/Interfaces/Transport/IBundleSessionStore.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Commons/Interfaces/Transport/IAuditCorrelationContext.cs`
|
||||
|
||||
**Step 1: Write the types and interfaces**
|
||||
|
||||
`ExportSelection.cs`:
|
||||
```csharp
|
||||
namespace ScadaLink.Commons.Types.Transport;
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Transport;
|
||||
|
||||
public sealed record ExportSelection(
|
||||
IReadOnlyList<int> TemplateIds,
|
||||
@@ -180,7 +180,7 @@ public sealed record ExportSelection(
|
||||
|
||||
`ImportPreview.cs`:
|
||||
```csharp
|
||||
namespace ScadaLink.Commons.Types.Transport;
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Transport;
|
||||
|
||||
public enum ConflictKind { Identical, Modified, New, Blocker }
|
||||
|
||||
@@ -200,7 +200,7 @@ public sealed record ImportPreview(
|
||||
|
||||
`ImportResolution.cs`:
|
||||
```csharp
|
||||
namespace ScadaLink.Commons.Types.Transport;
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Transport;
|
||||
|
||||
public enum ResolutionAction { Add, Overwrite, Skip, Rename }
|
||||
|
||||
@@ -213,7 +213,7 @@ public sealed record ImportResolution(
|
||||
|
||||
`ImportResult.cs`:
|
||||
```csharp
|
||||
namespace ScadaLink.Commons.Types.Transport;
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Transport;
|
||||
|
||||
public sealed record ImportResult(
|
||||
Guid BundleImportId,
|
||||
@@ -227,7 +227,7 @@ public sealed record ImportResult(
|
||||
|
||||
`BundleSession.cs`:
|
||||
```csharp
|
||||
namespace ScadaLink.Commons.Types.Transport;
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Transport;
|
||||
|
||||
public sealed class BundleSession
|
||||
{
|
||||
@@ -242,9 +242,9 @@ public sealed class BundleSession
|
||||
|
||||
`IBundleExporter.cs`:
|
||||
```csharp
|
||||
using ScadaLink.Commons.Types.Transport;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Transport;
|
||||
|
||||
namespace ScadaLink.Commons.Interfaces.Transport;
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Transport;
|
||||
|
||||
public interface IBundleExporter
|
||||
{
|
||||
@@ -259,9 +259,9 @@ public interface IBundleExporter
|
||||
|
||||
`IBundleImporter.cs`:
|
||||
```csharp
|
||||
using ScadaLink.Commons.Types.Transport;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Transport;
|
||||
|
||||
namespace ScadaLink.Commons.Interfaces.Transport;
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Transport;
|
||||
|
||||
public interface IBundleImporter
|
||||
{
|
||||
@@ -277,9 +277,9 @@ public interface IBundleImporter
|
||||
|
||||
`IBundleSessionStore.cs`:
|
||||
```csharp
|
||||
using ScadaLink.Commons.Types.Transport;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Transport;
|
||||
|
||||
namespace ScadaLink.Commons.Interfaces.Transport;
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Transport;
|
||||
|
||||
public interface IBundleSessionStore
|
||||
{
|
||||
@@ -292,7 +292,7 @@ public interface IBundleSessionStore
|
||||
|
||||
`IAuditCorrelationContext.cs`:
|
||||
```csharp
|
||||
namespace ScadaLink.Commons.Interfaces.Transport;
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Transport;
|
||||
|
||||
/// <summary>
|
||||
/// Scoped service the bundle importer sets to thread a BundleImportId through to
|
||||
@@ -307,13 +307,13 @@ public interface IAuditCorrelationContext
|
||||
|
||||
**Step 2: Build**
|
||||
|
||||
Run: `dotnet build src/ScadaLink.Commons/ScadaLink.Commons.csproj`
|
||||
Run: `dotnet build src/ZB.MOM.WW.ScadaBridge.Commons/ZB.MOM.WW.ScadaBridge.Commons.csproj`
|
||||
Expected: build succeeds.
|
||||
|
||||
**Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ScadaLink.Commons/Types/Transport/ src/ScadaLink.Commons/Interfaces/Transport/
|
||||
git add src/ZB.MOM.WW.ScadaBridge.Commons/Types/Transport/ src/ZB.MOM.WW.ScadaBridge.Commons/Interfaces/Transport/
|
||||
git commit -m "feat(transport): add IBundleExporter / IBundleImporter interfaces"
|
||||
```
|
||||
|
||||
@@ -326,8 +326,8 @@ git commit -m "feat(transport): add IBundleExporter / IBundleImporter interfaces
|
||||
**Parallelizable with:** Task 0, Task 1, Task 19
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ScadaLink.Commons/Entities/Audit/AuditLogEntry.cs` (add nullable `BundleImportId`)
|
||||
- Modify: `src/ScadaLink.ConfigurationDatabase/ScadaLinkDbContext.cs` (add Fluent config in `OnModelCreating` near the existing `AuditLogEntry` mapping; add `IX_AuditLogEntries_BundleImportId` non-clustered index)
|
||||
- Modify: `src/ZB.MOM.WW.ScadaBridge.Commons/Entities/Audit/AuditLogEntry.cs` (add nullable `BundleImportId`)
|
||||
- Modify: `src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/ScadaBridgeDbContext.cs` (add Fluent config in `OnModelCreating` near the existing `AuditLogEntry` mapping; add `IX_AuditLogEntries_BundleImportId` non-clustered index)
|
||||
|
||||
**Step 1: Add property**
|
||||
|
||||
@@ -338,7 +338,7 @@ public Guid? BundleImportId { get; set; }
|
||||
|
||||
**Step 2: Add Fluent config + index**
|
||||
|
||||
In `ScadaLinkDbContext.OnModelCreating`, find the existing `modelBuilder.Entity<AuditLogEntry>(...)` block and add:
|
||||
In `ScadaBridgeDbContext.OnModelCreating`, find the existing `modelBuilder.Entity<AuditLogEntry>(...)` block and add:
|
||||
```csharp
|
||||
e.HasIndex(x => x.BundleImportId).HasDatabaseName("IX_AuditLogEntries_BundleImportId");
|
||||
```
|
||||
@@ -347,14 +347,14 @@ If no existing block, add one near other audit configs.
|
||||
|
||||
**Step 3: Build**
|
||||
|
||||
Run: `dotnet build src/ScadaLink.ConfigurationDatabase/ScadaLink.ConfigurationDatabase.csproj`
|
||||
Run: `dotnet build src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.csproj`
|
||||
Expected: build succeeds.
|
||||
|
||||
**Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ScadaLink.Commons/Entities/Audit/AuditLogEntry.cs \
|
||||
src/ScadaLink.ConfigurationDatabase/ScadaLinkDbContext.cs
|
||||
git add src/ZB.MOM.WW.ScadaBridge.Commons/Entities/Audit/AuditLogEntry.cs \
|
||||
src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/ScadaBridgeDbContext.cs
|
||||
git commit -m "feat(transport): add BundleImportId column on AuditLogEntry"
|
||||
```
|
||||
|
||||
@@ -367,16 +367,16 @@ git commit -m "feat(transport): add BundleImportId column on AuditLogEntry"
|
||||
**Parallelizable with:** none (depends on Task 2)
|
||||
|
||||
**Files:**
|
||||
- Create: `src/ScadaLink.ConfigurationDatabase/Migrations/<timestamp>_AddBundleImportIdToAuditLog.cs` (generated)
|
||||
- Create: `src/ScadaLink.ConfigurationDatabase/Migrations/<timestamp>_AddBundleImportIdToAuditLog.Designer.cs` (generated)
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Migrations/<timestamp>_AddBundleImportIdToAuditLog.cs` (generated)
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Migrations/<timestamp>_AddBundleImportIdToAuditLog.Designer.cs` (generated)
|
||||
|
||||
**Step 1: Generate**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
dotnet ef migrations add AddBundleImportIdToAuditLog \
|
||||
-p src/ScadaLink.ConfigurationDatabase \
|
||||
-s src/ScadaLink.Host
|
||||
-p src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase \
|
||||
-s src/ZB.MOM.WW.ScadaBridge.Host
|
||||
```
|
||||
Expected: two files generated under `Migrations/` with naming pattern `yyyyMMddHHmmss_AddBundleImportIdToAuditLog.cs`.
|
||||
|
||||
@@ -390,13 +390,13 @@ If the generated migration includes anything else (drift from other entity chang
|
||||
|
||||
**Step 3: Build**
|
||||
|
||||
Run: `dotnet build src/ScadaLink.ConfigurationDatabase/ScadaLink.ConfigurationDatabase.csproj`
|
||||
Run: `dotnet build src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.csproj`
|
||||
Expected: build succeeds.
|
||||
|
||||
**Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ScadaLink.ConfigurationDatabase/Migrations/
|
||||
git add src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Migrations/
|
||||
git commit -m "feat(transport): EF migration AddBundleImportIdToAuditLog"
|
||||
```
|
||||
|
||||
@@ -409,16 +409,16 @@ git commit -m "feat(transport): EF migration AddBundleImportIdToAuditLog"
|
||||
**Parallelizable with:** Task 0, Task 1, Task 2, Task 19
|
||||
|
||||
**Files:**
|
||||
- Create: `src/ScadaLink.ConfigurationDatabase/Services/AuditCorrelationContext.cs`
|
||||
- Modify: `src/ScadaLink.ConfigurationDatabase/ServiceCollectionExtensions.cs` (add scoped registration; if no file, create alongside existing convention used by audit service)
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Services/AuditCorrelationContext.cs`
|
||||
- Modify: `src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/ServiceCollectionExtensions.cs` (add scoped registration; if no file, create alongside existing convention used by audit service)
|
||||
|
||||
**Step 1: Write the implementation**
|
||||
|
||||
`AuditCorrelationContext.cs`:
|
||||
```csharp
|
||||
using ScadaLink.Commons.Interfaces.Transport;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Transport;
|
||||
|
||||
namespace ScadaLink.ConfigurationDatabase.Services;
|
||||
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Per-scope mutable holder for the active bundle import id. AuditService reads it
|
||||
@@ -434,22 +434,22 @@ public sealed class AuditCorrelationContext : IAuditCorrelationContext
|
||||
|
||||
**Step 2: Register**
|
||||
|
||||
In `src/ScadaLink.ConfigurationDatabase/ServiceCollectionExtensions.cs` (the `AddConfigurationDatabase()` method), add:
|
||||
In `src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/ServiceCollectionExtensions.cs` (the `AddConfigurationDatabase()` method), add:
|
||||
```csharp
|
||||
services.AddScoped<IAuditCorrelationContext, AuditCorrelationContext>();
|
||||
```
|
||||
If a `ServiceCollectionExtensions.cs` doesn't exist in `ScadaLink.ConfigurationDatabase`, look at where `IAuditService` is currently registered (search the repo for `AddScoped<IAuditService`) and add the registration adjacent to it.
|
||||
If a `ServiceCollectionExtensions.cs` doesn't exist in `ZB.MOM.WW.ScadaBridge.ConfigurationDatabase`, look at where `IAuditService` is currently registered (search the repo for `AddScoped<IAuditService`) and add the registration adjacent to it.
|
||||
|
||||
**Step 3: Build**
|
||||
|
||||
Run: `dotnet build src/ScadaLink.ConfigurationDatabase/ScadaLink.ConfigurationDatabase.csproj`
|
||||
Run: `dotnet build src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.csproj`
|
||||
Expected: build succeeds.
|
||||
|
||||
**Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ScadaLink.ConfigurationDatabase/Services/AuditCorrelationContext.cs \
|
||||
src/ScadaLink.ConfigurationDatabase/ServiceCollectionExtensions.cs
|
||||
git add src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Services/AuditCorrelationContext.cs \
|
||||
src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/ServiceCollectionExtensions.cs
|
||||
git commit -m "feat(transport): add IAuditCorrelationContext scoped service"
|
||||
```
|
||||
|
||||
@@ -462,8 +462,8 @@ git commit -m "feat(transport): add IAuditCorrelationContext scoped service"
|
||||
**Parallelizable with:** none (depends on Tasks 2 + 4)
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ScadaLink.ConfigurationDatabase/Services/AuditService.cs`
|
||||
- Modify: `tests/ScadaLink.Commons.Tests/AuditServiceTests.cs` (if exists) OR create `tests/ScadaLink.ConfigurationDatabase.Tests/AuditServiceTests.cs`
|
||||
- Modify: `src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Services/AuditService.cs`
|
||||
- Modify: `tests/ZB.MOM.WW.ScadaBridge.Commons.Tests/AuditServiceTests.cs` (if exists) OR create `tests/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests/AuditServiceTests.cs`
|
||||
|
||||
**Step 1: Write the failing test first**
|
||||
|
||||
@@ -471,7 +471,7 @@ Add test that sets `IAuditCorrelationContext.BundleImportId`, calls `IAuditServi
|
||||
|
||||
**Step 2: Run to verify it fails**
|
||||
|
||||
Run: `dotnet test tests/ScadaLink.ConfigurationDatabase.Tests/`
|
||||
Run: `dotnet test tests/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests/`
|
||||
Expected: FAIL (correlation not yet wired through).
|
||||
|
||||
**Step 3: Implement**
|
||||
@@ -482,36 +482,36 @@ In `AuditService.cs`:
|
||||
|
||||
**Step 4: Run tests**
|
||||
|
||||
Run: `dotnet test tests/ScadaLink.ConfigurationDatabase.Tests/`
|
||||
Run: `dotnet test tests/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests/`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ScadaLink.ConfigurationDatabase/Services/AuditService.cs \
|
||||
tests/ScadaLink.ConfigurationDatabase.Tests/
|
||||
git add src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Services/AuditService.cs \
|
||||
tests/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests/
|
||||
git commit -m "feat(transport): AuditService stamps BundleImportId from correlation context"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 6: `ScadaLink.Transport` project skeleton
|
||||
## Task 6: `ZB.MOM.WW.ScadaBridge.Transport` project skeleton
|
||||
|
||||
**Classification:** small
|
||||
**Estimated implement time:** ~3 min
|
||||
**Parallelizable with:** none (subsequent tasks depend on the project existing)
|
||||
|
||||
**Files:**
|
||||
- Create: `src/ScadaLink.Transport/ScadaLink.Transport.csproj`
|
||||
- Create: `src/ScadaLink.Transport/ServiceCollectionExtensions.cs`
|
||||
- Modify: `ScadaLink.slnx` (add `<Project Path="src/ScadaLink.Transport/ScadaLink.Transport.csproj" />` in alphabetical position)
|
||||
- Create: `tests/ScadaLink.Transport.Tests/ScadaLink.Transport.Tests.csproj`
|
||||
- Create: `tests/ScadaLink.Transport.IntegrationTests/ScadaLink.Transport.IntegrationTests.csproj`
|
||||
- Modify: `ScadaLink.slnx` (add the two test projects)
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Transport/ZB.MOM.WW.ScadaBridge.Transport.csproj`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Transport/ServiceCollectionExtensions.cs`
|
||||
- Modify: `ZB.MOM.WW.ScadaBridge.slnx` (add `<Project Path="src/ZB.MOM.WW.ScadaBridge.Transport/ZB.MOM.WW.ScadaBridge.Transport.csproj" />` in alphabetical position)
|
||||
- Create: `tests/ZB.MOM.WW.ScadaBridge.Transport.Tests/ZB.MOM.WW.ScadaBridge.Transport.Tests.csproj`
|
||||
- Create: `tests/ZB.MOM.WW.ScadaBridge.Transport.IntegrationTests/ZB.MOM.WW.ScadaBridge.Transport.IntegrationTests.csproj`
|
||||
- Modify: `ZB.MOM.WW.ScadaBridge.slnx` (add the two test projects)
|
||||
|
||||
**Step 1: Write the csproj**
|
||||
|
||||
`ScadaLink.Transport.csproj`:
|
||||
`ZB.MOM.WW.ScadaBridge.Transport.csproj`:
|
||||
```xml
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
@@ -521,13 +521,13 @@ git commit -m "feat(transport): AuditService stamps BundleImportId from correlat
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../ScadaLink.Commons/ScadaLink.Commons.csproj" />
|
||||
<ProjectReference Include="../ScadaLink.ConfigurationDatabase/ScadaLink.ConfigurationDatabase.csproj" />
|
||||
<ProjectReference Include="../ScadaLink.TemplateEngine/ScadaLink.TemplateEngine.csproj" />
|
||||
<ProjectReference Include="../ZB.MOM.WW.ScadaBridge.Commons/ZB.MOM.WW.ScadaBridge.Commons.csproj" />
|
||||
<ProjectReference Include="../ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.csproj" />
|
||||
<ProjectReference Include="../ZB.MOM.WW.ScadaBridge.TemplateEngine/ZB.MOM.WW.ScadaBridge.TemplateEngine.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="ScadaLink.Transport.Tests" />
|
||||
<InternalsVisibleTo Include="ScadaLink.Transport.IntegrationTests" />
|
||||
<InternalsVisibleTo Include="ZB.MOM.WW.ScadaBridge.Transport.Tests" />
|
||||
<InternalsVisibleTo Include="ZB.MOM.WW.ScadaBridge.Transport.IntegrationTests" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
```
|
||||
@@ -536,11 +536,11 @@ git commit -m "feat(transport): AuditService stamps BundleImportId from correlat
|
||||
```csharp
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace ScadaLink.Transport;
|
||||
namespace ZB.MOM.WW.ScadaBridge.Transport;
|
||||
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
public const string OptionsSection = "ScadaLink:Transport";
|
||||
public const string OptionsSection = "ScadaBridge:Transport";
|
||||
|
||||
public static IServiceCollection AddTransport(this IServiceCollection services)
|
||||
{
|
||||
@@ -552,7 +552,7 @@ public static class ServiceCollectionExtensions
|
||||
}
|
||||
```
|
||||
|
||||
Test csprojs follow the existing test pattern (look at `tests/ScadaLink.NotificationOutbox.Tests/ScadaLink.NotificationOutbox.Tests.csproj` for shape — xUnit + NSubstitute + FluentAssertions + ProjectReference back to ScadaLink.Transport).
|
||||
Test csprojs follow the existing test pattern (look at `tests/ZB.MOM.WW.ScadaBridge.NotificationOutbox.Tests/ZB.MOM.WW.ScadaBridge.NotificationOutbox.Tests.csproj` for shape — xUnit + NSubstitute + FluentAssertions + ProjectReference back to ZB.MOM.WW.ScadaBridge.Transport).
|
||||
|
||||
**Step 2: Add to slnx**
|
||||
|
||||
@@ -560,14 +560,14 @@ Insert in alphabetical order under `/src/` and `/tests/` folder sections.
|
||||
|
||||
**Step 3: Build**
|
||||
|
||||
Run: `dotnet build ScadaLink.slnx`
|
||||
Run: `dotnet build ZB.MOM.WW.ScadaBridge.slnx`
|
||||
Expected: build succeeds (Transport empty + tests empty).
|
||||
|
||||
**Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ScadaLink.Transport/ tests/ScadaLink.Transport.Tests/ tests/ScadaLink.Transport.IntegrationTests/ ScadaLink.slnx
|
||||
git commit -m "feat(transport): scaffold ScadaLink.Transport project + test projects"
|
||||
git add src/ZB.MOM.WW.ScadaBridge.Transport/ tests/ZB.MOM.WW.ScadaBridge.Transport.Tests/ tests/ZB.MOM.WW.ScadaBridge.Transport.IntegrationTests/ ZB.MOM.WW.ScadaBridge.slnx
|
||||
git commit -m "feat(transport): scaffold ZB.MOM.WW.ScadaBridge.Transport project + test projects"
|
||||
```
|
||||
|
||||
---
|
||||
@@ -579,12 +579,12 @@ git commit -m "feat(transport): scaffold ScadaLink.Transport project + test proj
|
||||
**Parallelizable with:** Task 8 (different file)
|
||||
|
||||
**Files:**
|
||||
- Create: `src/ScadaLink.Transport/TransportOptions.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Transport/TransportOptions.cs`
|
||||
|
||||
**Step 1: Write the options class**
|
||||
|
||||
```csharp
|
||||
namespace ScadaLink.Transport;
|
||||
namespace ZB.MOM.WW.ScadaBridge.Transport;
|
||||
|
||||
public sealed class TransportOptions
|
||||
{
|
||||
@@ -600,7 +600,7 @@ public sealed class TransportOptions
|
||||
**Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ScadaLink.Transport/TransportOptions.cs
|
||||
git add src/ZB.MOM.WW.ScadaBridge.Transport/TransportOptions.cs
|
||||
git commit -m "feat(transport): add TransportOptions"
|
||||
```
|
||||
|
||||
@@ -613,8 +613,8 @@ git commit -m "feat(transport): add TransportOptions"
|
||||
**Parallelizable with:** Task 7, Task 9, Task 10, Task 12, Task 13, Task 19
|
||||
|
||||
**Files:**
|
||||
- Create: `src/ScadaLink.Transport/Encryption/BundleSecretEncryptor.cs`
|
||||
- Create: `tests/ScadaLink.Transport.Tests/BundleSecretEncryptorTests.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Transport/Encryption/BundleSecretEncryptor.cs`
|
||||
- Create: `tests/ZB.MOM.WW.ScadaBridge.Transport.Tests/BundleSecretEncryptorTests.cs`
|
||||
|
||||
**Step 1: Write failing tests first**
|
||||
|
||||
@@ -627,16 +627,16 @@ Tests to write (all in `BundleSecretEncryptorTests.cs`):
|
||||
|
||||
**Step 2: Run tests to verify failure**
|
||||
|
||||
Run: `dotnet test tests/ScadaLink.Transport.Tests/ --filter FullyQualifiedName~BundleSecretEncryptorTests`
|
||||
Run: `dotnet test tests/ZB.MOM.WW.ScadaBridge.Transport.Tests/ --filter FullyQualifiedName~BundleSecretEncryptorTests`
|
||||
Expected: FAIL (class not yet defined).
|
||||
|
||||
**Step 3: Implement**
|
||||
|
||||
```csharp
|
||||
using System.Security.Cryptography;
|
||||
using ScadaLink.Commons.Types.Transport;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Transport;
|
||||
|
||||
namespace ScadaLink.Transport.Encryption;
|
||||
namespace ZB.MOM.WW.ScadaBridge.Transport.Encryption;
|
||||
|
||||
public sealed class BundleSecretEncryptor
|
||||
{
|
||||
@@ -700,13 +700,13 @@ public sealed class BundleSecretEncryptor
|
||||
|
||||
**Step 4: Run tests**
|
||||
|
||||
Run: `dotnet test tests/ScadaLink.Transport.Tests/ --filter FullyQualifiedName~BundleSecretEncryptorTests`
|
||||
Run: `dotnet test tests/ZB.MOM.WW.ScadaBridge.Transport.Tests/ --filter FullyQualifiedName~BundleSecretEncryptorTests`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ScadaLink.Transport/Encryption/ tests/ScadaLink.Transport.Tests/BundleSecretEncryptorTests.cs
|
||||
git add src/ZB.MOM.WW.ScadaBridge.Transport/Encryption/ tests/ZB.MOM.WW.ScadaBridge.Transport.Tests/BundleSecretEncryptorTests.cs
|
||||
git commit -m "feat(transport): AES-256-GCM + PBKDF2 BundleSecretEncryptor"
|
||||
```
|
||||
|
||||
@@ -719,9 +719,9 @@ git commit -m "feat(transport): AES-256-GCM + PBKDF2 BundleSecretEncryptor"
|
||||
**Parallelizable with:** Task 7, Task 8, Task 10, Task 12, Task 13, Task 19
|
||||
|
||||
**Files:**
|
||||
- Create: `src/ScadaLink.Transport/Serialization/ManifestBuilder.cs`
|
||||
- Create: `src/ScadaLink.Transport/Serialization/ManifestValidator.cs`
|
||||
- Create: `tests/ScadaLink.Transport.Tests/ManifestBuilderTests.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Transport/Serialization/ManifestBuilder.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Transport/Serialization/ManifestValidator.cs`
|
||||
- Create: `tests/ZB.MOM.WW.ScadaBridge.Transport.Tests/ManifestBuilderTests.cs`
|
||||
|
||||
**Step 1: Write failing tests**
|
||||
|
||||
@@ -752,9 +752,9 @@ git commit -m "feat(transport): ManifestBuilder + ManifestValidator with schema-
|
||||
**Parallelizable with:** Task 7, Task 8, Task 9, Task 12, Task 13, Task 19
|
||||
|
||||
**Files:**
|
||||
- Create: `src/ScadaLink.Transport/Serialization/EntityDtos.cs` (all bundle DTOs: `TemplateDto`, `TemplateFolderDto`, `SharedScriptDto`, `ExternalSystemDto`, `DatabaseConnectionDto`, `NotificationListDto`, `SmtpConfigDto`, `ApiKeyDto`, `ApiMethodDto`, each with a nested `SecretsBlock` where applicable)
|
||||
- Create: `src/ScadaLink.Transport/Serialization/EntitySerializer.cs`
|
||||
- Create: `tests/ScadaLink.Transport.Tests/EntitySerializerTests.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Transport/Serialization/EntityDtos.cs` (all bundle DTOs: `TemplateDto`, `TemplateFolderDto`, `SharedScriptDto`, `ExternalSystemDto`, `DatabaseConnectionDto`, `NotificationListDto`, `SmtpConfigDto`, `ApiKeyDto`, `ApiMethodDto`, each with a nested `SecretsBlock` where applicable)
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Transport/Serialization/EntitySerializer.cs`
|
||||
- Create: `tests/ZB.MOM.WW.ScadaBridge.Transport.Tests/EntitySerializerTests.cs`
|
||||
|
||||
**Step 1: Write failing tests**
|
||||
|
||||
@@ -811,8 +811,8 @@ git commit -m "feat(transport): bundle entity DTOs + secret carving in EntitySer
|
||||
**Parallelizable with:** none (depends on Tasks 8, 9, 10)
|
||||
|
||||
**Files:**
|
||||
- Create: `src/ScadaLink.Transport/Serialization/BundleSerializer.cs`
|
||||
- Create: `tests/ScadaLink.Transport.Tests/BundleSerializerTests.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Transport/Serialization/BundleSerializer.cs`
|
||||
- Create: `tests/ZB.MOM.WW.ScadaBridge.Transport.Tests/BundleSerializerTests.cs`
|
||||
|
||||
**Step 1: Write failing tests**
|
||||
|
||||
@@ -844,8 +844,8 @@ git commit -m "feat(transport): BundleSerializer ZIP packer/reader"
|
||||
**Parallelizable with:** Task 7, Task 8, Task 9, Task 10, Task 13, Task 19
|
||||
|
||||
**Files:**
|
||||
- Create: `src/ScadaLink.Transport/Export/DependencyResolver.cs`
|
||||
- Create: `tests/ScadaLink.Transport.Tests/DependencyResolverTests.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Transport/Export/DependencyResolver.cs`
|
||||
- Create: `tests/ZB.MOM.WW.ScadaBridge.Transport.Tests/DependencyResolverTests.cs`
|
||||
|
||||
**Step 1: Write failing tests**
|
||||
|
||||
@@ -886,8 +886,8 @@ git commit -m "feat(transport): DependencyResolver with topological closure"
|
||||
**Parallelizable with:** Task 7, Task 8, Task 9, Task 10, Task 12, Task 19
|
||||
|
||||
**Files:**
|
||||
- Create: `src/ScadaLink.Transport/Import/BundleSessionStore.cs`
|
||||
- Create: `tests/ScadaLink.Transport.Tests/BundleSessionStoreTests.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Transport/Import/BundleSessionStore.cs`
|
||||
- Create: `tests/ZB.MOM.WW.ScadaBridge.Transport.Tests/BundleSessionStoreTests.cs`
|
||||
|
||||
**Step 1: Write failing tests**
|
||||
|
||||
@@ -916,9 +916,9 @@ git commit -m "feat(transport): in-memory BundleSessionStore with TTL + lockout"
|
||||
**Parallelizable with:** none (depends on Tasks 8–12)
|
||||
|
||||
**Files:**
|
||||
- Create: `src/ScadaLink.Transport/Export/BundleExporter.cs`
|
||||
- Create: `tests/ScadaLink.Transport.IntegrationTests/BundleExporterTests.cs` (integration — needs DB)
|
||||
- Modify: `src/ScadaLink.Transport/ServiceCollectionExtensions.cs` (register `IBundleExporter -> BundleExporter`)
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Transport/Export/BundleExporter.cs`
|
||||
- Create: `tests/ZB.MOM.WW.ScadaBridge.Transport.IntegrationTests/BundleExporterTests.cs` (integration — needs DB)
|
||||
- Modify: `src/ZB.MOM.WW.ScadaBridge.Transport/ServiceCollectionExtensions.cs` (register `IBundleExporter -> BundleExporter`)
|
||||
|
||||
**Step 1: Write failing integration test**
|
||||
|
||||
@@ -970,9 +970,9 @@ git commit -m "feat(transport): BundleExporter with audit logging"
|
||||
**Parallelizable with:** none (depends on Tasks 11, 13)
|
||||
|
||||
**Files:**
|
||||
- Create: `src/ScadaLink.Transport/Import/BundleImporter.cs`
|
||||
- Create: `tests/ScadaLink.Transport.Tests/BundleImporterLoadTests.cs`
|
||||
- Modify: `src/ScadaLink.Transport/ServiceCollectionExtensions.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Transport/Import/BundleImporter.cs`
|
||||
- Create: `tests/ZB.MOM.WW.ScadaBridge.Transport.Tests/BundleImporterLoadTests.cs`
|
||||
- Modify: `src/ZB.MOM.WW.ScadaBridge.Transport/ServiceCollectionExtensions.cs`
|
||||
|
||||
**Step 1: Write failing tests**
|
||||
|
||||
@@ -1014,9 +1014,9 @@ git commit -m "feat(transport): BundleImporter.LoadAsync with manifest validatio
|
||||
**Parallelizable with:** none (depends on Task 15)
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ScadaLink.Transport/Import/BundleImporter.cs`
|
||||
- Create: `src/ScadaLink.Transport/Import/ArtifactDiff.cs`
|
||||
- Create: `tests/ScadaLink.Transport.IntegrationTests/BundleImporterPreviewTests.cs`
|
||||
- Modify: `src/ZB.MOM.WW.ScadaBridge.Transport/Import/BundleImporter.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.Transport/Import/ArtifactDiff.cs`
|
||||
- Create: `tests/ZB.MOM.WW.ScadaBridge.Transport.IntegrationTests/BundleImporterPreviewTests.cs`
|
||||
|
||||
**Step 1: Write failing integration tests**
|
||||
|
||||
@@ -1046,8 +1046,8 @@ git commit -m "feat(transport): BundleImporter.PreviewAsync diff engine"
|
||||
**Parallelizable with:** none (depends on Task 16)
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ScadaLink.Transport/Import/BundleImporter.cs`
|
||||
- Create: `tests/ScadaLink.Transport.IntegrationTests/BundleImporterApplyTests.cs`
|
||||
- Modify: `src/ZB.MOM.WW.ScadaBridge.Transport/Import/BundleImporter.cs`
|
||||
- Create: `tests/ZB.MOM.WW.ScadaBridge.Transport.IntegrationTests/BundleImporterApplyTests.cs`
|
||||
|
||||
**Step 1: Write failing integration tests**
|
||||
|
||||
@@ -1132,9 +1132,9 @@ git commit -m "feat(transport): BundleImporter.ApplyAsync transactional with aud
|
||||
**Parallelizable with:** Task 19, Task 27, Task 28
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ScadaLink.Host/Program.cs` (add `builder.Services.AddTransport();` in the "Central-only components" section, alongside `AddNotificationOutbox()`)
|
||||
- Modify: `src/ScadaLink.Host/appsettings.json` (add `ScadaLink:Transport` section with defaults that match `TransportOptions`)
|
||||
- Modify: `src/ScadaLink.Host/ScadaLink.Host.csproj` (add `ProjectReference` to ScadaLink.Transport)
|
||||
- Modify: `src/ZB.MOM.WW.ScadaBridge.Host/Program.cs` (add `builder.Services.AddTransport();` in the "Central-only components" section, alongside `AddNotificationOutbox()`)
|
||||
- Modify: `src/ZB.MOM.WW.ScadaBridge.Host/appsettings.json` (add `ScadaBridge:Transport` section with defaults that match `TransportOptions`)
|
||||
- Modify: `src/ZB.MOM.WW.ScadaBridge.Host/ZB.MOM.WW.ScadaBridge.Host.csproj` (add `ProjectReference` to ZB.MOM.WW.ScadaBridge.Transport)
|
||||
|
||||
**Step 1: Modify Program.cs**
|
||||
|
||||
@@ -1143,7 +1143,7 @@ Find the existing `if (centralRolesActive) { ... builder.Services.AddNotificatio
|
||||
**Step 2: appsettings.json**
|
||||
|
||||
```json
|
||||
"ScadaLink": {
|
||||
"ScadaBridge": {
|
||||
"Transport": {
|
||||
"BundleSessionTtlMinutes": 30,
|
||||
"MaxBundleSizeMb": 100,
|
||||
@@ -1157,13 +1157,13 @@ Find the existing `if (centralRolesActive) { ... builder.Services.AddNotificatio
|
||||
|
||||
**Step 3: Build**
|
||||
|
||||
Run: `dotnet build ScadaLink.slnx`
|
||||
Run: `dotnet build ZB.MOM.WW.ScadaBridge.slnx`
|
||||
Expected: build succeeds.
|
||||
|
||||
**Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ScadaLink.Host/
|
||||
git add src/ZB.MOM.WW.ScadaBridge.Host/
|
||||
git commit -m "feat(transport): register AddTransport() on central nodes"
|
||||
```
|
||||
|
||||
@@ -1176,8 +1176,8 @@ git commit -m "feat(transport): register AddTransport() on central nodes"
|
||||
**Parallelizable with:** Task 0, Task 1, Task 2, Task 4, Task 7, Task 8, Task 9, Task 10, Task 12, Task 13
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ScadaLink.CentralUI/Components/Shared/TreeView.razor`
|
||||
- Create: `tests/ScadaLink.CentralUI.Tests/TreeViewMultiSelectTests.cs`
|
||||
- Modify: `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/TreeView.razor`
|
||||
- Create: `tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/TreeViewMultiSelectTests.cs`
|
||||
|
||||
**Step 1: Extend the component API**
|
||||
|
||||
@@ -1201,7 +1201,7 @@ Toggle semantics:
|
||||
|
||||
**Step 2: Write failing tests**
|
||||
|
||||
Use bUnit (already in `tests/ScadaLink.CentralUI.Tests/`):
|
||||
Use bUnit (already in `tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/`):
|
||||
1. `Checkbox_mode_renders_checkbox_per_node`
|
||||
2. `Clicking_folder_selects_all_descendants`
|
||||
3. `Clicking_leaf_makes_parent_indeterminate_when_sibling_unchecked`
|
||||
@@ -1209,14 +1209,14 @@ Use bUnit (already in `tests/ScadaLink.CentralUI.Tests/`):
|
||||
|
||||
**Step 4: Run tests**
|
||||
|
||||
Run: `dotnet test tests/ScadaLink.CentralUI.Tests/ --filter FullyQualifiedName~TreeViewMultiSelect`
|
||||
Run: `dotnet test tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/ --filter FullyQualifiedName~TreeViewMultiSelect`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ScadaLink.CentralUI/Components/Shared/TreeView.razor \
|
||||
tests/ScadaLink.CentralUI.Tests/TreeViewMultiSelectTests.cs
|
||||
git add src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/TreeView.razor \
|
||||
tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/TreeViewMultiSelectTests.cs
|
||||
git commit -m "feat(centralui): TreeView checkbox-selection mode with tri-state"
|
||||
```
|
||||
|
||||
@@ -1229,8 +1229,8 @@ git commit -m "feat(centralui): TreeView checkbox-selection mode with tri-state"
|
||||
**Parallelizable with:** none (depends on Task 19)
|
||||
|
||||
**Files:**
|
||||
- Create: `src/ScadaLink.CentralUI/Components/Shared/TemplateFolderTree.razor`
|
||||
- Modify: `src/ScadaLink.CentralUI/Components/Pages/Design/Templates.razor` (use the new component in `Single` mode)
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/TemplateFolderTree.razor`
|
||||
- Modify: `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/Templates.razor` (use the new component in `Single` mode)
|
||||
|
||||
**Step 1: Build the wrapper**
|
||||
|
||||
@@ -1251,15 +1251,15 @@ Replace the existing inline tree usage with `<TemplateFolderTree Folders="..." T
|
||||
|
||||
**Step 3: Manual smoke (no new tests needed — existing Templates.razor tests cover this)**
|
||||
|
||||
Run: `dotnet build src/ScadaLink.CentralUI/ScadaLink.CentralUI.csproj`
|
||||
Then: `dotnet test tests/ScadaLink.CentralUI.Tests/`
|
||||
Run: `dotnet build src/ZB.MOM.WW.ScadaBridge.CentralUI/ZB.MOM.WW.ScadaBridge.CentralUI.csproj`
|
||||
Then: `dotnet test tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/`
|
||||
Expected: all existing Templates page tests still pass.
|
||||
|
||||
**Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ScadaLink.CentralUI/Components/Shared/TemplateFolderTree.razor \
|
||||
src/ScadaLink.CentralUI/Components/Pages/Design/Templates.razor
|
||||
git add src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/TemplateFolderTree.razor \
|
||||
src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/Templates.razor
|
||||
git commit -m "refactor(centralui): extract TemplateFolderTree as shared component"
|
||||
```
|
||||
|
||||
@@ -1272,17 +1272,17 @@ git commit -m "refactor(centralui): extract TemplateFolderTree as shared compone
|
||||
**Parallelizable with:** Task 22 (separate page; only NavMenu touches both)
|
||||
|
||||
**Files:**
|
||||
- Create: `src/ScadaLink.CentralUI/Components/Pages/Design/TransportExport.razor`
|
||||
- Create: `src/ScadaLink.CentralUI/Components/Pages/Design/TransportExport.razor.cs` (code-behind for the wizard state machine)
|
||||
- Create: `tests/ScadaLink.CentralUI.Tests/TransportExportPageTests.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TransportExport.razor`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TransportExport.razor.cs` (code-behind for the wizard state machine)
|
||||
- Create: `tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/TransportExportPageTests.cs`
|
||||
|
||||
**Step 1: Page skeleton**
|
||||
|
||||
```razor
|
||||
@page "/design/transport/export"
|
||||
@using ScadaLink.Security
|
||||
@using ScadaLink.Commons.Types.Transport
|
||||
@using ScadaLink.Commons.Interfaces.Transport
|
||||
@using ZB.MOM.WW.ScadaBridge.Security
|
||||
@using ZB.MOM.WW.ScadaBridge.Commons.Types.Transport
|
||||
@using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Transport
|
||||
@attribute [Authorize(Policy = AuthorizationPolicies.RequireDesign)]
|
||||
@inject IBundleExporter BundleExporter
|
||||
@inject ITemplateEngineRepository TemplateRepo
|
||||
@@ -1302,7 +1302,7 @@ Step 2 calls `DependencyResolver` server-side and shows the expanded selection (
|
||||
|
||||
Step 3 collects passphrase + confirm (or explicit "Export without encryption" path with warning banner).
|
||||
|
||||
Step 4 calls `IBundleExporter.ExportAsync`, streams the result to the browser via `IJSRuntime.InvokeVoidAsync("scadalinkTransport.downloadBundle", filename, bytes)`. Display SHA-256 + size.
|
||||
Step 4 calls `IBundleExporter.ExportAsync`, streams the result to the browser via `IJSRuntime.InvokeVoidAsync("scadabridgeTransport.downloadBundle", filename, bytes)`. Display SHA-256 + size.
|
||||
|
||||
**Step 2: Page tests with bUnit**
|
||||
|
||||
@@ -1313,15 +1313,15 @@ Step 4 calls `IBundleExporter.ExportAsync`, streams the result to the browser vi
|
||||
|
||||
**Step 3: Run tests**
|
||||
|
||||
Run: `dotnet test tests/ScadaLink.CentralUI.Tests/ --filter FullyQualifiedName~TransportExportPage`
|
||||
Run: `dotnet test tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/ --filter FullyQualifiedName~TransportExportPage`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ScadaLink.CentralUI/Components/Pages/Design/TransportExport.razor \
|
||||
src/ScadaLink.CentralUI/Components/Pages/Design/TransportExport.razor.cs \
|
||||
tests/ScadaLink.CentralUI.Tests/TransportExportPageTests.cs
|
||||
git add src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TransportExport.razor \
|
||||
src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TransportExport.razor.cs \
|
||||
tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/TransportExportPageTests.cs
|
||||
git commit -m "feat(centralui): TransportExport wizard under Design nav group"
|
||||
```
|
||||
|
||||
@@ -1334,17 +1334,17 @@ git commit -m "feat(centralui): TransportExport wizard under Design nav group"
|
||||
**Parallelizable with:** Task 21
|
||||
|
||||
**Files:**
|
||||
- Create: `src/ScadaLink.CentralUI/Components/Pages/Design/TransportImport.razor`
|
||||
- Create: `src/ScadaLink.CentralUI/Components/Pages/Design/TransportImport.razor.cs`
|
||||
- Create: `tests/ScadaLink.CentralUI.Tests/TransportImportPageTests.cs`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TransportImport.razor`
|
||||
- Create: `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TransportImport.razor.cs`
|
||||
- Create: `tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/TransportImportPageTests.cs`
|
||||
|
||||
**Step 1: Page skeleton**
|
||||
|
||||
```razor
|
||||
@page "/design/transport/import"
|
||||
@using ScadaLink.Security
|
||||
@using ScadaLink.Commons.Types.Transport
|
||||
@using ScadaLink.Commons.Interfaces.Transport
|
||||
@using ZB.MOM.WW.ScadaBridge.Security
|
||||
@using ZB.MOM.WW.ScadaBridge.Commons.Types.Transport
|
||||
@using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Transport
|
||||
@attribute [Authorize(Policy = AuthorizationPolicies.RequireAdmin)]
|
||||
@inject IBundleImporter BundleImporter
|
||||
@inject NavigationManager Nav
|
||||
@@ -1370,9 +1370,9 @@ Five-step wizard:
|
||||
**Step 3: Run + commit**
|
||||
|
||||
```bash
|
||||
git add src/ScadaLink.CentralUI/Components/Pages/Design/TransportImport.razor \
|
||||
src/ScadaLink.CentralUI/Components/Pages/Design/TransportImport.razor.cs \
|
||||
tests/ScadaLink.CentralUI.Tests/TransportImportPageTests.cs
|
||||
git add src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TransportImport.razor \
|
||||
src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TransportImport.razor.cs \
|
||||
tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/TransportImportPageTests.cs
|
||||
git commit -m "feat(centralui): TransportImport wizard under Design nav group"
|
||||
```
|
||||
|
||||
@@ -1385,7 +1385,7 @@ git commit -m "feat(centralui): TransportImport wizard under Design nav group"
|
||||
**Parallelizable with:** none (depends on Tasks 21, 22)
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ScadaLink.CentralUI/Components/Layout/NavMenu.razor`
|
||||
- Modify: `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Layout/NavMenu.razor`
|
||||
|
||||
**Step 1: Add nav entries**
|
||||
|
||||
@@ -1407,13 +1407,13 @@ Inside the existing Design `<NavSection>`, after the Templates entry:
|
||||
|
||||
**Step 2: Build + smoke-test**
|
||||
|
||||
Run: `dotnet build src/ScadaLink.CentralUI/ScadaLink.CentralUI.csproj`
|
||||
Run: `dotnet build src/ZB.MOM.WW.ScadaBridge.CentralUI/ZB.MOM.WW.ScadaBridge.CentralUI.csproj`
|
||||
Expected: build succeeds.
|
||||
|
||||
**Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ScadaLink.CentralUI/Components/Layout/NavMenu.razor
|
||||
git add src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Layout/NavMenu.razor
|
||||
git commit -m "feat(centralui): add Export/Import Bundle nav entries"
|
||||
```
|
||||
|
||||
@@ -1426,8 +1426,8 @@ git commit -m "feat(centralui): add Export/Import Bundle nav entries"
|
||||
**Parallelizable with:** Task 22, Task 23
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ScadaLink.CentralUI/Components/Pages/Audit/ConfigurationAuditLog.razor` (locate by `git grep -l ConfigurationAuditLog src/ScadaLink.CentralUI/`)
|
||||
- Modify: `src/ScadaLink.ConfigurationDatabase/Repositories/` (the repository that drives the audit page — extend the query with an optional `Guid? bundleImportId` filter)
|
||||
- Modify: `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Audit/ConfigurationAuditLog.razor` (locate by `git grep -l ConfigurationAuditLog src/ZB.MOM.WW.ScadaBridge.CentralUI/`)
|
||||
- Modify: `src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Repositories/` (the repository that drives the audit page — extend the query with an optional `Guid? bundleImportId` filter)
|
||||
|
||||
**Step 1: Extend the repository query**
|
||||
|
||||
@@ -1461,7 +1461,7 @@ git commit -m "feat(centralui): Bundle Import filter on ConfigurationAuditLog pa
|
||||
**Parallelizable with:** Task 26, Task 27, Task 28
|
||||
|
||||
**Files:**
|
||||
- Create: `tests/ScadaLink.Transport.IntegrationTests/RoundTripTests.cs`
|
||||
- Create: `tests/ZB.MOM.WW.ScadaBridge.Transport.IntegrationTests/RoundTripTests.cs`
|
||||
|
||||
**Step 1: Write the test**
|
||||
|
||||
@@ -1484,7 +1484,7 @@ public async Task Export_then_wipe_then_import_restores_state()
|
||||
**Step 2: Run + commit**
|
||||
|
||||
```bash
|
||||
git add tests/ScadaLink.Transport.IntegrationTests/RoundTripTests.cs
|
||||
git add tests/ZB.MOM.WW.ScadaBridge.Transport.IntegrationTests/RoundTripTests.cs
|
||||
git commit -m "test(transport): integration round-trip export → wipe → import"
|
||||
```
|
||||
|
||||
@@ -1497,8 +1497,8 @@ git commit -m "test(transport): integration round-trip export → wipe → impor
|
||||
**Parallelizable with:** Task 25, Task 27, Task 28
|
||||
|
||||
**Files:**
|
||||
- Create: `tests/ScadaLink.Transport.IntegrationTests/ConflictResolutionTests.cs`
|
||||
- Create: `tests/ScadaLink.Transport.IntegrationTests/ValidationFailureTests.cs`
|
||||
- Create: `tests/ZB.MOM.WW.ScadaBridge.Transport.IntegrationTests/ConflictResolutionTests.cs`
|
||||
- Create: `tests/ZB.MOM.WW.ScadaBridge.Transport.IntegrationTests/ValidationFailureTests.cs`
|
||||
|
||||
**Step 1: Write the tests**
|
||||
|
||||
@@ -1513,7 +1513,7 @@ git commit -m "test(transport): integration round-trip export → wipe → impor
|
||||
**Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add tests/ScadaLink.Transport.IntegrationTests/
|
||||
git add tests/ZB.MOM.WW.ScadaBridge.Transport.IntegrationTests/
|
||||
git commit -m "test(transport): integration conflict resolution + rollback"
|
||||
```
|
||||
|
||||
@@ -1644,11 +1644,11 @@ git commit -m "docs(transport): manual cluster verification checklist"
|
||||
After Task 29:
|
||||
|
||||
```bash
|
||||
dotnet build ScadaLink.slnx
|
||||
dotnet test tests/ScadaLink.Transport.Tests/
|
||||
dotnet test tests/ScadaLink.Transport.IntegrationTests/
|
||||
dotnet test tests/ScadaLink.CentralUI.Tests/
|
||||
dotnet test tests/ScadaLink.ConfigurationDatabase.Tests/
|
||||
dotnet build ZB.MOM.WW.ScadaBridge.slnx
|
||||
dotnet test tests/ZB.MOM.WW.ScadaBridge.Transport.Tests/
|
||||
dotnet test tests/ZB.MOM.WW.ScadaBridge.Transport.IntegrationTests/
|
||||
dotnet test tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/
|
||||
dotnet test tests/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests/
|
||||
```
|
||||
|
||||
All must pass. Then proceed to Task 29 manual checklist against the docker cluster.
|
||||
@@ -1658,7 +1658,7 @@ All must pass. Then proceed to Task 29 manual checklist against the docker clust
|
||||
## Out-of-Scope Reminders (do NOT do)
|
||||
|
||||
- Do NOT add a feature flag.
|
||||
- Do NOT touch site-side projects (`ScadaLink.SiteRuntime` etc.).
|
||||
- Do NOT touch site-side projects (`ZB.MOM.WW.ScadaBridge.SiteRuntime` etc.).
|
||||
- Do NOT add a stale-mark column on `Instance` — the existing `DeploymentService.CompareAsync` already detects this.
|
||||
- Do NOT build CLI commands — those are deferred per design doc §13.
|
||||
- Do NOT add bundle signing — content hash in manifest is sufficient for v1.
|
||||
|
||||
Reference in New Issue
Block a user