fix(ui): schema-library delete-audit name + busy guard + edit-row guard + sanitized create-race test (#260)
This commit is contained in:
@@ -93,10 +93,13 @@
|
||||
<td>@(string.IsNullOrWhiteSpace(s.Scope) ? "—" : s.Scope)</td>
|
||||
<td><code>lib:@s.Name</code></td>
|
||||
<td class="text-end">
|
||||
@* Row actions are disabled while the editor is open so the
|
||||
row under edit (and its siblings) can't be deleted out from
|
||||
under the form, and while a delete is in flight (_busy). *@
|
||||
<button class="btn btn-outline-primary btn-sm me-1"
|
||||
@onclick="() => BeginEdit(s)">Edit</button>
|
||||
@onclick="() => BeginEdit(s)" disabled="@(_editing || _busy)">Edit</button>
|
||||
<button class="btn btn-outline-danger btn-sm"
|
||||
@onclick="() => DeleteSchema(s)">Delete</button>
|
||||
@onclick="() => DeleteSchema(s)" disabled="@(_editing || _busy)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
@@ -208,21 +211,34 @@
|
||||
|
||||
private async Task DeleteSchema(SharedSchema schema)
|
||||
{
|
||||
// In-flight guard: an editor-open row action is already disabled in the markup,
|
||||
// but the _busy gate is the authoritative guard against a double-invoked delete
|
||||
// (and mirrors the Save path's guard).
|
||||
if (_busy) return;
|
||||
|
||||
var confirmed = await Dialog.ConfirmAsync(
|
||||
"Delete Schema",
|
||||
$"Delete library schema '{schema.Name}'? References to lib:{schema.Name} will no longer resolve.",
|
||||
danger: true);
|
||||
if (!confirmed) return;
|
||||
|
||||
var result = await SchemaLibraryService.DeleteAsync(schema.Id);
|
||||
if (result.Success)
|
||||
_busy = true;
|
||||
try
|
||||
{
|
||||
_toast.ShowSuccess($"Schema '{schema.Name}' deleted.");
|
||||
await LoadAsync();
|
||||
var result = await SchemaLibraryService.DeleteAsync(schema.Id);
|
||||
if (result.Success)
|
||||
{
|
||||
_toast.ShowSuccess($"Schema '{schema.Name}' deleted.");
|
||||
await LoadAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
_toast.ShowError(result.Error ?? "Delete failed.");
|
||||
}
|
||||
}
|
||||
else
|
||||
finally
|
||||
{
|
||||
_toast.ShowError(result.Error ?? "Delete failed.");
|
||||
_busy = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,11 @@ public sealed class SchemaLibraryQueryService : ISchemaLibraryQueryService
|
||||
var repo = scope.ServiceProvider.GetRequiredService<ISharedSchemaRepository>();
|
||||
var all = await repo.ListAsync(cancellationToken);
|
||||
|
||||
// Ordinal-keyed to match the lib:Name resolver's exact-name lookup. Last-wins on
|
||||
// the (DB-unique) name guards against a transient duplicate read.
|
||||
// Ordinal-keyed to match the lib:Name resolver's exact-name lookup. Name is
|
||||
// DB-unique, so a list yields at most one row per name and no real collision
|
||||
// occurs; the indexer assignment is defensive only — should two rows ever share
|
||||
// a name (e.g. a mid-write transient read), the later one in enumeration order
|
||||
// overwrites the earlier rather than throwing.
|
||||
var map = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
foreach (var schema in all)
|
||||
{
|
||||
|
||||
@@ -2323,8 +2323,13 @@ public class ManagementActor : ReceiveActor
|
||||
private static async Task<object?> HandleDeleteSharedSchema(IServiceProvider sp, DeleteSharedSchemaCommand cmd, string user)
|
||||
{
|
||||
var repo = sp.GetRequiredService<ISharedSchemaRepository>();
|
||||
// Pre-fetch the human-readable name before the row is gone so the audit
|
||||
// EntityName records "Address" rather than the numeric id — mirroring the
|
||||
// Site delete handler. Falls back to the id when the row is already absent.
|
||||
var schema = await repo.GetByIdAsync(cmd.SharedSchemaId);
|
||||
await repo.DeleteAsync(cmd.SharedSchemaId);
|
||||
await AuditAsync(sp, user, "Delete", "SharedSchema", cmd.SharedSchemaId.ToString(), cmd.SharedSchemaId.ToString(), null);
|
||||
await AuditAsync(sp, user, "Delete", "SharedSchema", cmd.SharedSchemaId.ToString(),
|
||||
schema?.Name ?? cmd.SharedSchemaId.ToString(), null);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user