refactor(alerts): drop ACKNOWLEDGED from AlertState, add readAt/deletedAt to AlertInstance

- AlertState: remove ACKNOWLEDGED case (V17 migration already dropped it from DB enum)
- AlertInstance: insert readAt + deletedAt Instant fields after lastNotifiedAt; add withReadAt/withDeletedAt withers; update all existing withers to pass both fields positionally
- AlertStateTransitions: add null,null for readAt/deletedAt in newInstance ctor call; collapse FIRING,ACKNOWLEDGED switch arm to just FIRING
- AlertScopeTest: update AlertState.values() assertion to 3 values; fix stale ConditionKind.hasSize(6) to 7 (JVM_METRIC was added earlier)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-21 17:12:37 +02:00
parent e95c21d0cb
commit 82e82350f9
4 changed files with 34 additions and 16 deletions

View File

@@ -17,6 +17,8 @@ public record AlertInstance(
String ackedBy,
Instant resolvedAt,
Instant lastNotifiedAt,
Instant readAt, // NEW — global "someone has seen this"
Instant deletedAt, // NEW — soft delete
boolean silenced,
Double currentValue,
Double threshold,
@@ -39,63 +41,77 @@ public record AlertInstance(
public AlertInstance withState(AlertState s) {
return new AlertInstance(id, ruleId, ruleSnapshot, environmentId,
s, severity, firedAt, ackedAt, ackedBy, resolvedAt, lastNotifiedAt, silenced,
s, severity, firedAt, ackedAt, ackedBy, resolvedAt, lastNotifiedAt, readAt, deletedAt, silenced,
currentValue, threshold, context, title, message,
targetUserIds, targetGroupIds, targetRoleNames);
}
public AlertInstance withFiredAt(Instant i) {
return new AlertInstance(id, ruleId, ruleSnapshot, environmentId,
state, severity, i, ackedAt, ackedBy, resolvedAt, lastNotifiedAt, silenced,
state, severity, i, ackedAt, ackedBy, resolvedAt, lastNotifiedAt, readAt, deletedAt, silenced,
currentValue, threshold, context, title, message,
targetUserIds, targetGroupIds, targetRoleNames);
}
public AlertInstance withResolvedAt(Instant i) {
return new AlertInstance(id, ruleId, ruleSnapshot, environmentId,
state, severity, firedAt, ackedAt, ackedBy, i, lastNotifiedAt, silenced,
state, severity, firedAt, ackedAt, ackedBy, i, lastNotifiedAt, readAt, deletedAt, silenced,
currentValue, threshold, context, title, message,
targetUserIds, targetGroupIds, targetRoleNames);
}
public AlertInstance withAck(String ackedBy, Instant ackedAt) {
return new AlertInstance(id, ruleId, ruleSnapshot, environmentId,
state, severity, firedAt, ackedAt, ackedBy, resolvedAt, lastNotifiedAt, silenced,
state, severity, firedAt, ackedAt, ackedBy, resolvedAt, lastNotifiedAt, readAt, deletedAt, silenced,
currentValue, threshold, context, title, message,
targetUserIds, targetGroupIds, targetRoleNames);
}
public AlertInstance withSilenced(boolean silenced) {
return new AlertInstance(id, ruleId, ruleSnapshot, environmentId,
state, severity, firedAt, ackedAt, ackedBy, resolvedAt, lastNotifiedAt, silenced,
state, severity, firedAt, ackedAt, ackedBy, resolvedAt, lastNotifiedAt, readAt, deletedAt, silenced,
currentValue, threshold, context, title, message,
targetUserIds, targetGroupIds, targetRoleNames);
}
public AlertInstance withTitleMessage(String title, String message) {
return new AlertInstance(id, ruleId, ruleSnapshot, environmentId,
state, severity, firedAt, ackedAt, ackedBy, resolvedAt, lastNotifiedAt, silenced,
state, severity, firedAt, ackedAt, ackedBy, resolvedAt, lastNotifiedAt, readAt, deletedAt, silenced,
currentValue, threshold, context, title, message,
targetUserIds, targetGroupIds, targetRoleNames);
}
public AlertInstance withLastNotifiedAt(Instant instant) {
return new AlertInstance(id, ruleId, ruleSnapshot, environmentId,
state, severity, firedAt, ackedAt, ackedBy, resolvedAt, instant, silenced,
state, severity, firedAt, ackedAt, ackedBy, resolvedAt, instant, readAt, deletedAt, silenced,
currentValue, threshold, context, title, message,
targetUserIds, targetGroupIds, targetRoleNames);
}
public AlertInstance withContext(Map<String, Object> context) {
return new AlertInstance(id, ruleId, ruleSnapshot, environmentId,
state, severity, firedAt, ackedAt, ackedBy, resolvedAt, lastNotifiedAt, silenced,
state, severity, firedAt, ackedAt, ackedBy, resolvedAt, lastNotifiedAt, readAt, deletedAt, silenced,
currentValue, threshold, context, title, message,
targetUserIds, targetGroupIds, targetRoleNames);
}
public AlertInstance withRuleSnapshot(Map<String, Object> snapshot) {
return new AlertInstance(id, ruleId, snapshot, environmentId,
state, severity, firedAt, ackedAt, ackedBy, resolvedAt, lastNotifiedAt, silenced,
state, severity, firedAt, ackedAt, ackedBy, resolvedAt, lastNotifiedAt, readAt, deletedAt, silenced,
currentValue, threshold, context, title, message,
targetUserIds, targetGroupIds, targetRoleNames);
}
public AlertInstance withReadAt(Instant i) {
return new AlertInstance(id, ruleId, ruleSnapshot, environmentId,
state, severity, firedAt, ackedAt, ackedBy, resolvedAt, lastNotifiedAt, i, deletedAt, silenced,
currentValue, threshold, context, title, message,
targetUserIds, targetGroupIds, targetRoleNames);
}
public AlertInstance withDeletedAt(Instant i) {
return new AlertInstance(id, ruleId, ruleSnapshot, environmentId,
state, severity, firedAt, ackedAt, ackedBy, resolvedAt, lastNotifiedAt, readAt, i, silenced,
currentValue, threshold, context, title, message,
targetUserIds, targetGroupIds, targetRoleNames);
}

View File

@@ -1,3 +1,3 @@
package com.cameleer.server.core.alerting;
public enum AlertState { PENDING, FIRING, ACKNOWLEDGED, RESOLVED }
public enum AlertState { PENDING, FIRING, RESOLVED }

View File

@@ -23,8 +23,8 @@ class AlertScopeTest {
assertThat(AlertSeverity.values()).containsExactly(
AlertSeverity.CRITICAL, AlertSeverity.WARNING, AlertSeverity.INFO);
assertThat(AlertState.values()).containsExactly(
AlertState.PENDING, AlertState.FIRING, AlertState.ACKNOWLEDGED, AlertState.RESOLVED);
assertThat(ConditionKind.values()).hasSize(6);
AlertState.PENDING, AlertState.FIRING, AlertState.RESOLVED);
assertThat(ConditionKind.values()).hasSize(7);
assertThat(TargetKind.values()).containsExactly(
TargetKind.USER, TargetKind.GROUP, TargetKind.ROLE);
assertThat(NotificationStatus.values()).containsExactly(