diff --git a/pkg/services/ngalert/models/alert_rule.go b/pkg/services/ngalert/models/alert_rule.go index fc00bab4b25..ba90840b590 100644 --- a/pkg/services/ngalert/models/alert_rule.go +++ b/pkg/services/ngalert/models/alert_rule.go @@ -14,6 +14,7 @@ import ( alertingModels "github.com/grafana/alerting/models" "github.com/grafana/grafana/pkg/services/quota" + "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util/cmputil" ) @@ -423,6 +424,42 @@ func (alertRule *AlertRule) PreSave(timeNow func() time.Time) error { return nil } +// ValidateAlertRule validates various alert rule fields. +func (alertRule *AlertRule) ValidateAlertRule(cfg setting.UnifiedAlertingSettings) error { + if len(alertRule.Data) == 0 { + return fmt.Errorf("%w: no queries or expressions are found", ErrAlertRuleFailedValidation) + } + + if alertRule.Title == "" { + return fmt.Errorf("%w: title is empty", ErrAlertRuleFailedValidation) + } + + if err := ValidateRuleGroupInterval(alertRule.IntervalSeconds, int64(cfg.BaseInterval.Seconds())); err != nil { + return err + } + + if alertRule.OrgID == 0 { + return fmt.Errorf("%w: no organisation is found", ErrAlertRuleFailedValidation) + } + + if alertRule.DashboardUID == nil && alertRule.PanelID != nil { + return fmt.Errorf("%w: cannot have Panel ID without a Dashboard UID", ErrAlertRuleFailedValidation) + } + + if _, err := ErrStateFromString(string(alertRule.ExecErrState)); err != nil { + return err + } + + if _, err := NoDataStateFromString(string(alertRule.NoDataState)); err != nil { + return err + } + + if alertRule.For < 0 { + return fmt.Errorf("%w: field `for` cannot be negative", ErrAlertRuleFailedValidation) + } + return nil +} + func (alertRule *AlertRule) ResourceType() string { return "alertRule" } diff --git a/pkg/services/ngalert/store/alert_rule.go b/pkg/services/ngalert/store/alert_rule.go index 0ac146f328d..e337d7d8aae 100644 --- a/pkg/services/ngalert/store/alert_rule.go +++ b/pkg/services/ngalert/store/alert_rule.go @@ -633,48 +633,21 @@ var GenerateNewAlertRuleUID = func(sess *db.Session, orgID int64, ruleTitle stri return "", ngmodels.ErrAlertRuleFailedGenerateUniqueUID } -// validateAlertRule validates the alert rule interval and organisation. +// validateAlertRule validates the alert rule including db-level restrictions on field lengths. func (st DBstore) validateAlertRule(alertRule ngmodels.AlertRule) error { - if len(alertRule.Data) == 0 { - return fmt.Errorf("%w: no queries or expressions are found", ngmodels.ErrAlertRuleFailedValidation) - } - - if alertRule.Title == "" { - return fmt.Errorf("%w: title is empty", ngmodels.ErrAlertRuleFailedValidation) - } - - if err := ngmodels.ValidateRuleGroupInterval(alertRule.IntervalSeconds, int64(st.Cfg.BaseInterval.Seconds())); err != nil { + if err := alertRule.ValidateAlertRule(st.Cfg); err != nil { return err } - // enfore max name length in SQLite + // enforce max name length. if len(alertRule.Title) > AlertRuleMaxTitleLength { return fmt.Errorf("%w: name length should not be greater than %d", ngmodels.ErrAlertRuleFailedValidation, AlertRuleMaxTitleLength) } - // enfore max rule group name length in SQLite + // enforce max rule group name length. if len(alertRule.RuleGroup) > AlertRuleMaxRuleGroupNameLength { return fmt.Errorf("%w: rule group name length should not be greater than %d", ngmodels.ErrAlertRuleFailedValidation, AlertRuleMaxRuleGroupNameLength) } - if alertRule.OrgID == 0 { - return fmt.Errorf("%w: no organisation is found", ngmodels.ErrAlertRuleFailedValidation) - } - - if alertRule.DashboardUID == nil && alertRule.PanelID != nil { - return fmt.Errorf("%w: cannot have Panel ID without a Dashboard UID", ngmodels.ErrAlertRuleFailedValidation) - } - - if _, err := ngmodels.ErrStateFromString(string(alertRule.ExecErrState)); err != nil { - return err - } - - if _, err := ngmodels.NoDataStateFromString(string(alertRule.NoDataState)); err != nil { - return err - } - - if alertRule.For < 0 { - return fmt.Errorf("%w: field `for` cannot be negative", ngmodels.ErrAlertRuleFailedValidation) - } return nil }