fix: resolve 25 SonarQube code smells across 21 files
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m2s
CI / docker (push) Successful in 45s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 38s

Remove unused fields (log, rbacService, roleRepository, jwt),
unused variables (agentTps, routeKeys, updated), unused imports
(HttpHeaders, JdbcTemplate). Rename restricted identifier 'record'
to 'auditRecord'/'event'. Return empty collections instead of null.
Replace .collect(Collectors.toList()) with .toList(). Simplify
conditional return in BootstrapTokenValidator.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-04 09:36:13 +02:00
parent 633a61d89d
commit b04b12220b
21 changed files with 42 additions and 90 deletions

View File

@@ -11,8 +11,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
@@ -34,8 +32,6 @@ import java.util.List;
@Tag(name = "Ingestion", description = "Data ingestion endpoints") @Tag(name = "Ingestion", description = "Data ingestion endpoints")
public class DiagramController { public class DiagramController {
private static final Logger log = LoggerFactory.getLogger(DiagramController.class);
private final IngestionService ingestionService; private final IngestionService ingestionService;
private final AgentRegistryService registryService; private final AgentRegistryService registryService;
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;

View File

@@ -11,8 +11,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@@ -39,8 +37,6 @@ import java.util.List;
@Tag(name = "Ingestion", description = "Data ingestion endpoints") @Tag(name = "Ingestion", description = "Data ingestion endpoints")
public class ExecutionController { public class ExecutionController {
private static final Logger log = LoggerFactory.getLogger(ExecutionController.class);
private final IngestionService ingestionService; private final IngestionService ingestionService;
private final AgentRegistryService registryService; private final AgentRegistryService registryService;
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;

View File

@@ -6,7 +6,6 @@ import com.cameleer3.server.core.admin.AuditService;
import com.cameleer3.server.core.rbac.GroupDetail; import com.cameleer3.server.core.rbac.GroupDetail;
import com.cameleer3.server.core.rbac.GroupRepository; import com.cameleer3.server.core.rbac.GroupRepository;
import com.cameleer3.server.core.rbac.GroupSummary; import com.cameleer3.server.core.rbac.GroupSummary;
import com.cameleer3.server.core.rbac.RbacService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@@ -39,13 +38,10 @@ import java.util.UUID;
public class GroupAdminController { public class GroupAdminController {
private final GroupRepository groupRepository; private final GroupRepository groupRepository;
private final RbacService rbacService;
private final AuditService auditService; private final AuditService auditService;
public GroupAdminController(GroupRepository groupRepository, RbacService rbacService, public GroupAdminController(GroupRepository groupRepository, AuditService auditService) {
AuditService auditService) {
this.groupRepository = groupRepository; this.groupRepository = groupRepository;
this.rbacService = rbacService;
this.auditService = auditService; this.auditService = auditService;
} }

View File

@@ -3,7 +3,6 @@ package com.cameleer3.server.app.controller;
import com.cameleer3.server.core.admin.AuditCategory; import com.cameleer3.server.core.admin.AuditCategory;
import com.cameleer3.server.core.admin.AuditResult; import com.cameleer3.server.core.admin.AuditResult;
import com.cameleer3.server.core.admin.AuditService; import com.cameleer3.server.core.admin.AuditService;
import com.cameleer3.server.core.rbac.RbacService;
import com.cameleer3.server.core.rbac.RoleDetail; import com.cameleer3.server.core.rbac.RoleDetail;
import com.cameleer3.server.core.rbac.RoleRepository; import com.cameleer3.server.core.rbac.RoleRepository;
import com.cameleer3.server.core.rbac.SystemRole; import com.cameleer3.server.core.rbac.SystemRole;
@@ -37,13 +36,10 @@ import java.util.UUID;
public class RoleAdminController { public class RoleAdminController {
private final RoleRepository roleRepository; private final RoleRepository roleRepository;
private final RbacService rbacService;
private final AuditService auditService; private final AuditService auditService;
public RoleAdminController(RoleRepository roleRepository, RbacService rbacService, public RoleAdminController(RoleRepository roleRepository, AuditService auditService) {
AuditService auditService) {
this.roleRepository = roleRepository; this.roleRepository = roleRepository;
this.rbacService = rbacService;
this.auditService = auditService; this.auditService = auditService;
} }

View File

@@ -82,8 +82,6 @@ public class RouteCatalogController {
Instant now = Instant.now(); Instant now = Instant.now();
Instant rangeFrom = from != null ? Instant.parse(from) : now.minus(24, ChronoUnit.HOURS); Instant rangeFrom = from != null ? Instant.parse(from) : now.minus(24, ChronoUnit.HOURS);
Instant rangeTo = to != null ? Instant.parse(to) : now; Instant rangeTo = to != null ? Instant.parse(to) : now;
Instant from1m = now.minus(1, ChronoUnit.MINUTES);
// Route exchange counts from AggregatingMergeTree (literal SQL — ClickHouse JDBC driver // Route exchange counts from AggregatingMergeTree (literal SQL — ClickHouse JDBC driver
// wraps prepared statements in sub-queries that strip AggregateFunction column types) // wraps prepared statements in sub-queries that strip AggregateFunction column types)
Map<String, Long> routeExchangeCounts = new LinkedHashMap<>(); Map<String, Long> routeExchangeCounts = new LinkedHashMap<>();
@@ -113,20 +111,6 @@ public class RouteCatalogController {
} }
} }
// Per-agent TPS from the last minute
Map<String, Double> agentTps = new LinkedHashMap<>();
try {
jdbc.query(
"SELECT application_id, countMerge(total_count) AS cnt " +
"FROM stats_1m_route WHERE bucket >= " + lit(from1m) + " AND bucket < " + lit(now) +
" GROUP BY application_id",
rs -> {
// This gives per-app TPS; we'll distribute among agents below
});
} catch (Exception e) {
// AggregatingMergeTree table may not exist yet
}
// Build catalog entries — merge apps from agent registry + ClickHouse data // Build catalog entries — merge apps from agent registry + ClickHouse data
Set<String> allAppIds = new LinkedHashSet<>(agentsByApp.keySet()); Set<String> allAppIds = new LinkedHashSet<>(agentsByApp.keySet());
allAppIds.addAll(routesByApp.keySet()); allAppIds.addAll(routesByApp.keySet());

View File

@@ -67,10 +67,6 @@ public class RouteMetricsController {
} }
sql.append(" GROUP BY application_id, route_id ORDER BY application_id, route_id"); sql.append(" GROUP BY application_id, route_id ORDER BY application_id, route_id");
// Key struct for sparkline lookup
record RouteKey(String appId, String routeId) {}
List<RouteKey> routeKeys = new ArrayList<>();
List<RouteMetrics> metrics = jdbc.query(sql.toString(), (rs, rowNum) -> { List<RouteMetrics> metrics = jdbc.query(sql.toString(), (rs, rowNum) -> {
String applicationId = rs.getString("application_id"); String applicationId = rs.getString("application_id");
String routeId = rs.getString("route_id"); String routeId = rs.getString("route_id");
@@ -83,7 +79,6 @@ public class RouteMetricsController {
double errorRate = total > 0 ? (double) failed / total : 0.0; double errorRate = total > 0 ? (double) failed / total : 0.0;
double tps = windowSeconds > 0 ? (double) total / windowSeconds : 0.0; double tps = windowSeconds > 0 ? (double) total / windowSeconds : 0.0;
routeKeys.add(new RouteKey(applicationId, routeId));
return new RouteMetrics(routeId, applicationId, total, successRate, return new RouteMetrics(routeId, applicationId, total, successRate,
avgDur, p99Dur, errorRate, tps, List.of(), -1.0); avgDur, p99Dur, errorRate, tps, List.of(), -1.0);
}); });

View File

@@ -136,7 +136,7 @@ public class SearchController {
return ResponseEntity.ok(searchService.timeseriesForApp(from, end, buckets, application)); return ResponseEntity.ok(searchService.timeseriesForApp(from, end, buckets, application));
} }
List<String> agentIds = resolveApplicationToAgentIds(application); List<String> agentIds = resolveApplicationToAgentIds(application);
if (routeId == null && agentIds == null) { if (routeId == null && agentIds.isEmpty()) {
return ResponseEntity.ok(searchService.timeseries(from, end, buckets)); return ResponseEntity.ok(searchService.timeseries(from, end, buckets));
} }
return ResponseEntity.ok(searchService.timeseries(from, end, buckets, routeId, agentIds)); return ResponseEntity.ok(searchService.timeseries(from, end, buckets, routeId, agentIds));
@@ -192,11 +192,11 @@ public class SearchController {
/** /**
* Resolve an application name to agent IDs. * Resolve an application name to agent IDs.
* Returns null if application is null/blank (no filtering). * Returns empty list if application is null/blank (no filtering).
*/ */
private List<String> resolveApplicationToAgentIds(String application) { private List<String> resolveApplicationToAgentIds(String application) {
if (application == null || application.isBlank()) { if (application == null || application.isBlank()) {
return null; return List.of();
} }
return registryService.findByApplication(application).stream() return registryService.findByApplication(application).stream()
.map(AgentInfo::instanceId) .map(AgentInfo::instanceId)

View File

@@ -15,10 +15,10 @@ public record AgentEventResponse(
String detail, String detail,
@NotNull Instant timestamp @NotNull Instant timestamp
) { ) {
public static AgentEventResponse from(AgentEventRecord record) { public static AgentEventResponse from(AgentEventRecord event) {
return new AgentEventResponse( return new AgentEventResponse(
record.id(), record.instanceId(), record.applicationId(), event.id(), event.instanceId(), event.applicationId(),
record.eventType(), record.detail(), record.timestamp() event.eventType(), event.detail(), event.timestamp()
); );
} }
} }

View File

@@ -1,6 +1,12 @@
package com.cameleer3.server.app.rbac; package com.cameleer3.server.app.rbac;
import com.cameleer3.server.core.rbac.*; import com.cameleer3.server.core.rbac.GroupRepository;
import com.cameleer3.server.core.rbac.GroupSummary;
import com.cameleer3.server.core.rbac.RbacService;
import com.cameleer3.server.core.rbac.RbacStats;
import com.cameleer3.server.core.rbac.RoleSummary;
import com.cameleer3.server.core.rbac.UserDetail;
import com.cameleer3.server.core.rbac.UserSummary;
import com.cameleer3.server.core.security.UserInfo; import com.cameleer3.server.core.security.UserInfo;
import com.cameleer3.server.core.security.UserRepository; import com.cameleer3.server.core.security.UserRepository;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
@@ -14,14 +20,12 @@ public class RbacServiceImpl implements RbacService {
private final JdbcTemplate jdbc; private final JdbcTemplate jdbc;
private final UserRepository userRepository; private final UserRepository userRepository;
private final GroupRepository groupRepository; private final GroupRepository groupRepository;
private final RoleRepository roleRepository;
public RbacServiceImpl(JdbcTemplate jdbc, UserRepository userRepository, public RbacServiceImpl(JdbcTemplate jdbc, UserRepository userRepository,
GroupRepository groupRepository, RoleRepository roleRepository) { GroupRepository groupRepository) {
this.jdbc = jdbc; this.jdbc = jdbc;
this.userRepository = userRepository; this.userRepository = userRepository;
this.groupRepository = groupRepository; this.groupRepository = groupRepository;
this.roleRepository = roleRepository;
} }
@Override @Override

View File

@@ -31,20 +31,12 @@ public class BootstrapTokenValidator {
byte[] providedBytes = provided.getBytes(StandardCharsets.UTF_8); byte[] providedBytes = provided.getBytes(StandardCharsets.UTF_8);
// Check current token // Check current token, then previous token (rotation support)
String currentToken = properties.getBootstrapToken(); String currentToken = properties.getBootstrapToken();
if (currentToken != null
&& MessageDigest.isEqual(providedBytes, currentToken.getBytes(StandardCharsets.UTF_8))) {
return true;
}
// Check previous token (rotation support)
String previousToken = properties.getBootstrapTokenPrevious(); String previousToken = properties.getBootstrapTokenPrevious();
if (previousToken != null return (currentToken != null
&& MessageDigest.isEqual(providedBytes, previousToken.getBytes(StandardCharsets.UTF_8))) { && MessageDigest.isEqual(providedBytes, currentToken.getBytes(StandardCharsets.UTF_8)))
return true; || (previousToken != null
} && MessageDigest.isEqual(providedBytes, previousToken.getBytes(StandardCharsets.UTF_8)));
return false;
} }
} }

View File

@@ -61,7 +61,7 @@ public class PostgresApplicationConfigRepository {
} }
// Upsert: insert or update, auto-increment version // Upsert: insert or update, auto-increment version
int updated = jdbc.update(""" jdbc.update("""
INSERT INTO application_config (application, config_val, version, updated_at, updated_by) INSERT INTO application_config (application, config_val, version, updated_at, updated_by)
VALUES (?, ?::jsonb, 1, now(), ?) VALUES (?, ?::jsonb, 1, now(), ?)
ON CONFLICT (application) DO UPDATE SET ON CONFLICT (application) DO UPDATE SET

View File

@@ -34,11 +34,11 @@ public class PostgresAuditRepository implements AuditRepository {
} }
@Override @Override
public void insert(AuditRecord record) { public void insert(AuditRecord auditRecord) {
String detailJson = null; String detailJson = null;
if (record.detail() != null) { if (auditRecord.detail() != null) {
try { try {
detailJson = objectMapper.writeValueAsString(record.detail()); detailJson = objectMapper.writeValueAsString(auditRecord.detail());
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
throw new RuntimeException("Failed to serialize audit detail", e); throw new RuntimeException("Failed to serialize audit detail", e);
} }
@@ -48,11 +48,11 @@ public class PostgresAuditRepository implements AuditRepository {
INSERT INTO audit_log (username, action, category, target, detail, result, ip_address, user_agent) INSERT INTO audit_log (username, action, category, target, detail, result, ip_address, user_agent)
VALUES (?, ?, ?, ?, ?::jsonb, ?, ?, ?) VALUES (?, ?, ?, ?, ?::jsonb, ?, ?, ?)
""", """,
record.username(), record.action(), auditRecord.username(), auditRecord.action(),
record.category() != null ? record.category().name() : null, auditRecord.category() != null ? auditRecord.category().name() : null,
record.target(), detailJson, auditRecord.target(), detailJson,
record.result() != null ? record.result().name() : null, auditRecord.result() != null ? auditRecord.result().name() : null,
record.ipAddress(), record.userAgent()); auditRecord.ipAddress(), auditRecord.userAgent());
} }
@Override @Override

View File

@@ -9,7 +9,6 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity; import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;

View File

@@ -9,7 +9,6 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity; import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;

View File

@@ -9,7 +9,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.HttpEntity; import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import java.io.BufferedReader; import java.io.BufferedReader;

View File

@@ -10,7 +10,6 @@ import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.jdbc.core.JdbcTemplate;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -27,12 +27,11 @@ class SecurityFilterIT extends AbstractPostgresIT {
@Autowired @Autowired
private TestSecurityHelper securityHelper; private TestSecurityHelper securityHelper;
private String jwt;
private String viewerJwt; private String viewerJwt;
@BeforeEach @BeforeEach
void setUp() { void setUp() {
jwt = securityHelper.registerTestAgent("test-agent-security-filter-it"); securityHelper.registerTestAgent("test-agent-security-filter-it");
viewerJwt = securityHelper.viewerToken(); viewerJwt = securityHelper.viewerToken();
} }

View File

@@ -5,7 +5,7 @@ import java.util.List;
public interface AuditRepository { public interface AuditRepository {
void insert(AuditRecord record); void insert(AuditRecord auditRecord);
record AuditQuery( record AuditQuery(
String username, String username,

View File

@@ -30,9 +30,9 @@ public class AuditService {
HttpServletRequest request) { HttpServletRequest request) {
String ip = request != null ? request.getRemoteAddr() : null; String ip = request != null ? request.getRemoteAddr() : null;
String userAgent = request != null ? request.getHeader("User-Agent") : null; String userAgent = request != null ? request.getHeader("User-Agent") : null;
AuditRecord record = AuditRecord.create(username, action, category, target, detail, result, ip, userAgent); AuditRecord auditRecord = AuditRecord.create(username, action, category, target, detail, result, ip, userAgent);
repository.insert(record); repository.insert(auditRecord);
if (request != null) { if (request != null) {
request.setAttribute("audit.logged", true); request.setAttribute("audit.logged", true);

View File

@@ -13,7 +13,7 @@ import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.stream.Collectors;
/** /**
* In-memory agent registry managing agent lifecycle, heartbeats, and commands. * In-memory agent registry managing agent lifecycle, heartbeats, and commands.
@@ -53,7 +53,7 @@ public class AgentRegistryService {
List.copyOf(routeIds), Map.copyOf(capabilities), List.copyOf(routeIds), Map.copyOf(capabilities),
AgentState.LIVE, now, now, null); AgentState.LIVE, now, now, null);
AgentInfo result = agents.compute(id, (key, existing) -> { return agents.compute(id, (key, existing) -> {
if (existing != null) { if (existing != null) {
// Re-registration: update metadata, reset to LIVE // Re-registration: update metadata, reset to LIVE
log.info("Agent {} re-registering (was {})", id, existing.state()); log.info("Agent {} re-registering (was {})", id, existing.state());
@@ -67,8 +67,6 @@ public class AgentRegistryService {
log.info("Agent {} registered (name={}, application={})", id, name, application); log.info("Agent {} registered (name={}, application={})", id, name, application);
return newAgent; return newAgent;
}); });
return result;
} }
/** /**
@@ -196,7 +194,7 @@ public class AgentRegistryService {
public List<AgentInfo> findByState(AgentState state) { public List<AgentInfo> findByState(AgentState state) {
return agents.values().stream() return agents.values().stream()
.filter(a -> a.state() == state) .filter(a -> a.state() == state)
.collect(Collectors.toList()); .toList();
} }
/** /**
@@ -205,7 +203,7 @@ public class AgentRegistryService {
public List<AgentInfo> findByApplication(String application) { public List<AgentInfo> findByApplication(String application) {
return agents.values().stream() return agents.values().stream()
.filter(a -> application.equals(a.applicationId())) .filter(a -> application.equals(a.applicationId()))
.collect(Collectors.toList()); .toList();
} }
/** /**
@@ -276,7 +274,7 @@ public class AgentRegistryService {
} }
return queue.stream() return queue.stream()
.filter(cmd -> cmd.status() == CommandStatus.PENDING) .filter(cmd -> cmd.status() == CommandStatus.PENDING)
.collect(Collectors.toList()); .toList();
} }
/** /**

View File

@@ -29,7 +29,7 @@ public class DetailService {
// Prefer the raw processor tree (faithful to agent data) over // Prefer the raw processor tree (faithful to agent data) over
// flat-record reconstruction (which loses iteration context). // flat-record reconstruction (which loses iteration context).
List<ProcessorNode> processors = parseProcessorsJson(exec.processorsJson()); List<ProcessorNode> processors = parseProcessorsJson(exec.processorsJson());
if (processors == null) { if (processors.isEmpty()) {
// Fallback for executions ingested before processors_json was added // Fallback for executions ingested before processors_json was added
List<ProcessorRecord> records = executionStore.findProcessors(executionId); List<ProcessorRecord> records = executionStore.findProcessors(executionId);
processors = buildTree(records); processors = buildTree(records);
@@ -78,12 +78,12 @@ public class DetailService {
/** Parse the raw processor tree JSON stored alongside the execution. */ /** Parse the raw processor tree JSON stored alongside the execution. */
private List<ProcessorNode> parseProcessorsJson(String json) { private List<ProcessorNode> parseProcessorsJson(String json) {
if (json == null || json.isBlank()) return null; if (json == null || json.isBlank()) return List.of();
try { try {
List<ProcessorExecution> executions = JSON.readValue(json, PROCESSOR_EXEC_LIST); List<ProcessorExecution> executions = JSON.readValue(json, PROCESSOR_EXEC_LIST);
return convertProcessors(executions); return convertProcessors(executions);
} catch (Exception e) { } catch (Exception e) {
return null; return List.of();
} }
} }