feat(search): accept attr= multi-value query param on /executions GET
Add a repeatable attr query parameter to the GET /executions endpoint that parses key-only (exists check) and key:value (exact or wildcard-via-*) filters. Invalid keys are mapped to HTTP 400 via ResponseStatusException. The POST /executions/search path already honoured attributeFilters from the request body via the Jackson canonical ctor; an IT now proves it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import com.cameleer.server.app.web.EnvPath;
|
||||
import com.cameleer.server.core.admin.AppSettings;
|
||||
import com.cameleer.server.core.admin.AppSettingsRepository;
|
||||
import com.cameleer.server.core.runtime.Environment;
|
||||
import com.cameleer.server.core.search.AttributeFilter;
|
||||
import com.cameleer.server.core.search.ExecutionStats;
|
||||
import com.cameleer.server.core.search.ExecutionSummary;
|
||||
import com.cameleer.server.core.search.SearchRequest;
|
||||
@@ -23,6 +24,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -57,11 +59,20 @@ public class SearchController {
|
||||
@RequestParam(name = "agentId", required = false) String instanceId,
|
||||
@RequestParam(required = false) String processorType,
|
||||
@RequestParam(required = false) String application,
|
||||
@RequestParam(name = "attr", required = false) List<String> attr,
|
||||
@RequestParam(defaultValue = "0") int offset,
|
||||
@RequestParam(defaultValue = "50") int limit,
|
||||
@RequestParam(required = false) String sortField,
|
||||
@RequestParam(required = false) String sortDir) {
|
||||
|
||||
List<AttributeFilter> attributeFilters;
|
||||
try {
|
||||
attributeFilters = parseAttrParams(attr);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new org.springframework.web.server.ResponseStatusException(
|
||||
org.springframework.http.HttpStatus.BAD_REQUEST, e.getMessage(), e);
|
||||
}
|
||||
|
||||
SearchRequest request = new SearchRequest(
|
||||
status, timeFrom, timeTo,
|
||||
null, null,
|
||||
@@ -72,12 +83,36 @@ public class SearchController {
|
||||
offset, limit,
|
||||
sortField, sortDir,
|
||||
null,
|
||||
env.slug()
|
||||
env.slug(),
|
||||
attributeFilters
|
||||
);
|
||||
|
||||
return ResponseEntity.ok(searchService.search(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses {@code attr} query params of the form {@code key} (key-only) or {@code key:value}
|
||||
* (exact or wildcard via {@code *}). Splits on the first {@code :}; later colons are part of
|
||||
* the value. Blank / null list → empty result. Key validation is delegated to
|
||||
* {@link AttributeFilter}'s compact constructor, which throws {@link IllegalArgumentException}
|
||||
* on invalid keys (mapped to 400 by the caller).
|
||||
*/
|
||||
static List<AttributeFilter> parseAttrParams(List<String> raw) {
|
||||
if (raw == null || raw.isEmpty()) return List.of();
|
||||
List<AttributeFilter> out = new ArrayList<>(raw.size());
|
||||
for (String entry : raw) {
|
||||
if (entry == null || entry.isBlank()) continue;
|
||||
int colon = entry.indexOf(':');
|
||||
if (colon < 0) {
|
||||
out.add(new AttributeFilter(entry.trim(), null));
|
||||
} else {
|
||||
out.add(new AttributeFilter(entry.substring(0, colon).trim(),
|
||||
entry.substring(colon + 1)));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@PostMapping("/executions/search")
|
||||
@Operation(summary = "Advanced search with all filters",
|
||||
description = "Env from the path overrides any environment field in the body.")
|
||||
|
||||
Reference in New Issue
Block a user