feat(uns): area + line CRUD with #122 reassignment guard

This commit is contained in:
Joseph Doherty
2026-06-08 12:35:58 -04:00
parent c9f59e4bd2
commit 47b1d2259f
4 changed files with 628 additions and 0 deletions
@@ -26,4 +26,78 @@ public interface IUnsTreeService
/// <param name="ct">A token to cancel the load.</param>
/// <returns>Tag nodes followed by VirtualTag nodes; empty if the equipment has none.</returns>
Task<IReadOnlyList<UnsNode>> LoadEquipmentChildrenAsync(string equipmentId, CancellationToken ct = default);
/// <summary>
/// Creates a new UNS area under a cluster. Fails if an area with the same id already exists.
/// Whitespace-only notes are stored as <c>null</c>.
/// </summary>
/// <param name="clusterId">The owning cluster.</param>
/// <param name="unsAreaId">The unique area id to create.</param>
/// <param name="name">The area name.</param>
/// <param name="notes">Optional notes; whitespace collapses to <c>null</c>.</param>
/// <param name="ct">A token to cancel the operation.</param>
/// <returns>Success, or a duplicate-id failure.</returns>
Task<UnsMutationResult> CreateAreaAsync(string clusterId, string unsAreaId, string name, string? notes, CancellationToken ct = default);
/// <summary>
/// Updates a UNS area's name, notes, and owning cluster. When the cluster changes, the
/// decision-#122 reassignment guard blocks the move if any driver-bound equipment under the
/// area is bound to a driver in a different cluster than the target. Uses last-write-wins
/// optimistic concurrency on <see cref="Configuration.Entities.UnsArea.RowVersion"/>.
/// </summary>
/// <param name="unsAreaId">The area to update.</param>
/// <param name="name">The new name.</param>
/// <param name="notes">The new notes; whitespace collapses to <c>null</c>.</param>
/// <param name="newClusterId">The target cluster (may equal the current one).</param>
/// <param name="rowVersion">The concurrency token the caller last read.</param>
/// <param name="ct">A token to cancel the operation.</param>
/// <returns>Success, a missing-row failure, a #122 guard failure, or a concurrency failure.</returns>
Task<UnsMutationResult> UpdateAreaAsync(string unsAreaId, string name, string? notes, string newClusterId, byte[] rowVersion, CancellationToken ct = default);
/// <summary>
/// Deletes a UNS area. A missing row is treated as success (already gone). Uses last-write-wins
/// optimistic concurrency; a delete that fails because lines still reference the area surfaces a
/// guidance message.
/// </summary>
/// <param name="unsAreaId">The area to delete.</param>
/// <param name="rowVersion">The concurrency token the caller last read.</param>
/// <param name="ct">A token to cancel the operation.</param>
/// <returns>Success, a concurrency failure, or a delete-failed failure.</returns>
Task<UnsMutationResult> DeleteAreaAsync(string unsAreaId, byte[] rowVersion, CancellationToken ct = default);
/// <summary>
/// Creates a new UNS line under an area. Fails if a line with the same id already exists.
/// Whitespace-only notes are stored as <c>null</c>.
/// </summary>
/// <param name="unsAreaId">The owning area.</param>
/// <param name="unsLineId">The unique line id to create.</param>
/// <param name="name">The line name.</param>
/// <param name="notes">Optional notes; whitespace collapses to <c>null</c>.</param>
/// <param name="ct">A token to cancel the operation.</param>
/// <returns>Success, or a duplicate-id failure.</returns>
Task<UnsMutationResult> CreateLineAsync(string unsAreaId, string unsLineId, string name, string? notes, CancellationToken ct = default);
/// <summary>
/// Updates a UNS line's owning area, name, and notes. Uses last-write-wins optimistic
/// concurrency on <see cref="Configuration.Entities.UnsLine.RowVersion"/>.
/// </summary>
/// <param name="unsLineId">The line to update.</param>
/// <param name="name">The new name.</param>
/// <param name="notes">The new notes; whitespace collapses to <c>null</c>.</param>
/// <param name="newUnsAreaId">The target parent area.</param>
/// <param name="rowVersion">The concurrency token the caller last read.</param>
/// <param name="ct">A token to cancel the operation.</param>
/// <returns>Success, a missing-row failure, or a concurrency failure.</returns>
Task<UnsMutationResult> UpdateLineAsync(string unsLineId, string name, string? notes, string newUnsAreaId, byte[] rowVersion, CancellationToken ct = default);
/// <summary>
/// Deletes a UNS line. A missing row is treated as success (already gone). Uses last-write-wins
/// optimistic concurrency; a delete that fails because equipment still references the line
/// surfaces a guidance message.
/// </summary>
/// <param name="unsLineId">The line to delete.</param>
/// <param name="rowVersion">The concurrency token the caller last read.</param>
/// <param name="ct">A token to cancel the operation.</param>
/// <returns>Success, a concurrency failure, or a delete-failed failure.</returns>
Task<UnsMutationResult> DeleteLineAsync(string unsLineId, byte[] rowVersion, CancellationToken ct = default);
}