fix!: require environment on diagram lookup and attribute keys queries
Closes two cross-env data leakage paths. Both endpoints previously
returned data aggregated across all environments, so a diagram or
attribute key from dev could appear in a prod UI query (and vice versa).
B1: GET /api/v1/diagrams?application=&routeId= now requires
?environment= and resolves agents via
registryService.findByApplicationAndEnvironment instead of
findByApplication. Prevents serving a dev diagram for a prod route.
B2: GET /api/v1/search/attributes/keys now requires ?environment=.
SearchIndex.distinctAttributeKeys gains an environment parameter and
the ClickHouse query adds the env filter alongside the existing
tenant_id filter. Prevents prod attribute names leaking into dev
autocompletion (and vice versa).
SPA hooks updated to thread environment through from
useEnvironmentStore; query keys include environment so React Query
re-fetches on env switch. No call-site changes needed — hook
signatures unchanged.
B3 (AgentMetricsController env scope) deferred to P3C: agent-env is
effectively 1:1 today via the instance_id naming
({envSlug}-{appSlug}-{replicaIndex}), and the URL migration in P3C
to /api/v1/environments/{env}/agents/{agentId}/metrics naturally
introduces env from path. A minimal P1 fix would regress the "view
metrics of a killed agent" case.
BREAKING CHANGE: Both endpoints now require ?environment= (slug).
Clients omitting the parameter receive 400.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -91,15 +91,17 @@ public class DiagramRenderController {
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@Operation(summary = "Find diagram by application and route ID",
|
||||
description = "Resolves application to agent IDs and finds the latest diagram for the route")
|
||||
@Operation(summary = "Find diagram by application, environment, and route ID",
|
||||
description = "Resolves (application, environment) to agent IDs and finds the latest diagram for the route. "
|
||||
+ "The environment filter prevents cross-env diagram leakage — without it a dev route could return a prod diagram (or vice versa).")
|
||||
@ApiResponse(responseCode = "200", description = "Diagram layout returned")
|
||||
@ApiResponse(responseCode = "404", description = "No diagram found for the given application and route")
|
||||
@ApiResponse(responseCode = "404", description = "No diagram found for the given application, environment, and route")
|
||||
public ResponseEntity<DiagramLayout> findByApplicationAndRoute(
|
||||
@RequestParam String application,
|
||||
@RequestParam String environment,
|
||||
@RequestParam String routeId,
|
||||
@RequestParam(defaultValue = "LR") String direction) {
|
||||
List<String> agentIds = registryService.findByApplication(application).stream()
|
||||
List<String> agentIds = registryService.findByApplicationAndEnvironment(application, environment).stream()
|
||||
.map(AgentInfo::instanceId)
|
||||
.toList();
|
||||
|
||||
|
||||
@@ -165,9 +165,10 @@ public class SearchController {
|
||||
}
|
||||
|
||||
@GetMapping("/attributes/keys")
|
||||
@Operation(summary = "Distinct attribute key names across all executions")
|
||||
public ResponseEntity<List<String>> attributeKeys() {
|
||||
return ResponseEntity.ok(searchService.distinctAttributeKeys());
|
||||
@Operation(summary = "Distinct attribute key names for the given environment",
|
||||
description = "Scoped to an environment to prevent cross-env attribute leakage in UI completions")
|
||||
public ResponseEntity<List<String>> attributeKeys(@RequestParam String environment) {
|
||||
return ResponseEntity.ok(searchService.distinctAttributeKeys(environment));
|
||||
}
|
||||
|
||||
@GetMapping("/errors/top")
|
||||
|
||||
@@ -318,14 +318,14 @@ public class ClickHouseSearchIndex implements SearchIndex {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> distinctAttributeKeys() {
|
||||
public List<String> distinctAttributeKeys(String environment) {
|
||||
try {
|
||||
return jdbc.queryForList("""
|
||||
SELECT DISTINCT arrayJoin(JSONExtractKeys(attributes)) AS attr_key
|
||||
FROM executions FINAL
|
||||
WHERE tenant_id = ? AND attributes != '' AND attributes != '{}'
|
||||
WHERE tenant_id = ? AND environment = ? AND attributes != '' AND attributes != '{}'
|
||||
ORDER BY attr_key
|
||||
""", String.class, tenantId);
|
||||
""", String.class, tenantId, environment);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to query distinct attribute keys", e);
|
||||
return List.of();
|
||||
|
||||
Reference in New Issue
Block a user