feat: use endpointUri for cross-route drill-down instead of label parsing
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m2s
CI / docker (push) Successful in 1m0s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 38s

Server:
- Add endpointUri to PositionedNode (from RouteNode)
- Add fromEndpointUri to RouteSummary (catalog API)
- Catalog controller resolves endpoint URI from diagram store

UI:
- Build endpointRouteMap from catalog's fromEndpointUri field
- Drill-down uses exact match on node.endpointUri against the map
- Remove label parsing heuristics (extractTargetEndpoint, camelToKebab)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-28 18:31:08 +01:00
parent 0516207e83
commit e5e6175aca
9 changed files with 67 additions and 44 deletions

View File

@@ -18,30 +18,6 @@ const PADDING = 40;
/** Types that support drill-down — double-click navigates to the target route */
const DRILLDOWN_TYPES = new Set(['DIRECT', 'SEDA']);
/** Extract the target endpoint name from a node's label */
function extractTargetEndpoint(node: DiagramNodeType): string | null {
// Labels like "to: direct:orderProcessing" or "direct:orderProcessing"
const label = node.label ?? '';
const match = label.match(/(?:to:\s*)?(?:direct|seda):(\S+)/i);
return match ? match[1] : null;
}
/** Convert camelCase to kebab-case: "callGetProduct" → "call-get-product" */
function camelToKebab(s: string): string {
return s.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
}
/**
* Resolve a direct/seda endpoint name to a routeId.
* Tries: exact match, kebab-case conversion, then gives up.
*/
function resolveRouteId(endpoint: string, knownRouteIds: Set<string>): string | null {
if (knownRouteIds.has(endpoint)) return endpoint;
const kebab = camelToKebab(endpoint);
if (knownRouteIds.has(kebab)) return kebab;
return null;
}
export function ProcessDiagram({
application,
routeId,
@@ -51,6 +27,7 @@ export function ProcessDiagram({
onNodeAction,
nodeConfigs,
knownRouteIds,
endpointRouteMap,
className,
diagramLayout,
executionOverlay,
@@ -190,17 +167,18 @@ export function ProcessDiagram({
(nodeId: string) => {
const node = findNodeById(sections, nodeId);
if (!node || !DRILLDOWN_TYPES.has(node.type ?? '')) return;
const endpoint = extractTargetEndpoint(node);
if (!endpoint) return;
const resolved = knownRouteIds
? resolveRouteId(endpoint, knownRouteIds)
: endpoint;
// 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);
if (resolved) {
onNodeSelect?.('');
setRouteStack(prev => [...prev, resolved]);
}
},
[sections, onNodeSelect, knownRouteIds],
[sections, onNodeSelect, endpointRouteMap],
);
const handleBreadcrumbClick = useCallback(

View File

@@ -26,6 +26,8 @@ export interface ProcessDiagramProps {
nodeConfigs?: Map<string, NodeConfig>;
/** Known route IDs for this application (enables drill-down resolution) */
knownRouteIds?: Set<string>;
/** Maps from() endpoint URI → routeId for cross-route drill-down */
endpointRouteMap?: Map<string, string>;
className?: string;
/** Pre-fetched diagram layout (bypasses internal fetch by application/routeId) */
diagramLayout?: DiagramLayout;