feat(centralui): ParentExecutionId column, filter and parent drill-in on the Audit Log page
This commit is contained in:
@@ -66,6 +66,7 @@ internal static class AuditDataSeeder
|
||||
string? actor = null,
|
||||
Guid? correlationId = null,
|
||||
Guid? executionId = null,
|
||||
Guid? parentExecutionId = null,
|
||||
int? httpStatus = null,
|
||||
int? durationMs = null,
|
||||
string? errorMessage = null,
|
||||
@@ -77,12 +78,12 @@ internal static class AuditDataSeeder
|
||||
const string sql = @"
|
||||
INSERT INTO [AuditLog]
|
||||
([EventId], [OccurredAtUtc], [IngestedAtUtc], [Channel], [Kind], [CorrelationId],
|
||||
[ExecutionId], [SourceSiteId], [SourceInstanceId], [SourceScript], [Actor], [Target],
|
||||
[ExecutionId], [ParentExecutionId], [SourceSiteId], [SourceInstanceId], [SourceScript], [Actor], [Target],
|
||||
[Status], [HttpStatus], [DurationMs], [ErrorMessage], [ErrorDetail], [RequestSummary],
|
||||
[ResponseSummary], [PayloadTruncated], [Extra], [ForwardState])
|
||||
VALUES
|
||||
(@eventId, @occurredAtUtc, SYSUTCDATETIME(), @channel, @kind, @correlationId,
|
||||
@executionId, @sourceSiteId, NULL, NULL, @actor, @target,
|
||||
@executionId, @parentExecutionId, @sourceSiteId, NULL, NULL, @actor, @target,
|
||||
@status, @httpStatus, @durationMs, @errorMessage, NULL, @requestSummary,
|
||||
@responseSummary, 0, @extra, NULL);";
|
||||
|
||||
@@ -96,6 +97,7 @@ VALUES
|
||||
cmd.Parameters.AddWithValue("@kind", kind);
|
||||
cmd.Parameters.AddWithValue("@correlationId", (object?)correlationId ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@executionId", (object?)executionId ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@parentExecutionId", (object?)parentExecutionId ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@sourceSiteId", (object?)sourceSiteId ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@actor", (object?)actor ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@target", (object?)target ?? DBNull.Value);
|
||||
|
||||
@@ -27,6 +27,10 @@ namespace ScadaLink.CentralUI.PlaywrightTests.Audit;
|
||||
/// <item><c>DrillInFromExecutionId_LandsOnAuditLogWithFilterContext</c> — the
|
||||
/// <c>?executionId=</c> drill-in (the drawer's "View this execution" action)
|
||||
/// auto-loads the grid filtered by ExecutionId.</item>
|
||||
/// <item><c>DrillInFromParentExecution_FiltersGridToSpawnerExecution</c> — the
|
||||
/// drawer's "View parent execution" action on a spawned (child) row drills in
|
||||
/// to <c>?executionId={ParentExecutionId}</c>, auto-loading the spawner's
|
||||
/// rows.</item>
|
||||
/// <item><c>NotificationsPage_HasViewAuditHistoryLink_WhenNotificationsExist</c> —
|
||||
/// the report page wires drill-in links when notifications are present.</item>
|
||||
/// <item><c>ExportCsv_LinkIsVisibleAndDownloads</c> — Export CSV button gated on
|
||||
@@ -350,6 +354,81 @@ public class AuditLogPageTests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DrillInFromParentExecution_FiltersGridToSpawnerExecution()
|
||||
{
|
||||
// The drawer's "View parent execution" action navigates a routed (child)
|
||||
// row to /audit/log?executionId={ParentExecutionId}. We seed a spawner row
|
||||
// (its ExecutionId == the parent id) and a child row (ParentExecutionId
|
||||
// pointing at the spawner), open the child's drawer, click the action, and
|
||||
// assert the grid auto-loads filtered to the spawner's own rows.
|
||||
if (!await AuditDataSeeder.IsAvailableAsync())
|
||||
{
|
||||
throw new InvalidOperationException("MSSQL unavailable; see FilterNarrowing test for setup instructions.");
|
||||
}
|
||||
|
||||
var runId = Guid.NewGuid().ToString("N");
|
||||
var targetPrefix = $"playwright-test/parent-exec-drill-in/{runId}/";
|
||||
var parentExecutionId = Guid.NewGuid();
|
||||
var spawnerEventId = Guid.NewGuid();
|
||||
var childEventId = Guid.NewGuid();
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
try
|
||||
{
|
||||
// The spawner execution's own row — carries ExecutionId == parentExecutionId.
|
||||
await AuditDataSeeder.InsertAuditEventAsync(
|
||||
eventId: spawnerEventId,
|
||||
occurredAtUtc: now,
|
||||
channel: "ApiInbound",
|
||||
kind: "InboundRequest",
|
||||
status: "Delivered",
|
||||
target: targetPrefix + "spawner",
|
||||
executionId: parentExecutionId,
|
||||
httpStatus: 200,
|
||||
durationMs: 7);
|
||||
|
||||
// The child (spawned) row — ParentExecutionId points at the spawner.
|
||||
await AuditDataSeeder.InsertAuditEventAsync(
|
||||
eventId: childEventId,
|
||||
occurredAtUtc: now,
|
||||
channel: "ApiOutbound",
|
||||
kind: "ApiCall",
|
||||
status: "Delivered",
|
||||
target: targetPrefix + "child",
|
||||
executionId: Guid.NewGuid(),
|
||||
parentExecutionId: parentExecutionId,
|
||||
httpStatus: 200,
|
||||
durationMs: 13);
|
||||
|
||||
var page = await _fixture.NewAuthenticatedPageAsync();
|
||||
|
||||
// Land on the child row via its ParentExecutionId filter, open the drawer.
|
||||
await page.GotoAsync($"{PlaywrightFixture.BaseUrl}/audit/log?parentExecutionId={parentExecutionId}");
|
||||
await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
|
||||
|
||||
var childRow = page.Locator($"[data-test='grid-row-{childEventId}']");
|
||||
await Assertions.Expect(childRow).ToBeVisibleAsync();
|
||||
await childRow.ClickAsync();
|
||||
|
||||
// The "View parent execution" action drills in to the spawner.
|
||||
var viewParent = page.Locator("[data-test='view-parent-execution']");
|
||||
await Assertions.Expect(viewParent).ToBeVisibleAsync();
|
||||
await viewParent.ClickAsync();
|
||||
await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
|
||||
|
||||
// The drill-in lands on ?executionId={parentExecutionId} and auto-loads
|
||||
// the spawner's own row.
|
||||
Assert.Contains($"executionId={parentExecutionId}", page.Url);
|
||||
var spawnerRow = page.Locator($"[data-test='grid-row-{spawnerEventId}']");
|
||||
await Assertions.Expect(spawnerRow).ToBeVisibleAsync();
|
||||
}
|
||||
finally
|
||||
{
|
||||
await AuditDataSeeder.DeleteByTargetPrefixAsync(targetPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task NotificationsPage_RendersAuditDrillInLinkPattern()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user