fix(outbound): wire rulesReferencing to AlertRuleRepository (Plan 01 gate)
Replaces the Plan 01 stub that returned [] with a real call through AlertRuleRepository.findRuleIdsByOutboundConnectionId. Adds AlertingBeanConfig exposing the AlertRuleRepository bean; widens OutboundBeanConfig constructor to inject it. Delete and narrow-envs guards now correctly block when rules reference a connection. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
package com.cameleer.server.app.alerting.config;
|
||||||
|
|
||||||
|
import com.cameleer.server.app.alerting.storage.PostgresAlertRuleRepository;
|
||||||
|
import com.cameleer.server.core.alerting.AlertRuleRepository;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class AlertingBeanConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AlertRuleRepository alertRuleRepository(JdbcTemplate jdbc, ObjectMapper om) {
|
||||||
|
return new PostgresAlertRuleRepository(jdbc, om);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.cameleer.server.app.outbound;
|
package com.cameleer.server.app.outbound;
|
||||||
|
|
||||||
|
import com.cameleer.server.core.alerting.AlertRuleRepository;
|
||||||
import com.cameleer.server.core.outbound.OutboundConnection;
|
import com.cameleer.server.core.outbound.OutboundConnection;
|
||||||
import com.cameleer.server.core.outbound.OutboundConnectionRepository;
|
import com.cameleer.server.core.outbound.OutboundConnectionRepository;
|
||||||
import com.cameleer.server.core.outbound.OutboundConnectionService;
|
import com.cameleer.server.core.outbound.OutboundConnectionService;
|
||||||
@@ -13,10 +14,15 @@ import java.util.UUID;
|
|||||||
public class OutboundConnectionServiceImpl implements OutboundConnectionService {
|
public class OutboundConnectionServiceImpl implements OutboundConnectionService {
|
||||||
|
|
||||||
private final OutboundConnectionRepository repo;
|
private final OutboundConnectionRepository repo;
|
||||||
|
private final AlertRuleRepository ruleRepo;
|
||||||
private final String tenantId;
|
private final String tenantId;
|
||||||
|
|
||||||
public OutboundConnectionServiceImpl(OutboundConnectionRepository repo, String tenantId) {
|
public OutboundConnectionServiceImpl(
|
||||||
|
OutboundConnectionRepository repo,
|
||||||
|
AlertRuleRepository ruleRepo,
|
||||||
|
String tenantId) {
|
||||||
this.repo = repo;
|
this.repo = repo;
|
||||||
|
this.ruleRepo = ruleRepo;
|
||||||
this.tenantId = tenantId;
|
this.tenantId = tenantId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,8 +97,7 @@ public class OutboundConnectionServiceImpl implements OutboundConnectionService
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<UUID> rulesReferencing(UUID id) {
|
public List<UUID> rulesReferencing(UUID id) {
|
||||||
// Plan 01 stub. Plan 02 will wire this to AlertRuleRepository.
|
return ruleRepo.findRuleIdsByOutboundConnectionId(id);
|
||||||
return List.of();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertNameUnique(String name, UUID excludingId) {
|
private void assertNameUnique(String name, UUID excludingId) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.cameleer.server.app.outbound.config;
|
|||||||
import com.cameleer.server.app.outbound.OutboundConnectionServiceImpl;
|
import com.cameleer.server.app.outbound.OutboundConnectionServiceImpl;
|
||||||
import com.cameleer.server.app.outbound.crypto.SecretCipher;
|
import com.cameleer.server.app.outbound.crypto.SecretCipher;
|
||||||
import com.cameleer.server.app.outbound.storage.PostgresOutboundConnectionRepository;
|
import com.cameleer.server.app.outbound.storage.PostgresOutboundConnectionRepository;
|
||||||
|
import com.cameleer.server.core.alerting.AlertRuleRepository;
|
||||||
import com.cameleer.server.core.outbound.OutboundConnectionRepository;
|
import com.cameleer.server.core.outbound.OutboundConnectionRepository;
|
||||||
import com.cameleer.server.core.outbound.OutboundConnectionService;
|
import com.cameleer.server.core.outbound.OutboundConnectionService;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
@@ -29,7 +30,8 @@ public class OutboundBeanConfig {
|
|||||||
@Bean
|
@Bean
|
||||||
public OutboundConnectionService outboundConnectionService(
|
public OutboundConnectionService outboundConnectionService(
|
||||||
OutboundConnectionRepository repo,
|
OutboundConnectionRepository repo,
|
||||||
|
AlertRuleRepository ruleRepo,
|
||||||
@Value("${cameleer.server.tenant.id:default}") String tenantId) {
|
@Value("${cameleer.server.tenant.id:default}") String tenantId) {
|
||||||
return new OutboundConnectionServiceImpl(repo, tenantId);
|
return new OutboundConnectionServiceImpl(repo, ruleRepo, tenantId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package com.cameleer.server.app.outbound;
|
||||||
|
|
||||||
|
import com.cameleer.server.app.AbstractPostgresIT;
|
||||||
|
import com.cameleer.server.app.alerting.storage.PostgresAlertRuleRepository;
|
||||||
|
import com.cameleer.server.core.alerting.*;
|
||||||
|
import com.cameleer.server.core.http.TrustMode;
|
||||||
|
import com.cameleer.server.core.outbound.*;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
|
class OutboundConnectionServiceRulesReferencingIT extends AbstractPostgresIT {
|
||||||
|
|
||||||
|
@Autowired OutboundConnectionService service;
|
||||||
|
@Autowired OutboundConnectionRepository repo;
|
||||||
|
|
||||||
|
private UUID envId;
|
||||||
|
private UUID connId;
|
||||||
|
private UUID ruleId;
|
||||||
|
private PostgresAlertRuleRepository ruleRepo;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void seed() {
|
||||||
|
ruleRepo = new PostgresAlertRuleRepository(jdbcTemplate, new ObjectMapper());
|
||||||
|
envId = UUID.randomUUID();
|
||||||
|
jdbcTemplate.update(
|
||||||
|
"INSERT INTO environments (id, slug, display_name) VALUES (?, ?, ?)",
|
||||||
|
envId, "env-" + UUID.randomUUID(), "Test Env");
|
||||||
|
jdbcTemplate.update(
|
||||||
|
"INSERT INTO users (user_id, provider, email) VALUES ('u-ref', 'local', 'a@b.test')" +
|
||||||
|
" ON CONFLICT (user_id) DO NOTHING");
|
||||||
|
|
||||||
|
var c = repo.save(new OutboundConnection(
|
||||||
|
UUID.randomUUID(), "default", "conn-" + UUID.randomUUID(), null,
|
||||||
|
"https://example.test", OutboundMethod.POST,
|
||||||
|
Map.of(), null, TrustMode.SYSTEM_DEFAULT, List.of(), null,
|
||||||
|
new OutboundAuth.None(), List.of(),
|
||||||
|
Instant.now(), "u-ref", Instant.now(), "u-ref"));
|
||||||
|
connId = c.id();
|
||||||
|
|
||||||
|
ruleId = UUID.randomUUID();
|
||||||
|
var rule = new AlertRule(
|
||||||
|
ruleId, envId, "r", null, AlertSeverity.WARNING, true,
|
||||||
|
ConditionKind.AGENT_STATE,
|
||||||
|
new AgentStateCondition(new AlertScope(null, null, null), "DEAD", 60),
|
||||||
|
60, 0, 60, "t", "m",
|
||||||
|
List.of(new WebhookBinding(UUID.randomUUID(), connId, null, Map.of())),
|
||||||
|
List.of(), Instant.now(), null, null, Map.of(),
|
||||||
|
Instant.now(), "u-ref", Instant.now(), "u-ref");
|
||||||
|
ruleRepo.save(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void cleanup() {
|
||||||
|
jdbcTemplate.update("DELETE FROM alert_rules WHERE id = ?", ruleId);
|
||||||
|
jdbcTemplate.update("DELETE FROM outbound_connections WHERE id = ?", connId);
|
||||||
|
jdbcTemplate.update("DELETE FROM environments WHERE id = ?", envId);
|
||||||
|
jdbcTemplate.update("DELETE FROM users WHERE user_id = 'u-ref'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void deleteConnectionReferencedByRuleReturns409() {
|
||||||
|
assertThat(service.rulesReferencing(connId)).hasSize(1);
|
||||||
|
assertThatThrownBy(() -> service.delete(connId, "u-ref"))
|
||||||
|
.isInstanceOf(ResponseStatusException.class)
|
||||||
|
.hasMessageContaining("referenced by rules");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user