fix: update frontend field names for identity rename (applicationId, instanceId)
Some checks failed
CI / cleanup-branch (push) Has been skipped
CI / build (push) Failing after 32s
CI / docker (push) Has been skipped
CI / deploy (push) Has been skipped
CI / deploy-feature (push) Has been skipped

The backend identity rename (applicationName → applicationId,
agentId → instanceId) was not reflected in the frontend. This caused
drilldown to fail (detail.applicationName was undefined, disabling
the diagram fetch) and various display issues.

Updated schema.d.ts, ExchangeHeader, ExecutionDiagram, Dashboard,
AgentHealth, AgentInstance, LayoutShell, LogTab, InfoTab, DetailPanel,
ExchangesPage, and tracing-store.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-01 18:22:16 +02:00
parent aa2d203f4e
commit 4cdbcdaeea
12 changed files with 47 additions and 47 deletions

View File

@@ -1487,8 +1487,8 @@ export interface components {
ExecutionSummary: {
executionId: string;
routeId: string;
agentId: string;
applicationName: string;
instanceId: string;
applicationId: string;
status: string;
/** Format: date-time */
startTime: string;
@@ -1807,8 +1807,8 @@ export interface components {
ExecutionDetail: {
executionId: string;
routeId: string;
agentId: string;
applicationName: string;
instanceId: string;
applicationId: string;
status: string;
/** Format: date-time */
startTime: string;
@@ -1959,8 +1959,8 @@ export interface components {
AgentEventResponse: {
/** Format: int64 */
id: number;
agentId: string;
appId: string;
instanceId: string;
applicationId: string;
eventType: string;
detail: string;
/** Format: date-time */

View File

@@ -159,7 +159,7 @@ export function DetailPanel({
)}
{activeTab === 'log' && (
<LogTab
applicationName={executionDetail.applicationName}
applicationId={executionDetail.applicationId}
exchangeId={executionDetail.exchangeId}
processorId={selectedProcessor?.processorId ?? null}
/>

View File

@@ -193,7 +193,7 @@ export function ExecutionDiagram({
JSON
</button>
<ProcessDiagram
application={detail.applicationName}
application={detail.applicationId}
routeId={detail.routeId}
direction={direction}
diagramLayout={diagramLayout}

View File

@@ -96,7 +96,7 @@ export function InfoTab({ processor, executionDetail }: InfoTabProps) {
<Field label="Correlation ID" value={executionDetail.correlationId} mono />
<Field label="Exchange ID" value={executionDetail.exchangeId} mono />
<Field label="Application" value={executionDetail.applicationName} />
<Field label="Application" value={executionDetail.applicationId} />
<Field label="Route ID" value={executionDetail.routeId} />
<div>
<div className={styles.fieldLabel}>Status</div>

View File

@@ -4,7 +4,7 @@ import type { LogEntryResponse } from '../../../api/queries/logs';
import styles from '../ExecutionDiagram.module.css';
interface LogTabProps {
applicationName: string;
applicationId: string;
exchangeId?: string;
processorId: string | null;
}
@@ -28,11 +28,11 @@ function formatTime(iso: string): string {
return `${h}:${m}:${s}.${ms}`;
}
export function LogTab({ applicationName, exchangeId, processorId }: LogTabProps) {
export function LogTab({ applicationId, exchangeId, processorId }: LogTabProps) {
const [filter, setFilter] = useState('');
const { data: logs, isLoading } = useApplicationLogs(
applicationName,
applicationId,
undefined,
{ exchangeId, limit: 500 },
);

View File

@@ -138,8 +138,8 @@ function LayoutContent() {
category: 'exchange' as const,
title: e.executionId,
badges: [{ label: e.status, color: statusToColor(e.status) }],
meta: `${e.routeId} · ${e.applicationName ?? ''} · ${formatDuration(e.durationMs)}`,
path: `/exchanges/${e.applicationName ?? ''}/${e.routeId}/${e.executionId}`,
meta: `${e.routeId} · ${e.applicationId ?? ''} · ${formatDuration(e.durationMs)}`,
path: `/exchanges/${e.applicationId ?? ''}/${e.routeId}/${e.executionId}`,
serverFiltered: true,
matchContext: e.highlight ?? undefined,
}));
@@ -156,8 +156,8 @@ function LayoutContent() {
category: 'attribute' as const,
title: `${key} = "${value}"`,
badges: [{ label: e.status, color: statusToColor(e.status) }],
meta: `${e.executionId} · ${e.routeId} · ${e.applicationName ?? ''}`,
path: `/exchanges/${e.applicationName ?? ''}/${e.routeId}/${e.executionId}`,
meta: `${e.executionId} · ${e.routeId} · ${e.applicationId ?? ''}`,
path: `/exchanges/${e.applicationId ?? ''}/${e.routeId}/${e.executionId}`,
serverFiltered: true,
});
}
@@ -218,7 +218,7 @@ function LayoutContent() {
if (parts.length === 4 && parts[0] === 'exchanges') {
state.selectedExchange = {
executionId: parts[3],
applicationName: parts[1],
applicationId: parts[1],
routeId: parts[2],
};
}

View File

@@ -199,7 +199,7 @@ export default function AgentHealth() {
: e.eventType === 'RECOVERED'
? ('success' as const)
: ('running' as const),
message: `${e.agentId}: ${e.eventType}${e.detail ? ' \u2014 ' + e.detail : ''}`,
message: `${e.instanceId}: ${e.eventType}${e.detail ? ' \u2014 ' + e.detail : ''}`,
timestamp: new Date(e.timestamp),
}));
return eventSortAsc ? mapped.toReversed() : mapped;

View File

@@ -83,7 +83,7 @@ export default function AgentInstance() {
const feedEvents = useMemo<FeedEvent[]>(() => {
const mapped = (events || [])
.filter((e: any) => !instanceId || e.agentId === instanceId)
.filter((e: any) => !instanceId || e.instanceId === instanceId)
.map((e: any) => ({
id: String(e.id),
severity:

View File

@@ -92,11 +92,11 @@ function buildBaseColumns(): Column<Row>[] {
),
},
{
key: 'applicationName',
key: 'applicationId',
header: 'Application',
sortable: true,
render: (_: unknown, row: Row) => (
<span className={styles.appName}>{row.applicationName ?? ''}</span>
<span className={styles.appName}>{row.applicationId ?? ''}</span>
),
},
{
@@ -147,12 +147,12 @@ function buildBaseColumns(): Column<Row>[] {
),
},
{
key: 'agentId',
key: 'instanceId',
header: 'Agent',
render: (_: unknown, row: Row) => (
<span className={styles.agentBadge}>
<span className={styles.agentDot} />
{row.agentId}
{row.instanceId}
</span>
),
},
@@ -163,7 +163,7 @@ function buildBaseColumns(): Column<Row>[] {
export interface SelectedExchange {
executionId: string;
applicationName: string;
applicationId: string;
routeId: string;
}
@@ -226,7 +226,7 @@ export default function Dashboard({ onExchangeSelect }: DashboardProps = {}) {
if (onExchangeSelect) {
onExchangeSelect({
executionId: row.executionId,
applicationName: row.applicationName ?? '',
applicationId: row.applicationId ?? '',
routeId: row.routeId,
})
}

View File

@@ -12,7 +12,7 @@ import styles from './ExchangeHeader.module.css';
interface ExchangeHeaderProps {
detail: ExecutionDetail;
onCorrelatedSelect?: (executionId: string, applicationName: string, routeId: string) => void;
onCorrelatedSelect?: (executionId: string, applicationId: string, routeId: string) => void;
onClearSelection?: () => void;
}
@@ -50,17 +50,17 @@ export function ExchangeHeader({ detail, onCorrelatedSelect, onClearSelection }:
const attrs = Object.entries(detail.attributes ?? {});
// Look up agent state for icon coloring + route control capability
const { data: agents } = useAgents(undefined, detail.applicationName);
const { data: agents } = useAgents(undefined, detail.applicationId);
const { agentState, hasRouteControl, hasReplay } = useMemo(() => {
if (!agents) return { agentState: undefined, hasRouteControl: false, hasReplay: false };
const agentList = agents as any[];
const agent = detail.agentId ? agentList.find((a: any) => a.id === detail.agentId) : undefined;
const agent = detail.instanceId ? agentList.find((a: any) => a.id === detail.instanceId) : undefined;
return {
agentState: agent?.state?.toLowerCase() as 'live' | 'stale' | 'dead' | undefined,
hasRouteControl: agentList.some((a: any) => a.capabilities?.routeControl === true),
hasReplay: agentList.some((a: any) => a.capabilities?.replay === true),
};
}, [agents, detail.agentId]);
}, [agents, detail.instanceId]);
const roles = useAuthStore((s) => s.roles);
const canControl = roles.some(r => r === 'OPERATOR' || r === 'ADMIN');
@@ -80,21 +80,21 @@ export function ExchangeHeader({ detail, onCorrelatedSelect, onClearSelection }:
</>
)}
<span className={styles.separator} />
<button className={styles.linkBtn} onClick={() => { onClearSelection?.(); navigate(`/exchanges/${detail.applicationName}`); }} title="Show all exchanges for this application">
<span className={styles.app}>{detail.applicationName}</span>
<button className={styles.linkBtn} onClick={() => { onClearSelection?.(); navigate(`/exchanges/${detail.applicationId}`); }} title="Show all exchanges for this application">
<span className={styles.app}>{detail.applicationId}</span>
</button>
<button className={styles.linkBtn} onClick={() => { onClearSelection?.(); navigate(`/exchanges/${detail.applicationName}/${detail.routeId}`); }} title="Show all exchanges for this route">
<button className={styles.linkBtn} onClick={() => { onClearSelection?.(); navigate(`/exchanges/${detail.applicationId}/${detail.routeId}`); }} title="Show all exchanges for this route">
<span className={styles.route}>{detail.routeId}</span>
<GitBranch size={12} className={styles.icon} />
</button>
{detail.agentId && (
{detail.instanceId && (
<>
<span className={styles.separator} />
<button className={styles.linkBtn} onClick={() => navigate(`/runtime/${detail.applicationName}`)} title="All agents for this application">
<span className={styles.app}>{detail.applicationName}</span>
<button className={styles.linkBtn} onClick={() => navigate(`/runtime/${detail.applicationId}`)} title="All agents for this application">
<span className={styles.app}>{detail.applicationId}</span>
</button>
<button className={styles.linkBtn} onClick={() => navigate(`/runtime/${detail.applicationName}/${detail.agentId}`)} title="Agent details">
<MonoText size="xs">{detail.agentId}</MonoText>
<button className={styles.linkBtn} onClick={() => navigate(`/runtime/${detail.applicationId}/${detail.instanceId}`)} title="Agent details">
<MonoText size="xs">{detail.instanceId}</MonoText>
<Server size={12} className={agentState === 'live' ? styles.iconLive : agentState === 'stale' ? styles.iconStale : agentState === 'dead' ? styles.iconDead : styles.icon} />
</button>
</>
@@ -105,11 +105,11 @@ export function ExchangeHeader({ detail, onCorrelatedSelect, onClearSelection }:
{/* Route control / replay — only if agent supports it AND user has operator+ role */}
{canControl && (hasRouteControl || hasReplay) && (
<RouteControlBar
application={detail.applicationName}
application={detail.applicationId}
routeId={detail.routeId}
hasRouteControl={hasRouteControl}
hasReplay={hasReplay}
agentId={detail.agentId}
agentId={detail.instanceId}
exchangeId={detail.exchangeId}
inputHeaders={detail.inputHeaders}
inputBody={detail.inputBody}
@@ -135,7 +135,7 @@ export function ExchangeHeader({ detail, onCorrelatedSelect, onClearSelection }:
className={`${styles.chainNode} ${statusCls} ${isCurrent ? styles.chainNodeCurrent : ''}`}
onClick={() => {
if (!isCurrent && onCorrelatedSelect) {
onCorrelatedSelect(ce.executionId, ce.applicationName ?? detail.applicationName, ce.routeId);
onCorrelatedSelect(ce.executionId, ce.applicationId ?? detail.applicationId, ce.routeId);
}
}}
title={`${ce.executionId}\n${ce.routeId} \u2014 ${formatDuration(ce.durationMs)}${isReplay ? '\n(replay)' : ''}`}

View File

@@ -29,7 +29,7 @@ export default function ExchangesPage() {
// Derive selection from URL params when no state-based selection exists (Cmd-K, bookmarks)
const urlDerivedExchange: SelectedExchange | null =
(scopedExchangeId && scopedAppId && scopedRouteId)
? { executionId: scopedExchangeId, applicationName: scopedAppId, routeId: scopedRouteId }
? { executionId: scopedExchangeId, applicationId: scopedAppId, routeId: scopedRouteId }
: null;
const [selected, setSelectedInternal] = useState<SelectedExchange | null>(stateSelected ?? urlDerivedExchange);
@@ -42,7 +42,7 @@ export default function ExchangesPage() {
} else if (scopedExchangeId && scopedAppId && scopedRouteId) {
setSelectedInternal({
executionId: scopedExchangeId,
applicationName: scopedAppId,
applicationId: scopedAppId,
routeId: scopedRouteId,
});
} else {
@@ -62,8 +62,8 @@ export default function ExchangesPage() {
}, [navigate, location.pathname, location.search, location.state]);
// Select a correlated exchange: push another history entry
const handleCorrelatedSelect = useCallback((executionId: string, applicationName: string, routeId: string) => {
const exchange = { executionId, applicationName, routeId };
const handleCorrelatedSelect = useCallback((executionId: string, applicationId: string, routeId: string) => {
const exchange = { executionId, applicationId, routeId };
setSelectedInternal(exchange);
navigate(location.pathname + location.search, {
state: { ...location.state, selectedExchange: exchange },
@@ -106,7 +106,7 @@ export default function ExchangesPage() {
}
// Determine what the right panel shows
const panelAppId = selected?.applicationName ?? scopedAppId!;
const panelAppId = selected?.applicationId ?? scopedAppId!;
const panelRouteId = selected?.routeId ?? scopedRouteId!;
const panelExchangeId = selected?.executionId ?? undefined;
@@ -135,7 +135,7 @@ interface DiagramPanelProps {
appId: string;
routeId: string;
exchangeId?: string;
onCorrelatedSelect: (executionId: string, applicationName: string, routeId: string) => void;
onCorrelatedSelect: (executionId: string, applicationId: string, routeId: string) => void;
onClearSelection: () => void;
}

View File

@@ -3,7 +3,7 @@ import { create } from 'zustand'
type CaptureMode = 'NONE' | 'INPUT' | 'OUTPUT' | 'BOTH'
interface TracingState {
/** Key: applicationName → { processorId: captureMode } */
/** Key: applicationId → { processorId: captureMode } */
tracedProcessors: Record<string, Record<string, CaptureMode>>
isTraced: (app: string, processorId: string) => boolean
/** Toggle processor tracing (BOTH on, remove on off). Returns the full map for the app. */