feat: resolved URI display and drill-down for TO/TO_DYNAMIC nodes
- Show resolved endpoint URI as teal italic line on diagram nodes when execution overlay is active - Enable drill-down for TO and TO_DYNAMIC nodes (not just DIRECT/SEDA) - Use runtime resolvedEndpointUri from execution overlay for drill-down when static endpointUri doesn't match - Increase node height from 50px to 56px to accommodate the third line Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -53,7 +53,7 @@ public class ElkDiagramRenderer implements DiagramRenderer {
|
||||
}
|
||||
|
||||
private static final int PADDING = 20;
|
||||
private static final int NODE_HEIGHT = 50;
|
||||
private static final int NODE_HEIGHT = 56;
|
||||
private static final int NODE_WIDTH = 220;
|
||||
private static final int COMPOUND_TOP_PADDING = 30;
|
||||
private static final int COMPOUND_SIDE_PADDING = 10;
|
||||
|
||||
@@ -10,6 +10,8 @@ export interface NodeExecutionState {
|
||||
subRouteFailed?: boolean;
|
||||
/** True if trace data is available for this processor */
|
||||
hasTraceData?: boolean;
|
||||
/** Runtime-resolved endpoint URI (for TO_DYNAMIC, etc.) */
|
||||
resolvedEndpointUri?: string;
|
||||
}
|
||||
|
||||
export interface IterationInfo {
|
||||
|
||||
@@ -60,6 +60,7 @@ function buildOverlay(
|
||||
durationMs: proc.durationMs ?? 0,
|
||||
subRouteFailed: subRouteFailed || undefined,
|
||||
hasTraceData: !!proc.hasTraceData,
|
||||
resolvedEndpointUri: proc.resolvedEndpointUri || undefined,
|
||||
});
|
||||
|
||||
// Recurse into children
|
||||
|
||||
@@ -42,6 +42,7 @@ export function DiagramNode({
|
||||
// Extract label parts: type name and detail
|
||||
const typeName = node.type?.replace(/^EIP_/, '').replace(/_/g, ' ') ?? '';
|
||||
const detail = node.label || '';
|
||||
const resolvedUri = executionState?.resolvedEndpointUri;
|
||||
|
||||
// Overlay state derivation
|
||||
const isCompleted = executionState?.status === 'COMPLETED';
|
||||
@@ -125,15 +126,33 @@ export function DiagramNode({
|
||||
)}
|
||||
</g>
|
||||
|
||||
{/* Type name + detail (clipped to available width) */}
|
||||
{/* Type name + detail + resolved URI (clipped to available width) */}
|
||||
<g clipPath={`url(#clip-${node.id})`}>
|
||||
<text x={TEXT_LEFT} y={h / 2 - 1} fill={labelColor} fontSize={11} fontWeight={600}>
|
||||
{typeName}
|
||||
</text>
|
||||
{detail && detail !== typeName && (
|
||||
<text x={TEXT_LEFT} y={h / 2 + 12} fill={isFailed ? '#C0392B' : '#5C5347'} fontSize={10}>
|
||||
{detail}
|
||||
</text>
|
||||
{resolvedUri ? (
|
||||
<>
|
||||
<text x={TEXT_LEFT} y={TOP_BAR_HEIGHT + 12} fill={labelColor} fontSize={11} fontWeight={600}>
|
||||
{typeName}
|
||||
</text>
|
||||
{detail && detail !== typeName && (
|
||||
<text x={TEXT_LEFT} y={TOP_BAR_HEIGHT + 24} fill={isFailed ? '#C0392B' : '#5C5347'} fontSize={10}>
|
||||
{detail}
|
||||
</text>
|
||||
)}
|
||||
<text x={TEXT_LEFT} y={h - 5} fill="#1A7F8E" fontSize={9} fontStyle="italic">
|
||||
→ {resolvedUri.split('?')[0]}
|
||||
</text>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<text x={TEXT_LEFT} y={h / 2 - 1} fill={labelColor} fontSize={11} fontWeight={600}>
|
||||
{typeName}
|
||||
</text>
|
||||
{detail && detail !== typeName && (
|
||||
<text x={TEXT_LEFT} y={h / 2 + 12} fill={isFailed ? '#C0392B' : '#5C5347'} fontSize={10}>
|
||||
{detail}
|
||||
</text>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</g>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import styles from './ProcessDiagram.module.css';
|
||||
const PADDING = 40;
|
||||
|
||||
/** Types that support drill-down — double-click navigates to the target route */
|
||||
const DRILLDOWN_TYPES = new Set(['DIRECT', 'SEDA']);
|
||||
const DRILLDOWN_TYPES = new Set(['DIRECT', 'SEDA', 'TO', 'TO_DYNAMIC']);
|
||||
|
||||
export function ProcessDiagram({
|
||||
application,
|
||||
@@ -168,17 +168,26 @@ export function ProcessDiagram({
|
||||
const node = findNodeById(sections, nodeId);
|
||||
if (!node || !DRILLDOWN_TYPES.has(node.type ?? '')) return;
|
||||
|
||||
// Resolve via endpointUri → endpointRouteMap (exact match, no heuristics)
|
||||
const uri = node.endpointUri;
|
||||
if (!uri) return;
|
||||
const stripped = uri.split('?')[0];
|
||||
const resolved = endpointRouteMap?.get(stripped);
|
||||
// Try static endpointUri first, then runtime-resolved URI from execution overlay
|
||||
const staticUri = node.endpointUri;
|
||||
const runtimeUri = executionOverlay?.get(nodeId)?.resolvedEndpointUri;
|
||||
|
||||
function normalize(uri: string): string {
|
||||
return uri.split('?')[0];
|
||||
}
|
||||
|
||||
function tryResolve(uri: string | undefined): string | undefined {
|
||||
if (!uri) return undefined;
|
||||
return endpointRouteMap?.get(normalize(uri));
|
||||
}
|
||||
|
||||
const resolved = tryResolve(staticUri) ?? tryResolve(runtimeUri);
|
||||
if (resolved) {
|
||||
onNodeSelect?.('');
|
||||
setRouteStack(prev => [...prev, resolved]);
|
||||
}
|
||||
},
|
||||
[sections, onNodeSelect, endpointRouteMap],
|
||||
[sections, onNodeSelect, endpointRouteMap, executionOverlay],
|
||||
);
|
||||
|
||||
const handleBreadcrumbClick = useCallback(
|
||||
@@ -201,7 +210,6 @@ export function ProcessDiagram({
|
||||
(e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
if (routeStack.length > 1) {
|
||||
// Go back one level
|
||||
setRouteStack(prev => prev.slice(0, -1));
|
||||
} else {
|
||||
onNodeSelect?.('');
|
||||
|
||||
Reference in New Issue
Block a user