feat(mgmt): secured-write submit/reject/list handlers + Operator/Verifier gating (T14b)

This commit is contained in:
Joseph Doherty
2026-06-18 02:29:29 -04:00
parent 586d54359c
commit 25c9240415
3 changed files with 458 additions and 0 deletions
@@ -0,0 +1,83 @@
namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
// ============================================================================
// Two-person ("secured") write commands (M7 OPC UA / MxGateway UX, Task T14b).
//
// An Operator SUBMITS a pending secured write against an MxGateway data
// connection; a distinct Verifier later APPROVES (executes — Task C3) or
// REJECTS it. Separation of duties is enforced at the handler: a write may not
// be verified by the same principal that submitted it. ListSecuredWrites is a
// read-only query (any authenticated user). Role gating lives in
// ManagementActor.GetRequiredRole (Operator for submit; Verifier for
// reject/approve).
// ============================================================================
/// <summary>
/// Operator request to submit a pending secured write. The target connection
/// must exist within the site and use the MxGateway protocol; the value is
/// captured as <paramref name="ValueJson"/> interpreted per <paramref name="ValueType"/>.
/// Returns the newly-created <see cref="SecuredWriteDto"/>.
/// </summary>
/// <param name="SiteId">Site identifier the write targets.</param>
/// <param name="ConnectionName">Data connection name within the site.</param>
/// <param name="TagPath">Fully-qualified tag path the value is written to.</param>
/// <param name="ValueJson">JSON-serialised value to write.</param>
/// <param name="ValueType">Target data type name (e.g. <c>Boolean</c>, <c>Double</c>).</param>
/// <param name="Comment">Optional free-text comment supplied by the operator.</param>
public record SubmitSecuredWriteCommand(
string SiteId,
string ConnectionName,
string TagPath,
string ValueJson,
string ValueType,
string? Comment);
/// <summary>
/// Verifier request to approve (and execute — handled by Task C3) a pending
/// secured write. Declared here so the secured-write contract is complete; the
/// approve→execute relay handler and dispatch arm are implemented in C3.
/// </summary>
/// <param name="Id">Identity of the pending secured write.</param>
/// <param name="Comment">Optional free-text comment supplied by the verifier.</param>
public record ApproveSecuredWriteCommand(long Id, string? Comment);
/// <summary>
/// Verifier request to reject a pending secured write. The write must still be
/// <c>Pending</c>, and the verifier must differ from the submitting operator
/// (separation of duties). Returns the updated <see cref="SecuredWriteDto"/>.
/// </summary>
/// <param name="Id">Identity of the pending secured write.</param>
/// <param name="Comment">Optional free-text comment supplied by the verifier.</param>
public record RejectSecuredWriteCommand(long Id, string? Comment);
/// <summary>
/// Read-only query for secured writes, optionally filtered by status and site.
/// A <c>null</c> filter matches every row.
/// </summary>
/// <param name="Status">Status filter; <c>null</c> matches every status.</param>
/// <param name="SiteId">Site id filter; <c>null</c> matches every site.</param>
public record ListSecuredWritesCommand(string? Status, string? SiteId);
/// <summary>
/// Result projection of a single pending secured write row, mirroring the
/// <c>PendingSecuredWrite</c> entity shape.
/// </summary>
public record SecuredWriteDto(
long Id,
string SiteId,
string ConnectionName,
string TagPath,
string ValueJson,
string ValueType,
string Status,
string OperatorUser,
string? OperatorComment,
DateTime SubmittedAtUtc,
string? VerifierUser,
string? VerifierComment,
DateTime? DecidedAtUtc,
DateTime? ExecutedAtUtc,
string? ExecutionError);
/// <summary>Result wrapper for <see cref="ListSecuredWritesCommand"/>.</summary>
public record SecuredWriteListResult(IReadOnlyList<SecuredWriteDto> Items);