Fix UI types to match actual backend API
All checks were successful
CI / build (push) Successful in 1m0s
CI / docker (push) Successful in 45s
CI / deploy (push) Successful in 29s

Validated against live OpenAPI spec at /api/v1/api-docs. Fixes:
- duration → durationMs (all models)
- Remove processorCount (not in ExecutionSummary)
- Remove ProcessorNode.index and .uri (not in backend)
- ProcessorSnapshot is Record<string,string>, not structured object
- Add missing fields: endTime, diagramContentHash, exchangeId, etc.
- Save openapi.json from live server

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-13 14:23:56 +01:00
parent 1dfe53abee
commit 6f415cb017
6 changed files with 1107 additions and 38 deletions

1071
ui/src/api/openapi.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/**
* Hand-written OpenAPI types matching the cameleer3 server REST API.
* Will be replaced by openapi-typescript codegen once backend is running.
* Types matching the cameleer3 server REST API (validated against live OpenAPI spec).
* Generated from: GET /api/v1/api-docs
*/
export interface paths {
@@ -88,7 +88,7 @@ export interface paths {
responses: {
200: {
content: {
'application/json': ExchangeSnapshot;
'application/json': ProcessorSnapshot;
};
};
404: { content: { 'application/json': { message: string } } };
@@ -137,45 +137,46 @@ export interface ExecutionSummary {
executionId: string;
routeId: string;
agentId: string;
status: 'COMPLETED' | 'FAILED' | 'RUNNING';
status: string;
startTime: string;
duration: number;
processorCount: number;
endTime: string | null;
durationMs: number;
correlationId: string | null;
errorMessage: string | null;
diagramContentHash: string | null;
}
export interface ExecutionDetail {
executionId: string;
routeId: string;
agentId: string;
status: 'COMPLETED' | 'FAILED' | 'RUNNING';
status: string;
startTime: string;
duration: number;
endTime: string | null;
durationMs: number;
correlationId: string | null;
exchangeId: string | null;
errorMessage: string | null;
errorStackTrace: string | null;
diagramContentHash: string | null;
processors: ProcessorNode[];
}
export interface ProcessorNode {
index: number;
processorId: string;
processorType: string;
uri: string | null;
status: 'COMPLETED' | 'FAILED' | 'RUNNING';
duration: number;
status: string;
startTime: string;
endTime: string | null;
durationMs: number;
diagramNodeId: string | null;
errorMessage: string | null;
errorStackTrace: string | null;
children: ProcessorNode[];
}
export interface ExchangeSnapshot {
exchangeId: string;
correlationId: string | null;
bodyType: string | null;
body: string | null;
headers: Record<string, string> | null;
properties: Record<string, string> | null;
}
/** Processor snapshot is a flat key-value map (Map<String, String> in Java) */
export type ProcessorSnapshot = Record<string, string>;
export interface AgentInstance {
agentId: string;

View File

@@ -7,14 +7,16 @@ interface ExchangeDetailProps {
}
export function ExchangeDetail({ execution }: ExchangeDetailProps) {
// Fetch the first processor's snapshot (index 0) for body preview
// Fetch the first processor's snapshot (index 0) — returns Record<string, string>
const { data: snapshot } = useProcessorSnapshot(execution.executionId, 0);
const body = snapshot?.['body'];
return (
<div className={styles.sidebar}>
<h4 className={styles.title}>Exchange Details</h4>
<dl className={styles.kv}>
<dt className={styles.kvKey}>Exchange ID</dt>
<dt className={styles.kvKey}>Execution ID</dt>
<dd className={styles.kvValue}>{execution.executionId}</dd>
<dt className={styles.kvKey}>Correlation</dt>
<dd className={styles.kvValue}>{execution.correlationId ?? '-'}</dd>
@@ -25,15 +27,13 @@ export function ExchangeDetail({ execution }: ExchangeDetailProps) {
<dt className={styles.kvKey}>Timestamp</dt>
<dd className={styles.kvValue}>{new Date(execution.startTime).toISOString()}</dd>
<dt className={styles.kvKey}>Duration</dt>
<dd className={styles.kvValue}>{execution.duration}ms</dd>
<dt className={styles.kvKey}>Processors</dt>
<dd className={styles.kvValue}>{execution.processorCount}</dd>
<dd className={styles.kvValue}>{execution.durationMs}ms</dd>
</dl>
{snapshot?.body && (
{body && (
<div className={styles.bodyPreview}>
<span className={styles.bodyLabel}>Input Body</span>
{snapshot.body}
{body}
</div>
)}

View File

@@ -17,7 +17,7 @@ export function ExecutionExplorer() {
// Derive stats from current search results
const failedCount = results.filter((r) => r.status === 'FAILED').length;
const avgDuration = results.length > 0
? Math.round(results.reduce((sum, r) => sum + r.duration, 0) / results.length)
? Math.round(results.reduce((sum, r) => sum + r.durationMs, 0) / results.length)
: 0;
const showFrom = total > 0 ? offset + 1 : 0;

View File

@@ -35,8 +35,8 @@ export function ProcessorTree({ executionId }: { executionId: string }) {
return (
<div className={styles.tree}>
<h4 className={styles.title}>Processor Execution Tree</h4>
{data.processors.map((proc) => (
<ProcessorNodeView key={proc.index} node={proc} />
{data.processors.map((proc, i) => (
<ProcessorNodeView key={proc.processorId ?? i} node={proc} />
))}
</div>
);
@@ -52,16 +52,15 @@ function ProcessorNodeView({ node }: { node: ProcessorNodeType }) {
<div className={`${styles.procIcon} ${icon.className}`}>{icon.label}</div>
<div className={styles.procInfo}>
<div className={styles.procType}>{node.processorType}</div>
{node.uri && <div className={styles.procUri}>{node.uri}</div>}
</div>
<div className={styles.procTiming}>
<span className={styles.procDuration}>{node.duration}ms</span>
<span className={styles.procDuration}>{node.durationMs}ms</span>
</div>
</div>
{node.children.length > 0 && (
<div className={styles.nested}>
{node.children.map((child) => (
<ProcessorNodeView key={child.index} node={child} />
{node.children.map((child, i) => (
<ProcessorNodeView key={child.processorId ?? i} node={child} />
))}
</div>
)}

View File

@@ -52,7 +52,6 @@ export function ResultsTable({ results, loading }: ResultsTableProps) {
<th className={styles.th}>Route</th>
<th className={styles.th}>Correlation ID</th>
<th className={styles.th}>Duration</th>
<th className={styles.th}>Processors</th>
</tr>
</thead>
<tbody>
@@ -101,13 +100,12 @@ function ResultRow({
{exec.correlationId ?? '-'}
</td>
<td className={styles.td}>
<DurationBar duration={exec.duration} />
<DurationBar duration={exec.durationMs} />
</td>
<td className={`${styles.td} mono text-muted`}>{exec.processorCount}</td>
</tr>
{isExpanded && (
<tr className={styles.detailRowVisible}>
<td className={styles.detailCell} colSpan={8}>
<td className={styles.detailCell} colSpan={7}>
<div className={styles.detailContent}>
<ProcessorTree executionId={exec.executionId} />
<ExchangeDetail execution={exec} />