fix(central-ui): notification deep link and relay use the stored N-form NotificationId
Notify.Send mints NotificationId as Guid "N" (no dashes) and the outbox parses it back into the audit row's CorrelationId, so the Flow Debugger's Notify node linked and relayed a "D" string the Notifications table never matches (found live: the report opened empty). The link and RetryNotificationRequest now use "N", and the Notification report normalises any Guid-shaped ?correlationId= to "N" so Audit Log and Flow Debugger callers both land. Claude-Session: https://claude.ai/code/session_01NcMG6xXdsJbmD2wYAgkK29
This commit is contained in:
@@ -223,7 +223,7 @@
|
|||||||
private string? ReportHref
|
private string? ReportHref
|
||||||
=> Step is { CorrelationId: { } c } s && IsOutbound && s.Lifecycle is { Count: > 0 }
|
=> Step is { CorrelationId: { } c } s && IsOutbound && s.Lifecycle is { Count: > 0 }
|
||||||
? s.Kind == FlowStepKind.Notify
|
? s.Kind == FlowStepKind.Notify
|
||||||
? $"/notifications/report?correlationId={c:D}"
|
? $"/notifications/report?correlationId={c:N}" // NotificationId is minted as "N" (no dashes) — see ScriptRuntimeContext.Notify
|
||||||
: $"/site-calls/report?correlationId={c:D}"
|
: $"/site-calls/report?correlationId={c:D}"
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -515,7 +515,9 @@
|
|||||||
var candidate = values.ToString()?.Trim();
|
var candidate = values.ToString()?.Trim();
|
||||||
if (!string.IsNullOrEmpty(candidate) && candidate.Length <= MaxNotificationIdFilterLength)
|
if (!string.IsNullOrEmpty(candidate) && candidate.Length <= MaxNotificationIdFilterLength)
|
||||||
{
|
{
|
||||||
_notificationIdFilter = candidate;
|
// NotificationId is minted as a Guid in "N" form (no dashes); a caller that only has the
|
||||||
|
// audit row's CorrelationId (a Guid, usually rendered "D") must still land on the row.
|
||||||
|
_notificationIdFilter = Guid.TryParse(candidate, out var guid) ? guid.ToString("N") : candidate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -225,7 +225,9 @@ public sealed class FlowDebugService : IFlowDebugService
|
|||||||
|
|
||||||
if (isNotify)
|
if (isNotify)
|
||||||
{
|
{
|
||||||
var notificationId = c.ToString("D");
|
// Notify.Send mints NotificationId as Guid "N" (no dashes; ScriptRuntimeContext) and the
|
||||||
|
// outbox parses it back into the audit row's CorrelationId — so the relay key is "N".
|
||||||
|
var notificationId = c.ToString("N");
|
||||||
if (retry)
|
if (retry)
|
||||||
{
|
{
|
||||||
var r = await _comms.RetryNotificationAsync(new RetryNotificationRequest(corr, notificationId, requestedBy), ct);
|
var r = await _comms.RetryNotificationAsync(new RetryNotificationRequest(corr, notificationId, requestedBy), ct);
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ public class FlowStepDetailTests : BunitContext
|
|||||||
var notify = Step(FlowStepKind.Notify, FlowStepOutcome.Ok, "Planners", CachedLifecycle("Delivered"), c);
|
var notify = Step(FlowStepKind.Notify, FlowStepOutcome.Ok, "Planners", CachedLifecycle("Delivered"), c);
|
||||||
cut = Render<FlowStepDetail>(p => p.Add(x => x.Step, notify));
|
cut = Render<FlowStepDetail>(p => p.Add(x => x.Step, notify));
|
||||||
link = cut.Find("a[data-test=open-report]");
|
link = cut.Find("a[data-test=open-report]");
|
||||||
Assert.Equal($"/notifications/report?correlationId={c:D}", link.GetAttribute("href"));
|
Assert.Equal($"/notifications/report?correlationId={c:N}", link.GetAttribute("href"));
|
||||||
Assert.Equal("Open in Notification report", link.TextContent.Trim());
|
Assert.Equal("Open in Notification report", link.TextContent.Trim());
|
||||||
|
|
||||||
var sync = Step(FlowStepKind.ApiCall, FlowStepOutcome.Ok, "Delmia.AckRecipe");
|
var sync = Step(FlowStepKind.ApiCall, FlowStepOutcome.Ok, "Delmia.AckRecipe");
|
||||||
@@ -167,7 +167,7 @@ public class FlowStepDetailTests : BunitContext
|
|||||||
Assert.Contains("Parked", hint.TextContent);
|
Assert.Contains("Parked", hint.TextContent);
|
||||||
var hintLink = hint.QuerySelector("a");
|
var hintLink = hint.QuerySelector("a");
|
||||||
Assert.NotNull(hintLink);
|
Assert.NotNull(hintLink);
|
||||||
Assert.Equal($"/notifications/report?correlationId={c:D}", hintLink!.GetAttribute("href"));
|
Assert.Equal($"/notifications/report?correlationId={c:N}", hintLink!.GetAttribute("href"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -248,6 +248,25 @@ public class NotificationReportPageTests : BunitContext
|
|||||||
// with that id comes back.
|
// with that id comes back.
|
||||||
// ─────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NotificationReport_CorrelationIdQuery_DashedGuid_IsNormalisedToTheStoredNForm()
|
||||||
|
{
|
||||||
|
// Notify.Send mints NotificationId as Guid "N"; the Flow Debugger / Audit Log hold the
|
||||||
|
// same value as a Guid and may render it "D". Either spelling must filter to the row.
|
||||||
|
var guid = Guid.Parse("a72bc17f-b681-43eb-a364-e136c54d51c0");
|
||||||
|
var nav = (BunitNavigationManager)Services.GetRequiredService<NavigationManager>();
|
||||||
|
nav.NavigateTo($"/notifications/report?correlationId={guid:D}");
|
||||||
|
|
||||||
|
var cut = Render<NotificationReportPage>();
|
||||||
|
|
||||||
|
cut.WaitForAssertion(() =>
|
||||||
|
{
|
||||||
|
Assert.Single(_queryRequests);
|
||||||
|
Assert.Equal(guid.ToString("N"), _queryRequests[0].NotificationIdFilter);
|
||||||
|
Assert.Contains(guid.ToString("N")[..12], cut.Find("[data-test='notif-filter-notification']").TextContent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void NotificationReport_CorrelationIdQuery_SeedsTheNotificationFilterAndAutoOpensDetail()
|
public void NotificationReport_CorrelationIdQuery_SeedsTheNotificationFilterAndAutoOpensDetail()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ public sealed class FlowDebugServiceTests : IDisposable
|
|||||||
Assert.True(result.Success, result.Message);
|
Assert.True(result.Success, result.Message);
|
||||||
Assert.Contains("re-queued", result.Message);
|
Assert.Contains("re-queued", result.Message);
|
||||||
var request = Assert.IsType<RetryNotificationRequest>(Assert.Single(_outboxRequests));
|
var request = Assert.IsType<RetryNotificationRequest>(Assert.Single(_outboxRequests));
|
||||||
Assert.Equal(c.ToString("D"), request.NotificationId);
|
Assert.Equal(c.ToString("N"), request.NotificationId);
|
||||||
Assert.Equal("tester", request.RequestedBy);
|
Assert.Equal("tester", request.RequestedBy);
|
||||||
Assert.Empty(_siteCallRequests);
|
Assert.Empty(_siteCallRequests);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user