feat(configdb): add Deployment, NodeDeploymentState, ConfigEdit, DataProtectionKey entities

Phase 1 entities for the v2 live-edit + snapshot-deploy model:

  Deployment           — immutable artifact snapshot (replaces v1 ConfigGeneration row)
                         Status enum {Dispatching, AwaitingApplyAcks, Sealed,
                         PartiallyFailed, TimedOut}; carries the SHA256 RevisionHash and
                         the SnapshotAndFlatten() ArtifactBlob; RowVersion for optimistic
                         concurrency.
  NodeDeploymentState  — per-(node, deployment) apply progress row owned by
                         DriverHostActor (replaces single-row ClusterNodeGenerationState).
                         Composite key (NodeId, DeploymentId) gives the
                         ConfigPublishCoordinator the full history it needs to
                         reconstruct in-flight state after a failover.
  ConfigEdit           — append-only audit row written by AdminOperationsActor on every
                         mutating op; optional ExecutionId correlates edits inside one
                         admin transaction (e.g. an import batch).
  DataProtectionKey    — ASP.NET DataProtection key ring storage via
                         IDataProtectionKeyContext so every admin-role node decrypts
                         the same cookies without sharing a filesystem.

OtOpcUaConfigDbContext now implements IDataProtectionKeyContext and registers four new
DbSets + four new ConfigureXxx mappings.

Central package bumps (forced by Microsoft.AspNetCore.DataProtection.EntityFrameworkCore
10.0.7's transitive dep):

  Microsoft.EntityFrameworkCore.{,Design,InMemory,SqlServer}  10.0.0 -> 10.0.7
  Microsoft.Extensions.{Configuration.Abstractions,Configuration.Json,Hosting,Hosting.WindowsServices,Http}  10.0.0 -> 10.0.7

EF migration generation + the ConfigGeneration drop + RedundancyRole column removal are
deferred to Task 14 (high-risk, non-parallelizable).
This commit is contained in:
Joseph Doherty
2026-05-26 03:49:59 -04:00
parent 30a2104fa5
commit 8e2c4f2835
8 changed files with 214 additions and 10 deletions
@@ -0,0 +1,27 @@
namespace ZB.MOM.WW.OtOpcUa.Configuration.Entities;
/// <summary>
/// Append-only audit row written by AdminOperationsActor on every mutating live-edit
/// operation. The ExecutionId optionally correlates a sequence of edits that ran inside one
/// admin transaction (e.g. an import batch that updates many Equipment rows).
/// </summary>
public sealed class ConfigEdit
{
public Guid EditId { get; init; } = Guid.NewGuid();
public required string EntityType { get; init; }
public Guid EntityId { get; init; }
/// <summary>JSON payload of the column-name → new-value pairs touched by this edit.</summary>
public required string FieldsJson { get; init; }
/// <summary>Optional correlation across edits inside a single admin operation.</summary>
public Guid? ExecutionId { get; init; }
public required string EditedBy { get; init; }
public DateTime EditedAtUtc { get; init; } = DateTime.UtcNow;
public required string SourceNode { get; init; }
}
@@ -0,0 +1,30 @@
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
namespace ZB.MOM.WW.OtOpcUa.Configuration.Entities;
/// <summary>
/// Immutable snapshot of a config artifact dispatched to every driver-role node by the
/// ConfigPublishCoordinator. Replaces the v1 <see cref="ConfigGeneration"/> draft/publish
/// row; the ArtifactBlob carries the SnapshotAndFlatten() output produced by
/// AdminOperationsActor.
/// </summary>
public sealed class Deployment
{
public Guid DeploymentId { get; init; } = Guid.NewGuid();
public required string RevisionHash { get; init; }
public DeploymentStatus Status { get; set; } = DeploymentStatus.Dispatching;
public required string CreatedBy { get; init; }
public DateTime CreatedAtUtc { get; init; } = DateTime.UtcNow;
public byte[] ArtifactBlob { get; init; } = Array.Empty<byte>();
public byte[] RowVersion { get; set; } = Array.Empty<byte>();
public string? FailureReason { get; set; }
public DateTime? SealedAtUtc { get; set; }
}
@@ -0,0 +1,29 @@
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
namespace ZB.MOM.WW.OtOpcUa.Configuration.Entities;
/// <summary>
/// Per-(node, deployment) apply progress row owned by the DriverHostActor. Replaces the
/// v1 <see cref="ClusterNodeGenerationState"/> single-row-per-node model with a history
/// of every apply attempt so the ConfigPublishCoordinator can reconstruct in-flight state
/// after a failover.
/// </summary>
public sealed class NodeDeploymentState
{
public required string NodeId { get; init; }
public Guid DeploymentId { get; init; }
public NodeDeploymentStatus Status { get; set; } = NodeDeploymentStatus.Applying;
public DateTime StartedAtUtc { get; set; } = DateTime.UtcNow;
public DateTime? AppliedAtUtc { get; set; }
public string? FailureReason { get; set; }
public byte[] RowVersion { get; set; } = Array.Empty<byte>();
public ClusterNode? Node { get; set; }
public Deployment? Deployment { get; set; }
}