From b3c5e87230a0b1be72f0d0384f48c0e652e939e6 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Wed, 25 Mar 2026 22:02:26 +0100 Subject: [PATCH] fix: expose exchange body in API, fix RouteFlow index mapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add inputBody/outputBody/inputHeaders/outputHeaders to ExecutionDetail DTO so exchange-level bodies are returned by the detail endpoint. Show "Exchange Input" and "Exchange Output" panels on the detail page when the data is available. Fix RouteFlow node click selecting the wrong processor snapshot by building a flowToTreeIndex mapping that correctly translates flow display index → diagram node index → processorId → processor tree index. Previously the diagram node index was used directly as the processor tree index, which broke when the two orderings differed. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../server/core/detail/DetailService.java | 4 +- .../server/core/detail/ExecutionDetail.java | 10 ++- .../pages/ExchangeDetail/ExchangeDetail.tsx | 78 ++++++++++++++++++- 3 files changed, 87 insertions(+), 5 deletions(-) diff --git a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/DetailService.java b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/DetailService.java index 1ab9cb81..eef8fa68 100644 --- a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/DetailService.java +++ b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/DetailService.java @@ -25,7 +25,9 @@ public class DetailService { exec.durationMs() != null ? exec.durationMs() : 0L, exec.correlationId(), exec.exchangeId(), exec.errorMessage(), exec.errorStacktrace(), - exec.diagramContentHash(), roots + exec.diagramContentHash(), roots, + exec.inputBody(), exec.outputBody(), + exec.inputHeaders(), exec.outputHeaders() ); }); } diff --git a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/ExecutionDetail.java b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/ExecutionDetail.java index ba133b2a..e2790cd7 100644 --- a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/ExecutionDetail.java +++ b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/ExecutionDetail.java @@ -22,6 +22,10 @@ import java.util.List; * @param errorStackTrace error stack trace (empty string if no error) * @param diagramContentHash content hash linking to the active route diagram version * @param processors nested processor execution tree (root nodes) + * @param inputBody exchange input body at route entry (null if not captured) + * @param outputBody exchange output body at route exit (null if not captured) + * @param inputHeaders exchange input headers at route entry (null if not captured) + * @param outputHeaders exchange output headers at route exit (null if not captured) */ public record ExecutionDetail( String executionId, @@ -37,6 +41,10 @@ public record ExecutionDetail( String errorMessage, String errorStackTrace, String diagramContentHash, - List processors + List processors, + String inputBody, + String outputBody, + String inputHeaders, + String outputHeaders ) { } diff --git a/ui/src/pages/ExchangeDetail/ExchangeDetail.tsx b/ui/src/pages/ExchangeDetail/ExchangeDetail.tsx index bf5cfe58..9e32591e 100644 --- a/ui/src/pages/ExchangeDetail/ExchangeDetail.tsx +++ b/ui/src/pages/ExchangeDetail/ExchangeDetail.tsx @@ -171,7 +171,7 @@ export default function ExchangeDetail() { return ids }, [procList]) - // ProcessorId lookup: flow node index → processorId (diagram order) + // ProcessorId lookup: diagram node index → processorId const flowProcessorIds: string[] = useMemo(() => { if (!diagram?.nodes) return processorIds const flatProcs: Array<{ diagramNodeId?: string; processorId?: string }> = [] @@ -188,6 +188,15 @@ export default function ExchangeDetail() { return diagram.nodes.map(node => lookup.get(node.id ?? '') ?? node.id ?? '') }, [diagram, procList, processorIds]) + // Map flow display index → processor tree index (for snapshot API) + const flowToTreeIndex = useMemo(() => + flowIndexMap.map(diagramIdx => { + const pid = flowProcessorIds[diagramIdx] + return pid ? processorIds.indexOf(pid) : -1 + }), + [flowIndexMap, flowProcessorIds, processorIds], + ) + // ── Tracing toggle ────────────────────────────────────────────────────── const { toast } = useToast() const tracingStore = useTracingStore() @@ -395,8 +404,11 @@ export default function ExchangeDetail() { routeFlows.length > 0 ? ( setSelectedProcessorIndex(flowIndexMap[index] ?? index)} - selectedIndex={flowIndexMap.indexOf(activeIndex)} + onNodeClick={(_node, index) => { + const treeIdx = flowToTreeIndex[index] + if (treeIdx >= 0) setSelectedProcessorIndex(treeIdx) + }} + selectedIndex={flowToTreeIndex.indexOf(activeIndex)} getActions={(_node, index) => { const origIdx = flowIndexMap[index] ?? index const pid = flowProcessorIds[origIdx] @@ -415,6 +427,66 @@ export default function ExchangeDetail() { + {/* Exchange-level body (start/end of route) */} + {detail && (detail.inputBody || detail.outputBody) && ( +
+
+
+ + Exchange Input + + at route entry +
+
+ {detail.inputHeaders && ( +
+
Headers
+
+ {Object.entries(parseHeaders(detail.inputHeaders)).map(([key, value]) => ( +
+ {key} + {value} +
+ ))} +
+
+ )} +
+
Body
+ +
+
+
+
+
+ + Exchange Output + + at route exit +
+
+ {detail.outputHeaders && ( +
+
Headers
+
+ {Object.entries(parseHeaders(detail.outputHeaders)).map(([key, value]) => ( +
+ {key} + {value} +
+ ))} +
+
+ )} +
+
Body
+ +
+
+
+
+ )} + {/* Processor Detail Panel (split IN / OUT) */} {selectedProc && snapshot && (