From 86e016874a9796ad05ef714756af10297e936d30 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Fri, 13 Mar 2026 17:13:14 +0100 Subject: [PATCH] Fix command palette: agent ID propagation, result selection, and scope tabs - Propagate authenticated agent identity through write buffers via TaggedExecution/TaggedDiagram wrappers so ClickHouse rows get real agent IDs instead of empty strings - Add execution_id to text search LIKE clause so selecting an execution by ID in the palette actually finds it - Clear status filter to all three statuses on palette selection so the chosen execution/agent isn't filtered out - Add disabled Routes and Exchanges scope tabs with "coming soon" state Co-Authored-By: Claude Opus 4.6 --- .../app/config/IngestionBeanConfig.java | 12 +++---- .../app/controller/DiagramController.java | 20 ++++++++--- .../app/controller/ExecutionController.java | 20 ++++++++--- .../ingestion/ClickHouseFlushScheduler.java | 24 ++++++------- .../app/search/ClickHouseSearchEngine.java | 19 +++++++---- .../storage/ClickHouseDiagramRepository.java | 9 ++--- .../ClickHouseExecutionRepository.java | 11 +++--- .../core/ingestion/IngestionService.java | 34 +++++++++---------- .../server/core/ingestion/TaggedDiagram.java | 11 ++++++ .../core/ingestion/TaggedExecution.java | 11 ++++++ .../core/storage/DiagramRepository.java | 5 +-- .../core/storage/ExecutionRepository.java | 6 ++-- .../command-palette/CommandPalette.tsx | 2 ++ .../command-palette/ResultsList.tsx | 20 ++++++++++- .../components/command-palette/ScopeTabs.tsx | 14 ++++++-- .../command-palette/use-command-palette.ts | 2 +- 16 files changed, 151 insertions(+), 69 deletions(-) create mode 100644 cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/TaggedDiagram.java create mode 100644 cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/TaggedExecution.java diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/IngestionBeanConfig.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/IngestionBeanConfig.java index e33e5648..83507e16 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/IngestionBeanConfig.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/IngestionBeanConfig.java @@ -1,8 +1,8 @@ package com.cameleer3.server.app.config; -import com.cameleer3.common.graph.RouteGraph; -import com.cameleer3.common.model.RouteExecution; import com.cameleer3.server.core.ingestion.IngestionService; +import com.cameleer3.server.core.ingestion.TaggedDiagram; +import com.cameleer3.server.core.ingestion.TaggedExecution; import com.cameleer3.server.core.ingestion.WriteBuffer; import com.cameleer3.server.core.storage.model.MetricsSnapshot; import org.springframework.context.annotation.Bean; @@ -18,12 +18,12 @@ import org.springframework.context.annotation.Configuration; public class IngestionBeanConfig { @Bean - public WriteBuffer executionBuffer(IngestionConfig config) { + public WriteBuffer executionBuffer(IngestionConfig config) { return new WriteBuffer<>(config.getBufferCapacity()); } @Bean - public WriteBuffer diagramBuffer(IngestionConfig config) { + public WriteBuffer diagramBuffer(IngestionConfig config) { return new WriteBuffer<>(config.getBufferCapacity()); } @@ -33,8 +33,8 @@ public class IngestionBeanConfig { } @Bean - public IngestionService ingestionService(WriteBuffer executionBuffer, - WriteBuffer diagramBuffer, + public IngestionService ingestionService(WriteBuffer executionBuffer, + WriteBuffer diagramBuffer, WriteBuffer metricsBuffer) { return new IngestionService(executionBuffer, diagramBuffer, metricsBuffer); } diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramController.java index 45de17ca..d4359968 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramController.java @@ -2,6 +2,7 @@ package com.cameleer3.server.app.controller; import com.cameleer3.common.graph.RouteGraph; import com.cameleer3.server.core.ingestion.IngestionService; +import com.cameleer3.server.core.ingestion.TaggedDiagram; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -12,6 +13,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -46,13 +49,17 @@ public class DiagramController { @ApiResponse(responseCode = "202", description = "Data accepted for processing") @ApiResponse(responseCode = "503", description = "Buffer full, retry later") public ResponseEntity ingestDiagrams(@RequestBody String body) throws JsonProcessingException { + String agentId = extractAgentId(); List graphs = parsePayload(body); - boolean accepted; + List tagged = graphs.stream() + .map(graph -> new TaggedDiagram(agentId, graph)) + .toList(); - if (graphs.size() == 1) { - accepted = ingestionService.acceptDiagram(graphs.get(0)); + boolean accepted; + if (tagged.size() == 1) { + accepted = ingestionService.acceptDiagram(tagged.get(0)); } else { - accepted = ingestionService.acceptDiagrams(graphs); + accepted = ingestionService.acceptDiagrams(tagged); } if (!accepted) { @@ -65,6 +72,11 @@ public class DiagramController { return ResponseEntity.accepted().build(); } + private String extractAgentId() { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + return auth != null ? auth.getName() : ""; + } + private List parsePayload(String body) throws JsonProcessingException { String trimmed = body.strip(); if (trimmed.startsWith("[")) { diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ExecutionController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ExecutionController.java index 1079baf3..e44f2645 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ExecutionController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ExecutionController.java @@ -2,6 +2,7 @@ package com.cameleer3.server.app.controller; import com.cameleer3.common.model.RouteExecution; import com.cameleer3.server.core.ingestion.IngestionService; +import com.cameleer3.server.core.ingestion.TaggedExecution; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -12,6 +13,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -47,13 +50,17 @@ public class ExecutionController { @ApiResponse(responseCode = "202", description = "Data accepted for processing") @ApiResponse(responseCode = "503", description = "Buffer full, retry later") public ResponseEntity ingestExecutions(@RequestBody String body) throws JsonProcessingException { + String agentId = extractAgentId(); List executions = parsePayload(body); - boolean accepted; + List tagged = executions.stream() + .map(exec -> new TaggedExecution(agentId, exec)) + .toList(); - if (executions.size() == 1) { - accepted = ingestionService.acceptExecution(executions.get(0)); + boolean accepted; + if (tagged.size() == 1) { + accepted = ingestionService.acceptExecution(tagged.get(0)); } else { - accepted = ingestionService.acceptExecutions(executions); + accepted = ingestionService.acceptExecutions(tagged); } if (!accepted) { @@ -66,6 +73,11 @@ public class ExecutionController { return ResponseEntity.accepted().build(); } + private String extractAgentId() { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + return auth != null ? auth.getName() : ""; + } + private List parsePayload(String body) throws JsonProcessingException { String trimmed = body.strip(); if (trimmed.startsWith("[")) { diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/ingestion/ClickHouseFlushScheduler.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/ingestion/ClickHouseFlushScheduler.java index aa42083f..e48a2a92 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/ingestion/ClickHouseFlushScheduler.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/ingestion/ClickHouseFlushScheduler.java @@ -1,8 +1,8 @@ package com.cameleer3.server.app.ingestion; -import com.cameleer3.common.graph.RouteGraph; -import com.cameleer3.common.model.RouteExecution; import com.cameleer3.server.app.config.IngestionConfig; +import com.cameleer3.server.core.ingestion.TaggedDiagram; +import com.cameleer3.server.core.ingestion.TaggedExecution; import com.cameleer3.server.core.ingestion.WriteBuffer; import com.cameleer3.server.core.storage.DiagramRepository; import com.cameleer3.server.core.storage.ExecutionRepository; @@ -27,8 +27,8 @@ public class ClickHouseFlushScheduler implements SmartLifecycle { private static final Logger log = LoggerFactory.getLogger(ClickHouseFlushScheduler.class); - private final WriteBuffer executionBuffer; - private final WriteBuffer diagramBuffer; + private final WriteBuffer executionBuffer; + private final WriteBuffer diagramBuffer; private final WriteBuffer metricsBuffer; private final ExecutionRepository executionRepository; private final DiagramRepository diagramRepository; @@ -37,8 +37,8 @@ public class ClickHouseFlushScheduler implements SmartLifecycle { private volatile boolean running = false; - public ClickHouseFlushScheduler(WriteBuffer executionBuffer, - WriteBuffer diagramBuffer, + public ClickHouseFlushScheduler(WriteBuffer executionBuffer, + WriteBuffer diagramBuffer, WriteBuffer metricsBuffer, ExecutionRepository executionRepository, DiagramRepository diagramRepository, @@ -62,7 +62,7 @@ public class ClickHouseFlushScheduler implements SmartLifecycle { private void flushExecutions() { try { - List batch = executionBuffer.drain(batchSize); + List batch = executionBuffer.drain(batchSize); if (!batch.isEmpty()) { executionRepository.insertBatch(batch); log.debug("Flushed {} executions to ClickHouse", batch.size()); @@ -74,9 +74,9 @@ public class ClickHouseFlushScheduler implements SmartLifecycle { private void flushDiagrams() { try { - List batch = diagramBuffer.drain(batchSize); - for (RouteGraph graph : batch) { - diagramRepository.store(graph); + List batch = diagramBuffer.drain(batchSize); + for (TaggedDiagram diagram : batch) { + diagramRepository.store(diagram); } if (!batch.isEmpty()) { log.debug("Flushed {} diagrams to ClickHouse", batch.size()); @@ -130,8 +130,8 @@ public class ClickHouseFlushScheduler implements SmartLifecycle { private void drainAll() { drainBufferCompletely("executions", executionBuffer, batch -> executionRepository.insertBatch(batch)); drainBufferCompletely("diagrams", diagramBuffer, batch -> { - for (RouteGraph g : batch) { - diagramRepository.store(g); + for (TaggedDiagram d : batch) { + diagramRepository.store(d); } }); drainBufferCompletely("metrics", metricsBuffer, batch -> metricsRepository.insertBatch(batch)); diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/search/ClickHouseSearchEngine.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/search/ClickHouseSearchEngine.java index 655793b6..14695a92 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/search/ClickHouseSearchEngine.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/search/ClickHouseSearchEngine.java @@ -123,13 +123,18 @@ public class ClickHouseSearchEngine implements SearchEngine { } if (req.text() != null && !req.text().isBlank()) { String pattern = "%" + escapeLike(req.text()) + "%"; - conditions.add("(route_id LIKE ? OR agent_id LIKE ? OR error_message LIKE ? OR error_stacktrace LIKE ? OR exchange_bodies LIKE ? OR exchange_headers LIKE ?)"); - params.add(pattern); - params.add(pattern); - params.add(pattern); - params.add(pattern); - params.add(pattern); - params.add(pattern); + String[] textColumns = { + "execution_id", "route_id", "agent_id", + "error_message", "error_stacktrace", + "exchange_bodies", "exchange_headers" + }; + var likeClauses = java.util.Arrays.stream(textColumns) + .map(col -> col + " LIKE ?") + .toList(); + conditions.add("(" + String.join(" OR ", likeClauses) + ")"); + for (int i = 0; i < textColumns.length; i++) { + params.add(pattern); + } } if (req.textInBody() != null && !req.textInBody().isBlank()) { conditions.add("exchange_bodies LIKE ?"); diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseDiagramRepository.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseDiagramRepository.java index ff1fd28b..85644c2e 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseDiagramRepository.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseDiagramRepository.java @@ -1,6 +1,7 @@ package com.cameleer3.server.app.storage; import com.cameleer3.common.graph.RouteGraph; +import com.cameleer3.server.core.ingestion.TaggedDiagram; import com.cameleer3.server.core.storage.DiagramRepository; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -54,16 +55,16 @@ public class ClickHouseDiagramRepository implements DiagramRepository { } @Override - public void store(RouteGraph graph) { + public void store(TaggedDiagram diagram) { try { + RouteGraph graph = diagram.graph(); + String agentId = diagram.agentId() != null ? diagram.agentId() : ""; String json = objectMapper.writeValueAsString(graph); String contentHash = sha256Hex(json); String routeId = graph.getRouteId() != null ? graph.getRouteId() : ""; - // agent_id is not part of RouteGraph -- set empty, controllers can enrich - String agentId = ""; jdbcTemplate.update(INSERT_SQL, contentHash, routeId, agentId, json); - log.debug("Stored diagram for route={} with hash={}", routeId, contentHash); + log.debug("Stored diagram for route={} agent={} with hash={}", routeId, agentId, contentHash); } catch (JsonProcessingException e) { throw new RuntimeException("Failed to serialize RouteGraph to JSON", e); } diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java index 142e1512..b119f7e7 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java @@ -4,6 +4,7 @@ import com.cameleer3.common.model.ExchangeSnapshot; import com.cameleer3.common.model.ProcessorExecution; import com.cameleer3.common.model.RouteExecution; import com.cameleer3.server.core.detail.RawExecutionRow; +import com.cameleer3.server.core.ingestion.TaggedExecution; import com.cameleer3.server.core.storage.DiagramRepository; import com.cameleer3.server.core.storage.ExecutionRepository; import com.fasterxml.jackson.core.JsonProcessingException; @@ -62,7 +63,7 @@ public class ClickHouseExecutionRepository implements ExecutionRepository { } @Override - public void insertBatch(List executions) { + public void insertBatch(List executions) { if (executions.isEmpty()) { return; } @@ -70,13 +71,15 @@ public class ClickHouseExecutionRepository implements ExecutionRepository { jdbcTemplate.batchUpdate(INSERT_SQL, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { - RouteExecution exec = executions.get(i); + TaggedExecution tagged = executions.get(i); + RouteExecution exec = tagged.execution(); + String agentId = tagged.agentId() != null ? tagged.agentId() : ""; List flatProcessors = flattenWithMetadata(exec.getProcessors()); int col = 1; ps.setString(col++, UUID.randomUUID().toString()); ps.setString(col++, nullSafe(exec.getRouteId())); - ps.setString(col++, ""); // agent_id set by controller header or empty + ps.setString(col++, agentId); ps.setString(col++, exec.getStatus() != null ? exec.getStatus().name() : "RUNNING"); ps.setObject(col++, toTimestamp(exec.getStartTime())); ps.setObject(col++, toTimestamp(exec.getEndTime())); @@ -142,7 +145,7 @@ public class ClickHouseExecutionRepository implements ExecutionRepository { ps.setObject(col++, outputHeaders); // processor_output_headers ps.setObject(col++, diagramNodeIds); // processor_diagram_node_ids String diagramHash = diagramRepository - .findContentHashForRoute(exec.getRouteId(), "") + .findContentHashForRoute(exec.getRouteId(), agentId) .orElse(""); ps.setString(col++, diagramHash); // diagram_content_hash } diff --git a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/IngestionService.java b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/IngestionService.java index 8ed9afba..6841c683 100644 --- a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/IngestionService.java +++ b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/IngestionService.java @@ -1,7 +1,5 @@ package com.cameleer3.server.core.ingestion; -import com.cameleer3.common.graph.RouteGraph; -import com.cameleer3.common.model.RouteExecution; import com.cameleer3.server.core.storage.model.MetricsSnapshot; import java.util.List; @@ -14,12 +12,12 @@ import java.util.List; */ public class IngestionService { - private final WriteBuffer executionBuffer; - private final WriteBuffer diagramBuffer; + private final WriteBuffer executionBuffer; + private final WriteBuffer diagramBuffer; private final WriteBuffer metricsBuffer; - public IngestionService(WriteBuffer executionBuffer, - WriteBuffer diagramBuffer, + public IngestionService(WriteBuffer executionBuffer, + WriteBuffer diagramBuffer, WriteBuffer metricsBuffer) { this.executionBuffer = executionBuffer; this.diagramBuffer = diagramBuffer; @@ -27,39 +25,39 @@ public class IngestionService { } /** - * Accept a batch of route executions into the buffer. + * Accept a batch of tagged route executions into the buffer. * * @return true if all items were buffered, false if buffer is full (backpressure) */ - public boolean acceptExecutions(List executions) { + public boolean acceptExecutions(List executions) { return executionBuffer.offerBatch(executions); } /** - * Accept a single route execution into the buffer. + * Accept a single tagged route execution into the buffer. * * @return true if the item was buffered, false if buffer is full (backpressure) */ - public boolean acceptExecution(RouteExecution execution) { + public boolean acceptExecution(TaggedExecution execution) { return executionBuffer.offer(execution); } /** - * Accept a single route diagram into the buffer. + * Accept a single tagged route diagram into the buffer. * * @return true if the item was buffered, false if buffer is full (backpressure) */ - public boolean acceptDiagram(RouteGraph graph) { - return diagramBuffer.offer(graph); + public boolean acceptDiagram(TaggedDiagram diagram) { + return diagramBuffer.offer(diagram); } /** - * Accept a batch of route diagrams into the buffer. + * Accept a batch of tagged route diagrams into the buffer. * * @return true if all items were buffered, false if buffer is full (backpressure) */ - public boolean acceptDiagrams(List graphs) { - return diagramBuffer.offerBatch(graphs); + public boolean acceptDiagrams(List diagrams) { + return diagramBuffer.offerBatch(diagrams); } /** @@ -95,14 +93,14 @@ public class IngestionService { /** * @return the execution write buffer (for use by flush scheduler) */ - public WriteBuffer getExecutionBuffer() { + public WriteBuffer getExecutionBuffer() { return executionBuffer; } /** * @return the diagram write buffer (for use by flush scheduler) */ - public WriteBuffer getDiagramBuffer() { + public WriteBuffer getDiagramBuffer() { return diagramBuffer; } diff --git a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/TaggedDiagram.java b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/TaggedDiagram.java new file mode 100644 index 00000000..2f7b9ec5 --- /dev/null +++ b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/TaggedDiagram.java @@ -0,0 +1,11 @@ +package com.cameleer3.server.core.ingestion; + +import com.cameleer3.common.graph.RouteGraph; + +/** + * Pairs a {@link RouteGraph} with the authenticated agent identity. + *

+ * The agent ID is extracted from the SecurityContext in the controller layer + * and carried through the write buffer so the flush scheduler can persist it. + */ +public record TaggedDiagram(String agentId, RouteGraph graph) {} diff --git a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/TaggedExecution.java b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/TaggedExecution.java new file mode 100644 index 00000000..a65ee5db --- /dev/null +++ b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/ingestion/TaggedExecution.java @@ -0,0 +1,11 @@ +package com.cameleer3.server.core.ingestion; + +import com.cameleer3.common.model.RouteExecution; + +/** + * Pairs a {@link RouteExecution} with the authenticated agent identity. + *

+ * The agent ID is extracted from the SecurityContext in the controller layer + * and carried through the write buffer so the flush scheduler can persist it. + */ +public record TaggedExecution(String agentId, RouteExecution execution) {} diff --git a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/DiagramRepository.java b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/DiagramRepository.java index 57aede79..7cfd289d 100644 --- a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/DiagramRepository.java +++ b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/DiagramRepository.java @@ -1,6 +1,7 @@ package com.cameleer3.server.core.storage; import com.cameleer3.common.graph.RouteGraph; +import com.cameleer3.server.core.ingestion.TaggedDiagram; import java.util.Optional; @@ -10,9 +11,9 @@ import java.util.Optional; public interface DiagramRepository { /** - * Store a route graph. Uses content-hash deduplication via ReplacingMergeTree. + * Store a tagged route graph. Uses content-hash deduplication via ReplacingMergeTree. */ - void store(RouteGraph graph); + void store(TaggedDiagram diagram); /** * Find a route graph by its content hash. diff --git a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/ExecutionRepository.java b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/ExecutionRepository.java index 286ab076..c58c1f81 100644 --- a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/ExecutionRepository.java +++ b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/ExecutionRepository.java @@ -1,7 +1,7 @@ package com.cameleer3.server.core.storage; -import com.cameleer3.common.model.RouteExecution; import com.cameleer3.server.core.detail.RawExecutionRow; +import com.cameleer3.server.core.ingestion.TaggedExecution; import java.util.List; import java.util.Optional; @@ -12,10 +12,10 @@ import java.util.Optional; public interface ExecutionRepository { /** - * Insert a batch of route executions. + * Insert a batch of tagged route executions. * Implementations must perform a single batch insert for efficiency. */ - void insertBatch(List executions); + void insertBatch(List executions); /** * Find a raw execution row by execution ID, including all parallel arrays diff --git a/ui/src/components/command-palette/CommandPalette.tsx b/ui/src/components/command-palette/CommandPalette.tsx index 8817321b..25f58588 100644 --- a/ui/src/components/command-palette/CommandPalette.tsx +++ b/ui/src/components/command-palette/CommandPalette.tsx @@ -22,12 +22,14 @@ export function CommandPalette() { (result: PaletteResult) => { if (result.type === 'execution') { const exec = result.data as ExecutionSummary; + execSearch.setStatus(['COMPLETED', 'FAILED', 'RUNNING']); execSearch.setText(exec.executionId); execSearch.setRouteId(''); execSearch.setAgentId(''); execSearch.setProcessorType(''); } else if (result.type === 'agent') { const agent = result.data as AgentInstance; + execSearch.setStatus(['COMPLETED', 'FAILED', 'RUNNING']); execSearch.setAgentId(agent.agentId); execSearch.setText(''); execSearch.setRouteId(''); diff --git a/ui/src/components/command-palette/ResultsList.tsx b/ui/src/components/command-palette/ResultsList.tsx index b721eccb..90db6fce 100644 --- a/ui/src/components/command-palette/ResultsList.tsx +++ b/ui/src/components/command-palette/ResultsList.tsx @@ -11,7 +11,7 @@ interface ResultsListProps { } export function ResultsList({ results, isLoading, onSelect }: ResultsListProps) { - const { selectedIndex, query } = useCommandPalette(); + const { selectedIndex, query, scope } = useCommandPalette(); const listRef = useRef(null); useEffect(() => { @@ -21,6 +21,24 @@ export function ResultsList({ results, isLoading, onSelect }: ResultsListProps) items?.[selectedIndex]?.scrollIntoView({ block: 'nearest' }); }, [selectedIndex]); + if (scope === 'routes' || scope === 'exchanges') { + const label = scope === 'routes' ? 'Route' : 'Exchange'; + return ( +

+
+ + + + + {label} search coming soon + + This feature is planned for a future release + +
+
+ ); + } + if (isLoading && results.length === 0) { return (
diff --git a/ui/src/components/command-palette/ScopeTabs.tsx b/ui/src/components/command-palette/ScopeTabs.tsx index 39629f31..8433eaf4 100644 --- a/ui/src/components/command-palette/ScopeTabs.tsx +++ b/ui/src/components/command-palette/ScopeTabs.tsx @@ -10,16 +10,18 @@ const SCOPES: { key: PaletteScope; label: string; disabled?: boolean }[] = [ { key: 'all', label: 'All' }, { key: 'executions', label: 'Executions' }, { key: 'agents', label: 'Agents' }, + { key: 'routes', label: 'Routes', disabled: true }, + { key: 'exchanges', label: 'Exchanges', disabled: true }, ]; export function ScopeTabs({ executionCount, agentCount }: ScopeTabsProps) { const { scope, setScope } = useCommandPalette(); - function getCount(key: PaletteScope): number { + function getCount(key: PaletteScope): string | number { if (key === 'all') return executionCount + agentCount; if (key === 'executions') return executionCount; if (key === 'agents') return agentCount; - return 0; + return '\u2014'; } return ( @@ -27,7 +29,13 @@ export function ScopeTabs({ executionCount, agentCount }: ScopeTabsProps) { {SCOPES.map((s) => (