feat: trace data indicators, inline tap config, and detail tab gating
Trace data visibility: - ProcessorNode now includes hasTraceData flag computed from captured body/headers during tree conversion - ConfigBadge shows teal for tracing configured, green when data captured - Search results show green footprints icon for exchanges with trace data - New has_trace_data column on executions table (V11 migration with backfill) - OpenSearch documents and ExecutionSummary include the flag Inline tap configuration: - Extracted reusable TapConfigModal component from RouteDetail - Diagram context menu opens tap modal inline instead of navigating away - Toggle-trace action works immediately with toast feedback - Modal closes only on ESC, Cancel, Save, or Delete (not backdrop click) Detail panel tab gating: - Headers, Input, Output tabs disabled when no data is available - Works at both exchange and processor level - Falls back to Info tab when active tab becomes empty Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -397,7 +397,8 @@ public class OpenSearchIndex implements SearchIndex {
|
||||
(String) src.get("error_message"),
|
||||
null, // diagramContentHash not stored in index
|
||||
extractHighlight(hit),
|
||||
attributes
|
||||
attributes,
|
||||
Boolean.TRUE.equals(src.get("has_trace_data"))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,10 +31,10 @@ public class PostgresExecutionStore implements ExecutionStore {
|
||||
attributes,
|
||||
error_type, error_category, root_cause_type, root_cause_message,
|
||||
trace_id, span_id,
|
||||
processors_json,
|
||||
processors_json, has_trace_data,
|
||||
created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?::jsonb, ?::jsonb, ?::jsonb,
|
||||
?, ?, ?, ?, ?, ?, ?::jsonb, now(), now())
|
||||
?, ?, ?, ?, ?, ?, ?::jsonb, ?, now(), now())
|
||||
ON CONFLICT (execution_id, start_time) DO UPDATE SET
|
||||
status = CASE
|
||||
WHEN EXCLUDED.status IN ('COMPLETED', 'FAILED')
|
||||
@@ -61,6 +61,7 @@ public class PostgresExecutionStore implements ExecutionStore {
|
||||
trace_id = COALESCE(EXCLUDED.trace_id, executions.trace_id),
|
||||
span_id = COALESCE(EXCLUDED.span_id, executions.span_id),
|
||||
processors_json = COALESCE(EXCLUDED.processors_json, executions.processors_json),
|
||||
has_trace_data = EXCLUDED.has_trace_data OR executions.has_trace_data,
|
||||
updated_at = now()
|
||||
""",
|
||||
execution.executionId(), execution.routeId(), execution.agentId(),
|
||||
@@ -77,7 +78,7 @@ public class PostgresExecutionStore implements ExecutionStore {
|
||||
execution.errorType(), execution.errorCategory(),
|
||||
execution.rootCauseType(), execution.rootCauseMessage(),
|
||||
execution.traceId(), execution.spanId(),
|
||||
execution.processorsJson());
|
||||
execution.processorsJson(), execution.hasTraceData());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -178,7 +179,8 @@ public class PostgresExecutionStore implements ExecutionStore {
|
||||
rs.getString("error_type"), rs.getString("error_category"),
|
||||
rs.getString("root_cause_type"), rs.getString("root_cause_message"),
|
||||
rs.getString("trace_id"), rs.getString("span_id"),
|
||||
rs.getString("processors_json"));
|
||||
rs.getString("processors_json"),
|
||||
rs.getBoolean("has_trace_data"));
|
||||
|
||||
private static final RowMapper<ProcessorRecord> PROCESSOR_MAPPER = (rs, rowNum) ->
|
||||
new ProcessorRecord(
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
-- Flag indicating whether any processor in this execution captured trace data
|
||||
ALTER TABLE executions ADD COLUMN IF NOT EXISTS has_trace_data BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
|
||||
-- Backfill: set flag for existing executions that have processor trace data
|
||||
UPDATE executions e SET has_trace_data = TRUE
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM processor_executions pe
|
||||
WHERE pe.execution_id = e.execution_id
|
||||
AND (pe.input_body IS NOT NULL OR pe.output_body IS NOT NULL)
|
||||
);
|
||||
@@ -36,7 +36,7 @@ class OpenSearchIndexIT extends AbstractPostgresIT {
|
||||
"OrderNotFoundException: order-12345 not found", null,
|
||||
List.of(new ProcessorDoc("proc-1", "log", "COMPLETED",
|
||||
null, null, "request body with customer-99", null, null, null, null)),
|
||||
null);
|
||||
null, false);
|
||||
|
||||
searchIndex.index(doc);
|
||||
refreshOpenSearchIndices();
|
||||
@@ -62,7 +62,7 @@ class OpenSearchIndexIT extends AbstractPostgresIT {
|
||||
now, now.plusMillis(50), 50L, null, null,
|
||||
List.of(new ProcessorDoc("proc-1", "bean", "COMPLETED",
|
||||
null, null, "UniquePayloadIdentifier12345", null, null, null, null)),
|
||||
null);
|
||||
null, false);
|
||||
|
||||
searchIndex.index(doc);
|
||||
refreshOpenSearchIndices();
|
||||
|
||||
@@ -27,7 +27,7 @@ class PostgresExecutionStoreIT extends AbstractPostgresIT {
|
||||
now, now.plusMillis(100), 100L,
|
||||
null, null, null,
|
||||
"REGULAR", null, null, null, null, null,
|
||||
null, null, null, null, null, null, null);
|
||||
null, null, null, null, null, null, null, false);
|
||||
|
||||
executionStore.upsert(record);
|
||||
Optional<ExecutionRecord> found = executionStore.findById("exec-1");
|
||||
@@ -45,12 +45,12 @@ class PostgresExecutionStoreIT extends AbstractPostgresIT {
|
||||
"exec-dup", "route-a", "agent-1", "app-1",
|
||||
"RUNNING", null, null, now, null, null, null, null, null,
|
||||
null, null, null, null, null, null,
|
||||
null, null, null, null, null, null, null);
|
||||
null, null, null, null, null, null, null, false);
|
||||
ExecutionRecord second = new ExecutionRecord(
|
||||
"exec-dup", "route-a", "agent-1", "app-1",
|
||||
"COMPLETED", null, null, now, now.plusMillis(200), 200L, null, null, null,
|
||||
"COMPLETE", null, null, null, null, null,
|
||||
null, null, null, null, null, null, null);
|
||||
null, null, null, null, null, null, null, false);
|
||||
|
||||
executionStore.upsert(first);
|
||||
executionStore.upsert(second);
|
||||
@@ -68,7 +68,7 @@ class PostgresExecutionStoreIT extends AbstractPostgresIT {
|
||||
"exec-proc", "route-a", "agent-1", "app-1",
|
||||
"COMPLETED", null, null, now, now.plusMillis(50), 50L, null, null, null,
|
||||
"COMPLETE", null, null, null, null, null,
|
||||
null, null, null, null, null, null, null);
|
||||
null, null, null, null, null, null, null, false);
|
||||
executionStore.upsert(exec);
|
||||
|
||||
List<ProcessorRecord> processors = List.of(
|
||||
|
||||
@@ -61,6 +61,6 @@ class PostgresStatsStoreIT extends AbstractPostgresIT {
|
||||
startTime, startTime.plusMillis(durationMs), durationMs,
|
||||
status.equals("FAILED") ? "error" : null, null, null,
|
||||
null, null, null, null, null, null,
|
||||
null, null, null, null, null, null, null));
|
||||
null, null, null, null, null, null, null, false));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user