feat: add snapshot-by-processorId endpoint for robust processor lookup

Add GET /executions/{id}/processors/by-id/{processorId}/snapshot endpoint
that fetches processor snapshot data by processorId instead of positional
index, which is fragile when the tree structure changes. The existing
index-based endpoint remains unchanged for backward compatibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-27 18:34:45 +01:00
parent edd841ffeb
commit cf6c4bd60c
4 changed files with 34 additions and 1 deletions

View File

@@ -49,7 +49,7 @@ public class DetailController {
}
@GetMapping("/{executionId}/processors/{index}/snapshot")
@Operation(summary = "Get exchange snapshot for a specific processor")
@Operation(summary = "Get exchange snapshot for a specific processor by index")
@ApiResponse(responseCode = "200", description = "Snapshot data")
@ApiResponse(responseCode = "404", description = "Snapshot not found")
public ResponseEntity<Map<String, String>> getProcessorSnapshot(
@@ -69,4 +69,16 @@ public class DetailController {
return ResponseEntity.ok(snapshot);
}
@GetMapping("/{executionId}/processors/by-id/{processorId}/snapshot")
@Operation(summary = "Get exchange snapshot for a specific processor by processorId")
@ApiResponse(responseCode = "200", description = "Snapshot data")
@ApiResponse(responseCode = "404", description = "Snapshot not found")
public ResponseEntity<Map<String, String>> processorSnapshotById(
@PathVariable String executionId,
@PathVariable String processorId) {
return detailService.getProcessorSnapshot(executionId, processorId)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
}

View File

@@ -121,6 +121,13 @@ public class PostgresExecutionStore implements ExecutionStore {
PROCESSOR_MAPPER, executionId);
}
@Override
public Optional<ProcessorRecord> findProcessorById(String executionId, String processorId) {
String sql = "SELECT * FROM processor_executions WHERE execution_id = ? AND processor_id = ? LIMIT 1";
List<ProcessorRecord> results = jdbc.query(sql, PROCESSOR_MAPPER, executionId, processorId);
return results.isEmpty() ? Optional.empty() : Optional.of(results.get(0));
}
private static final RowMapper<ExecutionRecord> EXECUTION_MAPPER = (rs, rowNum) ->
new ExecutionRecord(
rs.getString("execution_id"), rs.getString("route_id"),

View File

@@ -38,6 +38,18 @@ public class DetailService {
});
}
public Optional<Map<String, String>> getProcessorSnapshot(String executionId, String processorId) {
return executionStore.findProcessorById(executionId, processorId)
.map(p -> {
Map<String, String> snapshot = new LinkedHashMap<>();
if (p.inputBody() != null) snapshot.put("inputBody", p.inputBody());
if (p.outputBody() != null) snapshot.put("outputBody", p.outputBody());
if (p.inputHeaders() != null) snapshot.put("inputHeaders", p.inputHeaders());
if (p.outputHeaders() != null) snapshot.put("outputHeaders", p.outputHeaders());
return snapshot;
});
}
List<ProcessorNode> buildTree(List<ProcessorRecord> processors) {
if (processors.isEmpty()) return List.of();

View File

@@ -16,6 +16,8 @@ public interface ExecutionStore {
List<ProcessorRecord> findProcessors(String executionId);
Optional<ProcessorRecord> findProcessorById(String executionId, String processorId);
record ExecutionRecord(
String executionId, String routeId, String agentId, String applicationName,
String status, String correlationId, String exchangeId,