Add route diagram page with execution overlay and group-aware APIs
Backend: Add group filtering to agent list, search, stats, and timeseries
endpoints. Add diagram lookup by group+routeId. Resolve application group
to agent IDs server-side for ClickHouse IN-clause queries.
Frontend: New route detail page at /apps/{group}/routes/{routeId} with
three tabs (Diagram, Performance, Processor Tree). SVG diagram rendering
with panzoom, execution overlay (glow effects, duration/sequence badges,
flow particles, minimap), and processor detail panel. uPlot charts for
performance tab replacing old SVG sparklines. Ctrl+Click from
ExecutionExplorer navigates to route diagram with overlay.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -167,6 +167,15 @@ public class AgentRegistryService {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all agents belonging to the given application group.
|
||||
*/
|
||||
public List<AgentInfo> findByGroup(String group) {
|
||||
return agents.values().stream()
|
||||
.filter(a -> group.equals(a.group()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a command to an agent's pending queue.
|
||||
* Notifies the event listener if one is set.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.cameleer3.server.core.search;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Swappable search backend abstraction.
|
||||
* <p>
|
||||
@@ -34,6 +36,17 @@ public interface SearchEngine {
|
||||
*/
|
||||
ExecutionStats stats(java.time.Instant from, java.time.Instant to);
|
||||
|
||||
/**
|
||||
* Compute aggregate stats scoped to specific routes and agents.
|
||||
*
|
||||
* @param from start of the time window
|
||||
* @param to end of the time window
|
||||
* @param routeId optional route ID filter
|
||||
* @param agentIds optional agent ID filter (from group resolution)
|
||||
* @return execution stats
|
||||
*/
|
||||
ExecutionStats stats(java.time.Instant from, java.time.Instant to, String routeId, List<String> agentIds);
|
||||
|
||||
/**
|
||||
* Compute bucketed time-series stats over a time window.
|
||||
*
|
||||
@@ -43,4 +56,17 @@ public interface SearchEngine {
|
||||
* @return bucketed stats
|
||||
*/
|
||||
StatsTimeseries timeseries(java.time.Instant from, java.time.Instant to, int bucketCount);
|
||||
|
||||
/**
|
||||
* Compute bucketed time-series stats scoped to specific routes and agents.
|
||||
*
|
||||
* @param from start of the time window
|
||||
* @param to end of the time window
|
||||
* @param bucketCount number of buckets to divide the window into
|
||||
* @param routeId optional route ID filter
|
||||
* @param agentIds optional agent ID filter (from group resolution)
|
||||
* @return bucketed stats
|
||||
*/
|
||||
StatsTimeseries timeseries(java.time.Instant from, java.time.Instant to, int bucketCount,
|
||||
String routeId, List<String> agentIds);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.cameleer3.server.core.search;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Immutable search criteria for querying route executions.
|
||||
@@ -21,6 +22,8 @@ import java.time.Instant;
|
||||
* @param routeId exact match on route_id
|
||||
* @param agentId exact match on agent_id
|
||||
* @param processorType matches processor_types array via has()
|
||||
* @param group application group filter (resolved to agentIds server-side)
|
||||
* @param agentIds list of agent IDs (resolved from group, used for IN clause)
|
||||
* @param offset pagination offset (0-based)
|
||||
* @param limit page size (default 50, max 500)
|
||||
* @param sortField column to sort by (default: startTime)
|
||||
@@ -40,6 +43,8 @@ public record SearchRequest(
|
||||
String routeId,
|
||||
String agentId,
|
||||
String processorType,
|
||||
String group,
|
||||
List<String> agentIds,
|
||||
int offset,
|
||||
int limit,
|
||||
String sortField,
|
||||
@@ -74,4 +79,14 @@ public record SearchRequest(
|
||||
public String sortColumn() {
|
||||
return SORT_FIELD_TO_COLUMN.getOrDefault(sortField, "start_time");
|
||||
}
|
||||
|
||||
/** Create a copy with resolved agentIds (from group lookup). */
|
||||
public SearchRequest withAgentIds(List<String> resolvedAgentIds) {
|
||||
return new SearchRequest(
|
||||
status, timeFrom, timeTo, durationMin, durationMax, correlationId,
|
||||
text, textInBody, textInHeaders, textInErrors,
|
||||
routeId, agentId, processorType, group, resolvedAgentIds,
|
||||
offset, limit, sortField, sortDir
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.cameleer3.server.core.search;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Orchestrates search operations, delegating to a {@link SearchEngine} backend.
|
||||
* <p>
|
||||
@@ -36,10 +38,26 @@ public class SearchService {
|
||||
return engine.stats(from, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute aggregate execution stats scoped to specific routes and agents.
|
||||
*/
|
||||
public ExecutionStats stats(java.time.Instant from, java.time.Instant to,
|
||||
String routeId, List<String> agentIds) {
|
||||
return engine.stats(from, to, routeId, agentIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute bucketed time-series stats over a time window.
|
||||
*/
|
||||
public StatsTimeseries timeseries(java.time.Instant from, java.time.Instant to, int bucketCount) {
|
||||
return engine.timeseries(from, to, bucketCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute bucketed time-series stats scoped to specific routes and agents.
|
||||
*/
|
||||
public StatsTimeseries timeseries(java.time.Instant from, java.time.Instant to, int bucketCount,
|
||||
String routeId, List<String> agentIds) {
|
||||
return engine.timeseries(from, to, bucketCount, routeId, agentIds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.cameleer3.server.core.storage;
|
||||
import com.cameleer3.common.graph.RouteGraph;
|
||||
import com.cameleer3.server.core.ingestion.TaggedDiagram;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
@@ -24,4 +25,11 @@ public interface DiagramRepository {
|
||||
* Find the content hash for the latest diagram of a given route and agent.
|
||||
*/
|
||||
Optional<String> findContentHashForRoute(String routeId, String agentId);
|
||||
|
||||
/**
|
||||
* Find the content hash for the latest diagram of a route across any agent in the given list.
|
||||
* All instances of the same application produce the same route graph, so any agent's
|
||||
* diagram for the same route will have the same content hash.
|
||||
*/
|
||||
Optional<String> findContentHashForRouteByAgents(String routeId, List<String> agentIds);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user