feat(m9/T32c): schema-library CRUD commands + handlers + Central UI page + read-accessor

This commit is contained in:
Joseph Doherty
2026-06-18 12:32:31 -04:00
parent 71d5722692
commit 71a2bca4df
13 changed files with 1363 additions and 0 deletions
@@ -0,0 +1,83 @@
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Schemas;
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
/// <summary>
/// Outcome of a single schema-library mutation dispatch (create / update / delete).
/// Wraps either success or a human-readable server error (a duplicate-name guard, an
/// invalid-JSON validation failure, or a not-found) so the schema-library page can
/// render an inline result rather than reasoning about transport exceptions or the
/// three management response shapes. Mirrors <see cref="DataConnectionMoveResult"/>.
/// </summary>
/// <param name="Success">Whether the mutation succeeded.</param>
/// <param name="Error">A human-readable error message when <paramref name="Success"/> is <c>false</c>; otherwise <c>null</c>.</param>
public record SchemaLibraryActionResult(bool Success, string? Error)
{
/// <summary>Creates a successful result.</summary>
/// <returns>A successful <see cref="SchemaLibraryActionResult"/>.</returns>
public static SchemaLibraryActionResult Ok() => new(true, null);
/// <summary>Creates a failed result carrying <paramref name="error"/>.</summary>
/// <param name="error">The human-readable failure (typically a server guard / validation message).</param>
/// <returns>A failed <see cref="SchemaLibraryActionResult"/>.</returns>
public static SchemaLibraryActionResult Fail(string error) => new(false, error);
}
/// <summary>
/// CentralUI facade over the schema-library CRUD management commands (M9-T32c). Every
/// mutation (<c>CreateSharedSchemaCommand</c> / <c>UpdateSharedSchemaCommand</c> /
/// <c>DeleteSharedSchemaCommand</c>) and the list query is dispatched to the central
/// <c>ManagementActor</c> through the in-process <c>ManagementActorHolder</c> seam — the
/// SAME Ask path the HTTP <c>/management</c> endpoint uses — so the server remains the
/// single enforcer of the Designer role gate, the unique-name guard, and JSON-Schema
/// validation. The page re-implements none of that: it lists, authors via
/// <c>SchemaBuilder</c>, and SUBMITS commands; any guard / validation failure comes back
/// as a classified <see cref="SchemaLibraryActionResult"/>.
/// </summary>
/// <remarks>
/// The current Blazor principal is projected to an <c>AuthenticatedUser</c> so the
/// server's role gate runs against the real identity. Mirrors
/// <c>IDataConnectionMoveService</c> / <c>ISecuredWriteService</c>.
/// </remarks>
public interface ISchemaLibraryService
{
/// <summary>
/// Lists every library schema (name-ordered) via the read-gated
/// <c>ListSharedSchemasCommand</c>. A read failure yields an empty list (logged) so
/// the page renders gracefully.
/// </summary>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task resolving to the library entries, or an empty list on failure.</returns>
Task<IReadOnlyList<SharedSchema>> ListAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Creates a library schema via the Designer-gated <c>CreateSharedSchemaCommand</c>.
/// </summary>
/// <param name="name">The unique schema name.</param>
/// <param name="scope">An optional scope discriminator (<c>null</c> = global).</param>
/// <param name="schemaJson">The JSON Schema document text.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task resolving to a classified create outcome.</returns>
Task<SchemaLibraryActionResult> CreateAsync(
string name, string? scope, string schemaJson, CancellationToken cancellationToken = default);
/// <summary>
/// Updates a library schema via the Designer-gated <c>UpdateSharedSchemaCommand</c>.
/// </summary>
/// <param name="id">Primary key of the schema to update.</param>
/// <param name="name">The (possibly renamed) unique schema name.</param>
/// <param name="scope">An optional scope discriminator (<c>null</c> = global).</param>
/// <param name="schemaJson">The JSON Schema document text.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task resolving to a classified update outcome.</returns>
Task<SchemaLibraryActionResult> UpdateAsync(
int id, string name, string? scope, string schemaJson, CancellationToken cancellationToken = default);
/// <summary>
/// Deletes a library schema via the Designer-gated <c>DeleteSharedSchemaCommand</c>.
/// </summary>
/// <param name="id">Primary key of the schema to delete.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task resolving to a classified delete outcome.</returns>
Task<SchemaLibraryActionResult> DeleteAsync(int id, CancellationToken cancellationToken = default);
}