Files
ScadaBridge/src/ZB.MOM.WW.ScadaBridge.Commons/Interfaces/Repositories/ISecuredWriteRepository.cs
T

128 lines
6.9 KiB
C#

using ZB.MOM.WW.ScadaBridge.Commons.Entities.SecuredWrites;
namespace ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
/// <summary>
/// Operational-state data access for the central <c>PendingSecuredWrites</c> table.
/// One row per pending two-person secured
/// write; rows are inserted at submission and mutated as the request is decided and
/// executed. Mirrors the <c>SiteCalls</c> repository shape.
/// </summary>
public interface ISecuredWriteRepository
{
/// <summary>
/// Inserts <paramref name="securedWrite"/> and returns the store-generated
/// <see cref="PendingSecuredWrite.Id"/>.
/// </summary>
/// <param name="securedWrite">The pending secured write to persist.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>A task that resolves to the generated identity of the inserted row.</returns>
Task<long> AddAsync(PendingSecuredWrite securedWrite, CancellationToken ct = default);
/// <summary>
/// Returns the row for the given id, or <c>null</c> if none exists.
/// </summary>
/// <param name="id">The identity to look up.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>A task that resolves to the matching <see cref="PendingSecuredWrite"/>, or <c>null</c> if no row exists.</returns>
Task<PendingSecuredWrite?> GetAsync(long id, CancellationToken ct = default);
/// <summary>
/// Persists the current state of <paramref name="securedWrite"/> (matched by
/// <see cref="PendingSecuredWrite.Id"/>).
/// </summary>
/// <param name="securedWrite">The tracked entity whose changes to persist.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
Task UpdateAsync(PendingSecuredWrite securedWrite, CancellationToken ct = default);
/// <summary>
/// Returns up to <paramref name="take"/> rows (skipping <paramref name="skip"/>)
/// optionally filtered by <paramref name="status"/> and <paramref name="siteId"/>,
/// ordered by <c>SubmittedAtUtc DESC, Id DESC</c>. A <c>null</c> filter argument
/// 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>
/// <param name="skip">Number of rows to skip (offset paging).</param>
/// <param name="take">Maximum number of rows to return.</param>
/// <param name="permittedSiteIds">When non-null, only rows whose <c>SiteId</c> (site
/// identifier) is in the set match; <c>null</c> matches every site. Applied IN ADDITION
/// to <paramref name="siteId"/>. Used by the ManagementActor to constrain an unfiltered
/// list to a site-scoped caller's permitted sites at the query level (arch-review R2 N3),
/// so paging and totals stay correct.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>A task that resolves to a page of matching rows, newest submission first.</returns>
Task<IReadOnlyList<PendingSecuredWrite>> QueryAsync(
string? status,
string? siteId,
int skip,
int take,
IReadOnlyCollection<string>? permittedSiteIds = null,
CancellationToken ct = default);
/// <summary>
/// Atomically flips a row from <c>Pending</c> to <c>Approved</c>, stamping the
/// verifier identity, comment, and decision time, but ONLY if the row is still
/// <c>Pending</c>. This is the compare-and-swap guard for the two-verifier race:
/// two verifiers may approve the same write concurrently, but the
/// conditional <c>WHERE Status='Pending'</c> guarantees exactly one wins. The
/// loser observes <c>false</c> and must not relay the write.
/// </summary>
/// <param name="id">Identity of the pending secured write.</param>
/// <param name="verifierUser">The approving verifier's username.</param>
/// <param name="verifierComment">Optional free-text comment from the verifier.</param>
/// <param name="decidedAtUtc">UTC instant the approval decision was made.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>
/// A task resolving to <c>true</c> if this caller won the approval (exactly one
/// row transitioned), or <c>false</c> if the row was no longer <c>Pending</c>
/// (already decided by another verifier).
/// </returns>
Task<bool> TryMarkApprovedAsync(
long id,
string verifierUser,
string? verifierComment,
DateTime decidedAtUtc,
CancellationToken ct = default);
/// <summary>
/// Atomically flips a row from <c>Pending</c> to <c>Expired</c>, stamping the
/// expiry as the decision time, but ONLY if the row is still <c>Pending</c>.
/// This is the multi-node-safe expiry primitive: the conditional
/// <c>WHERE Status='Pending'</c> makes the Pending-&gt;Expired transition atomic at
/// the row level, so two nodes sweeping the same overdue write concurrently
/// produce exactly one winner. The expiry is a system decision — no verifier is
/// recorded and <see cref="PendingSecuredWrite.VerifierUser"/> stays <c>null</c>.
/// The loser observes <c>false</c> (the row was already decided or expired).
/// </summary>
/// <param name="id">Identity of the pending secured write.</param>
/// <param name="expiredAtUtc">UTC instant the write was deemed expired.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>
/// A task resolving to <c>true</c> if this caller won the expiry (exactly one
/// row transitioned), or <c>false</c> if the row was no longer <c>Pending</c>.
/// </returns>
Task<bool> TryMarkExpiredAsync(long id, DateTime expiredAtUtc, CancellationToken ct = default);
/// <summary>
/// Returns the number of rows matching the optional <paramref name="status"/> and
/// <paramref name="siteId"/> filters. A <c>null</c> filter argument matches every
/// row. Mirrors <see cref="QueryAsync"/>'s filter composition without paging.
/// </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="permittedSiteIds">When non-null, only rows whose <c>SiteId</c> (site
/// identifier) is in the set match; <c>null</c> matches every site. Applied IN ADDITION
/// to <paramref name="siteId"/>. Used by the ManagementActor to constrain an unfiltered
/// list to a site-scoped caller's permitted sites at the query level (arch-review R2 N3),
/// so paging and totals stay correct.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>A task that resolves to the count of matching rows.</returns>
Task<int> CountAsync(
string? status,
string? siteId,
IReadOnlyCollection<string>? permittedSiteIds = null,
CancellationToken ct = default);
}