refactor: remove diagramNodeId indirection, use processorId directly
Agent now uses Camel processorId as RouteNode.id, eliminating the nodeId mapping layer. Drop diagram_node_id column (V6 migration), remove from ProcessorRecord/ProcessorNode/IngestionService/DetailService, add /processor-routes endpoint for processorId→routeId lookup, simplify frontend diagram-mapping and ExchangeDetail overlays, replace N diagram fetches in AppConfigPage with single hook. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import com.cameleer3.server.core.agent.AgentRegistryService;
|
||||
import com.cameleer3.server.core.agent.AgentState;
|
||||
import com.cameleer3.server.core.agent.CommandReply;
|
||||
import com.cameleer3.server.core.agent.CommandType;
|
||||
import com.cameleer3.server.core.storage.ExecutionStore;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -48,15 +49,18 @@ public class ApplicationConfigController {
|
||||
private final AgentRegistryService registryService;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final AuditService auditService;
|
||||
private final ExecutionStore executionStore;
|
||||
|
||||
public ApplicationConfigController(PostgresApplicationConfigRepository configRepository,
|
||||
AgentRegistryService registryService,
|
||||
ObjectMapper objectMapper,
|
||||
AuditService auditService) {
|
||||
AuditService auditService,
|
||||
ExecutionStore executionStore) {
|
||||
this.configRepository = configRepository;
|
||||
this.registryService = registryService;
|
||||
this.objectMapper = objectMapper;
|
||||
this.auditService = auditService;
|
||||
this.executionStore = executionStore;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@@ -103,6 +107,14 @@ public class ApplicationConfigController {
|
||||
return ResponseEntity.ok(saved);
|
||||
}
|
||||
|
||||
@GetMapping("/{application}/processor-routes")
|
||||
@Operation(summary = "Get processor to route mapping",
|
||||
description = "Returns a map of processorId → routeId for all processors seen in this application")
|
||||
@ApiResponse(responseCode = "200", description = "Mapping returned")
|
||||
public ResponseEntity<Map<String, String>> getProcessorRouteMapping(@PathVariable String application) {
|
||||
return ResponseEntity.ok(executionStore.findProcessorRouteMapping(application));
|
||||
}
|
||||
|
||||
@PostMapping("/{application}/test-expression")
|
||||
@Operation(summary = "Test a tap expression against sample data via a live agent")
|
||||
@ApiResponse(responseCode = "200", description = "Expression evaluated successfully")
|
||||
|
||||
@@ -9,7 +9,9 @@ import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
@@ -70,10 +72,10 @@ public class PostgresExecutionStore implements ExecutionStore {
|
||||
List<ProcessorRecord> processors) {
|
||||
jdbc.batchUpdate("""
|
||||
INSERT INTO processor_executions (execution_id, processor_id, processor_type,
|
||||
diagram_node_id, application_name, route_id, depth, parent_processor_id,
|
||||
application_name, route_id, depth, parent_processor_id,
|
||||
status, start_time, end_time, duration_ms, error_message, error_stacktrace,
|
||||
input_body, output_body, input_headers, output_headers, attributes)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?::jsonb, ?::jsonb, ?::jsonb)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?::jsonb, ?::jsonb, ?::jsonb)
|
||||
ON CONFLICT (execution_id, processor_id, start_time) DO UPDATE SET
|
||||
status = EXCLUDED.status,
|
||||
end_time = COALESCE(EXCLUDED.end_time, processor_executions.end_time),
|
||||
@@ -88,7 +90,7 @@ public class PostgresExecutionStore implements ExecutionStore {
|
||||
""",
|
||||
processors.stream().map(p -> new Object[]{
|
||||
p.executionId(), p.processorId(), p.processorType(),
|
||||
p.diagramNodeId(), p.applicationName(), p.routeId(),
|
||||
p.applicationName(), p.routeId(),
|
||||
p.depth(), p.parentProcessorId(), p.status(),
|
||||
Timestamp.from(p.startTime()),
|
||||
p.endTime() != null ? Timestamp.from(p.endTime()) : null,
|
||||
@@ -113,6 +115,18 @@ public class PostgresExecutionStore implements ExecutionStore {
|
||||
PROCESSOR_MAPPER, executionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> findProcessorRouteMapping(String applicationName) {
|
||||
Map<String, String> mapping = new HashMap<>();
|
||||
jdbc.query("""
|
||||
SELECT DISTINCT ON (processor_id) processor_id, route_id
|
||||
FROM processor_executions WHERE application_name = ? ORDER BY processor_id
|
||||
""",
|
||||
rs -> { mapping.put(rs.getString("processor_id"), rs.getString("route_id")); },
|
||||
applicationName);
|
||||
return mapping;
|
||||
}
|
||||
|
||||
private static final RowMapper<ExecutionRecord> EXECUTION_MAPPER = (rs, rowNum) ->
|
||||
new ExecutionRecord(
|
||||
rs.getString("execution_id"), rs.getString("route_id"),
|
||||
@@ -131,7 +145,7 @@ public class PostgresExecutionStore implements ExecutionStore {
|
||||
private static final RowMapper<ProcessorRecord> PROCESSOR_MAPPER = (rs, rowNum) ->
|
||||
new ProcessorRecord(
|
||||
rs.getString("execution_id"), rs.getString("processor_id"),
|
||||
rs.getString("processor_type"), rs.getString("diagram_node_id"),
|
||||
rs.getString("processor_type"),
|
||||
rs.getString("application_name"), rs.getString("route_id"),
|
||||
rs.getInt("depth"), rs.getString("parent_processor_id"),
|
||||
rs.getString("status"),
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE processor_executions DROP COLUMN IF EXISTS diagram_node_id;
|
||||
@@ -65,7 +65,6 @@ class DetailControllerIT extends AbstractPostgresIT {
|
||||
"startTime": "2026-03-10T10:00:00Z",
|
||||
"endTime": "2026-03-10T10:00:01Z",
|
||||
"durationMs": 1000,
|
||||
"diagramNodeId": "node-root",
|
||||
"inputBody": "root-input-body",
|
||||
"outputBody": "root-output-body",
|
||||
"inputHeaders": {"Content-Type": "application/json"},
|
||||
@@ -78,7 +77,6 @@ class DetailControllerIT extends AbstractPostgresIT {
|
||||
"startTime": "2026-03-10T10:00:00.100Z",
|
||||
"endTime": "2026-03-10T10:00:00.200Z",
|
||||
"durationMs": 100,
|
||||
"diagramNodeId": "node-child1",
|
||||
"inputBody": "child1-input",
|
||||
"outputBody": "child1-output",
|
||||
"inputHeaders": {},
|
||||
@@ -91,7 +89,6 @@ class DetailControllerIT extends AbstractPostgresIT {
|
||||
"startTime": "2026-03-10T10:00:00.200Z",
|
||||
"endTime": "2026-03-10T10:00:00.800Z",
|
||||
"durationMs": 600,
|
||||
"diagramNodeId": "node-child2",
|
||||
"inputBody": "child2-input",
|
||||
"outputBody": "child2-output",
|
||||
"inputHeaders": {},
|
||||
@@ -104,7 +101,6 @@ class DetailControllerIT extends AbstractPostgresIT {
|
||||
"startTime": "2026-03-10T10:00:00.300Z",
|
||||
"endTime": "2026-03-10T10:00:00.700Z",
|
||||
"durationMs": 400,
|
||||
"diagramNodeId": "node-gc",
|
||||
"inputBody": "gc-input",
|
||||
"outputBody": "gc-output",
|
||||
"inputHeaders": {"X-GC": "true"},
|
||||
|
||||
@@ -39,8 +39,7 @@ class DiagramControllerIT extends AbstractPostgresIT {
|
||||
"description": "Test route",
|
||||
"version": 1,
|
||||
"nodes": [],
|
||||
"edges": [],
|
||||
"processorNodeMapping": {}
|
||||
"edges": []
|
||||
}
|
||||
""";
|
||||
|
||||
@@ -60,8 +59,7 @@ class DiagramControllerIT extends AbstractPostgresIT {
|
||||
"description": "Flush test",
|
||||
"version": 1,
|
||||
"nodes": [],
|
||||
"edges": [],
|
||||
"processorNodeMapping": {}
|
||||
"edges": []
|
||||
}
|
||||
""";
|
||||
|
||||
|
||||
@@ -53,8 +53,7 @@ class DiagramRenderControllerIT extends AbstractPostgresIT {
|
||||
"edges": [
|
||||
{"source": "n1", "target": "n2", "edgeType": "FLOW"},
|
||||
{"source": "n2", "target": "n3", "edgeType": "FLOW"}
|
||||
],
|
||||
"processorNodeMapping": {}
|
||||
]
|
||||
}
|
||||
""";
|
||||
|
||||
|
||||
@@ -46,8 +46,7 @@ class DiagramLinkingIT extends AbstractPostgresIT {
|
||||
],
|
||||
"edges": [
|
||||
{"source": "n1", "target": "n2", "edgeType": "FLOW"}
|
||||
],
|
||||
"processorNodeMapping": {}
|
||||
]
|
||||
}
|
||||
""";
|
||||
|
||||
|
||||
@@ -55,8 +55,7 @@ class IngestionSchemaIT extends AbstractPostgresIT {
|
||||
"startTime": "2026-03-11T10:00:00Z",
|
||||
"endTime": "2026-03-11T10:00:00.500Z",
|
||||
"durationMs": 500,
|
||||
"diagramNodeId": "node-root",
|
||||
"inputBody": "root-input",
|
||||
"inputBody": "root-input",
|
||||
"outputBody": "root-output",
|
||||
"inputHeaders": {"Content-Type": "application/json"},
|
||||
"outputHeaders": {"X-Result": "ok"},
|
||||
@@ -68,8 +67,7 @@ class IngestionSchemaIT extends AbstractPostgresIT {
|
||||
"startTime": "2026-03-11T10:00:00.100Z",
|
||||
"endTime": "2026-03-11T10:00:00.400Z",
|
||||
"durationMs": 300,
|
||||
"diagramNodeId": "node-child",
|
||||
"inputBody": "child-input",
|
||||
"inputBody": "child-input",
|
||||
"outputBody": "child-output",
|
||||
"children": [
|
||||
{
|
||||
@@ -79,8 +77,7 @@ class IngestionSchemaIT extends AbstractPostgresIT {
|
||||
"startTime": "2026-03-11T10:00:00.200Z",
|
||||
"endTime": "2026-03-11T10:00:00.300Z",
|
||||
"durationMs": 100,
|
||||
"diagramNodeId": "node-grandchild",
|
||||
"children": []
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -101,7 +98,7 @@ class IngestionSchemaIT extends AbstractPostgresIT {
|
||||
// Verify processors were flattened into processor_executions
|
||||
List<Map<String, Object>> processors = jdbcTemplate.queryForList(
|
||||
"SELECT processor_id, processor_type, depth, parent_processor_id, " +
|
||||
"diagram_node_id, input_body, output_body, input_headers " +
|
||||
"input_body, output_body, input_headers " +
|
||||
"FROM processor_executions WHERE execution_id = 'ex-tree-1' " +
|
||||
"ORDER BY depth, processor_id");
|
||||
assertThat(processors).hasSize(3);
|
||||
@@ -110,7 +107,6 @@ class IngestionSchemaIT extends AbstractPostgresIT {
|
||||
assertThat(processors.get(0).get("processor_id")).isEqualTo("root-proc");
|
||||
assertThat(((Number) processors.get(0).get("depth")).intValue()).isEqualTo(0);
|
||||
assertThat(processors.get(0).get("parent_processor_id")).isNull();
|
||||
assertThat(processors.get(0).get("diagram_node_id")).isEqualTo("node-root");
|
||||
assertThat(processors.get(0).get("input_body")).isEqualTo("root-input");
|
||||
assertThat(processors.get(0).get("output_body")).isEqualTo("root-output");
|
||||
assertThat(processors.get(0).get("input_headers").toString()).contains("Content-Type");
|
||||
@@ -119,7 +115,6 @@ class IngestionSchemaIT extends AbstractPostgresIT {
|
||||
assertThat(processors.get(1).get("processor_id")).isEqualTo("child-proc");
|
||||
assertThat(((Number) processors.get(1).get("depth")).intValue()).isEqualTo(1);
|
||||
assertThat(processors.get(1).get("parent_processor_id")).isEqualTo("root-proc");
|
||||
assertThat(processors.get(1).get("diagram_node_id")).isEqualTo("node-child");
|
||||
assertThat(processors.get(1).get("input_body")).isEqualTo("child-input");
|
||||
assertThat(processors.get(1).get("output_body")).isEqualTo("child-output");
|
||||
|
||||
@@ -127,7 +122,6 @@ class IngestionSchemaIT extends AbstractPostgresIT {
|
||||
assertThat(processors.get(2).get("processor_id")).isEqualTo("grandchild-proc");
|
||||
assertThat(((Number) processors.get(2).get("depth")).intValue()).isEqualTo(2);
|
||||
assertThat(processors.get(2).get("parent_processor_id")).isEqualTo("child-proc");
|
||||
assertThat(processors.get(2).get("diagram_node_id")).isEqualTo("node-grandchild");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user