feat(centralui): Secured Writes page — operator submit + verifier queue + history (T14b)
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Outcome of a single secured-write command dispatch. Wraps either the resulting
|
||||
/// <see cref="SecuredWriteDto"/> (on success) or a human-readable error, so the page
|
||||
/// can render an inline result/toast rather than reasoning about transport exceptions
|
||||
/// or the three management response shapes.
|
||||
/// </summary>
|
||||
/// <param name="Success">Whether the command succeeded.</param>
|
||||
/// <param name="Dto">The resulting secured-write row when <paramref name="Success"/> is <c>true</c>; otherwise <c>null</c>.</param>
|
||||
/// <param name="Error">A human-readable error message when <paramref name="Success"/> is <c>false</c>; otherwise <c>null</c>.</param>
|
||||
public record SecuredWriteActionResult(bool Success, SecuredWriteDto? Dto, string? Error)
|
||||
{
|
||||
/// <summary>Creates a successful result wrapping <paramref name="dto"/>.</summary>
|
||||
/// <param name="dto">The secured-write row the command produced.</param>
|
||||
/// <returns>A successful <see cref="SecuredWriteActionResult"/>.</returns>
|
||||
public static SecuredWriteActionResult Ok(SecuredWriteDto dto) => new(true, dto, null);
|
||||
|
||||
/// <summary>Creates a failed result carrying <paramref name="error"/>.</summary>
|
||||
/// <param name="error">The human-readable failure message.</param>
|
||||
/// <returns>A failed <see cref="SecuredWriteActionResult"/>.</returns>
|
||||
public static SecuredWriteActionResult Fail(string error) => new(false, null, error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CentralUI facade over the two-person ("secured") write management commands
|
||||
/// (M7 OPC UA / MxGateway UX, Task T14b — page C5). It dispatches the strongly-typed
|
||||
/// <c>SubmitSecuredWriteCommand</c> / <c>ApproveSecuredWriteCommand</c> /
|
||||
/// <c>RejectSecuredWriteCommand</c> / <c>ListSecuredWritesCommand</c> to the central
|
||||
/// <c>ManagementActor</c> through the in-process <c>ManagementActorHolder</c> seam —
|
||||
/// the SAME path the HTTP <c>/management</c> endpoint uses — so the server remains the
|
||||
/// single enforcer of role gating, separation-of-duties (no self-approval), the actual
|
||||
/// MxGateway device relay on approve, and the append-only audit trail.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The page itself performs NO device I/O and re-implements NO security logic: every
|
||||
/// mutation is a command submitted to the server. The current user's claims are mapped
|
||||
/// to an <see cref="AuthenticatedUser"/> so the actor's role check and no-self-approval
|
||||
/// guard run against the real principal. <c>ManagementUnauthorized</c> /
|
||||
/// <c>ManagementError</c> / transport failures collapse into a typed
|
||||
/// <see cref="SecuredWriteActionResult"/> so the page renders an inline outcome rather
|
||||
/// than throwing.
|
||||
/// </remarks>
|
||||
public interface ISecuredWriteService
|
||||
{
|
||||
/// <summary>
|
||||
/// Submits a new pending secured write (Operator action). The target connection
|
||||
/// must exist within <paramref name="siteId"/> and use the MxGateway protocol —
|
||||
/// both validated server-side.
|
||||
/// </summary>
|
||||
/// <param name="siteId">Site identifier the write targets.</param>
|
||||
/// <param name="connectionName">MxGateway 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>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task resolving to the submit outcome.</returns>
|
||||
Task<SecuredWriteActionResult> SubmitAsync(
|
||||
string siteId,
|
||||
string connectionName,
|
||||
string tagPath,
|
||||
string valueJson,
|
||||
string valueType,
|
||||
string? comment,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Approves (and triggers execution of) a pending secured write (Verifier action).
|
||||
/// The server enforces that the approver differs from the submitter and runs the
|
||||
/// device relay.
|
||||
/// </summary>
|
||||
/// <param name="id">Identity of the pending secured write.</param>
|
||||
/// <param name="comment">Optional free-text comment supplied by the verifier.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task resolving to the approve outcome.</returns>
|
||||
Task<SecuredWriteActionResult> ApproveAsync(
|
||||
long id, string? comment, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Rejects a pending secured write (Verifier action). The server enforces that the
|
||||
/// rejecter differs from the submitter.
|
||||
/// </summary>
|
||||
/// <param name="id">Identity of the pending secured write.</param>
|
||||
/// <param name="comment">Optional free-text comment supplied by the verifier.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task resolving to the reject outcome.</returns>
|
||||
Task<SecuredWriteActionResult> RejectAsync(
|
||||
long id, string? comment, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Lists secured writes, optionally filtered by <paramref name="status"/> and
|
||||
/// <paramref name="siteId"/> (read-only — any authenticated user). On any failure
|
||||
/// an empty list is returned so the page renders gracefully.
|
||||
/// </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>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task resolving to the matching rows, newest submission first.</returns>
|
||||
Task<IReadOnlyList<SecuredWriteDto>> ListAsync(
|
||||
string? status, string? siteId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
Reference in New Issue
Block a user