fix(alerting): reject null fireMode on ExchangeMatchCondition + repair in-flight rows
All checks were successful
All checks were successful
The rule editor wizard reset the condition payload on kind-change without
seeding a fireMode default; the ExchangeMatchCondition ctor allowed null to
pass through; AlertEvaluatorJob then NPE-looped every tick on a saved rule.
- core: compact ctor now rejects null fireMode (Jackson-deser path only — all
production callers already pass a value).
- V14: repair existing EXCHANGE_MATCH rows with fireMode=null to
PER_EXCHANGE + perExchangeLingerSeconds=300 (default matches the wizard).
- ui: ConditionStep.onKindChange seeds EXCHANGE_MATCH defaults so the
Select's displayed fallback ("Per exchange") is actually in form state.
- ui: validateStep('condition', ...) now enforces fireMode presence + the
mode-specific fields before the user reaches Review.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -147,6 +147,19 @@ export function validateStep(step: WizardStep, f: FormState): string[] {
|
||||
}
|
||||
if (step === 'condition') {
|
||||
if (!f.conditionKind) errs.push('Condition kind is required.');
|
||||
if (f.conditionKind === 'EXCHANGE_MATCH') {
|
||||
const c = f.condition as Record<string, unknown>;
|
||||
if (!c.fireMode) {
|
||||
errs.push('Fire mode is required.');
|
||||
} else if (c.fireMode === 'PER_EXCHANGE') {
|
||||
if (c.perExchangeLingerSeconds == null) {
|
||||
errs.push('Linger seconds is required for PER_EXCHANGE.');
|
||||
}
|
||||
} else if (c.fireMode === 'COUNT_IN_WINDOW') {
|
||||
if (c.threshold == null) errs.push('Threshold is required for COUNT_IN_WINDOW.');
|
||||
if (c.windowSeconds == null) errs.push('Window (seconds) is required for COUNT_IN_WINDOW.');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (step === 'trigger') {
|
||||
if (f.evaluationIntervalSeconds < 5) errs.push('Evaluation interval must be \u2265 5 s.');
|
||||
|
||||
Reference in New Issue
Block a user