mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: Sql store split for legacy alerting (#52901)
Moves ~20 sqlstore methods for legacy alerting out of sqlstore (sqlstore.Store interface) and into alerting.
This commit is contained in:
@@ -1,367 +0,0 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
// timeNow makes it possible to test usage of time
|
||||
var timeNow = time.Now
|
||||
|
||||
func (ss *SQLStore) GetAlertById(ctx context.Context, query *models.GetAlertByIdQuery) error {
|
||||
return ss.WithDbSession(ctx, func(sess *DBSession) error {
|
||||
alert := models.Alert{}
|
||||
has, err := sess.ID(query.Id).Get(&alert)
|
||||
if !has {
|
||||
return fmt.Errorf("could not find alert")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query.Result = &alert
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *SQLStore) GetAllAlertQueryHandler(ctx context.Context, query *models.GetAllAlertsQuery) error {
|
||||
return ss.WithDbSession(ctx, func(sess *DBSession) error {
|
||||
var alerts []*models.Alert
|
||||
err := sess.SQL("select * from alert").Find(&alerts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query.Result = alerts
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func deleteAlertByIdInternal(alertId int64, reason string, sess *DBSession) error {
|
||||
sqlog.Debug("Deleting alert", "id", alertId, "reason", reason)
|
||||
|
||||
if _, err := sess.Exec("DELETE FROM alert WHERE id = ?", alertId); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := sess.Exec("DELETE FROM annotation WHERE alert_id = ?", alertId); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := sess.Exec("DELETE FROM alert_notification_state WHERE alert_id = ?", alertId); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := sess.Exec("DELETE FROM alert_rule_tag WHERE alert_id = ?", alertId); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ss *SQLStore) HandleAlertsQuery(ctx context.Context, query *models.GetAlertsQuery) error {
|
||||
return ss.WithDbSession(ctx, func(sess *DBSession) error {
|
||||
builder := SQLBuilder{}
|
||||
|
||||
builder.Write(`SELECT
|
||||
alert.id,
|
||||
alert.dashboard_id,
|
||||
alert.panel_id,
|
||||
alert.name,
|
||||
alert.state,
|
||||
alert.new_state_date,
|
||||
alert.eval_data,
|
||||
alert.eval_date,
|
||||
alert.execution_error,
|
||||
dashboard.uid as dashboard_uid,
|
||||
dashboard.slug as dashboard_slug
|
||||
FROM alert
|
||||
INNER JOIN dashboard on dashboard.id = alert.dashboard_id `)
|
||||
|
||||
builder.Write(`WHERE alert.org_id = ?`, query.OrgId)
|
||||
|
||||
if len(strings.TrimSpace(query.Query)) > 0 {
|
||||
builder.Write(" AND alert.name "+dialect.LikeStr()+" ?", "%"+query.Query+"%")
|
||||
}
|
||||
|
||||
if len(query.DashboardIDs) > 0 {
|
||||
builder.sql.WriteString(` AND alert.dashboard_id IN (?` + strings.Repeat(",?", len(query.DashboardIDs)-1) + `) `)
|
||||
|
||||
for _, dbID := range query.DashboardIDs {
|
||||
builder.AddParams(dbID)
|
||||
}
|
||||
}
|
||||
|
||||
if query.PanelId != 0 {
|
||||
builder.Write(` AND alert.panel_id = ?`, query.PanelId)
|
||||
}
|
||||
|
||||
if len(query.State) > 0 && query.State[0] != "all" {
|
||||
builder.Write(` AND (`)
|
||||
for i, v := range query.State {
|
||||
if i > 0 {
|
||||
builder.Write(" OR ")
|
||||
}
|
||||
if strings.HasPrefix(v, "not_") {
|
||||
builder.Write("state <> ? ")
|
||||
v = strings.TrimPrefix(v, "not_")
|
||||
} else {
|
||||
builder.Write("state = ? ")
|
||||
}
|
||||
builder.AddParams(v)
|
||||
}
|
||||
builder.Write(")")
|
||||
}
|
||||
|
||||
if query.User.OrgRole != models.ROLE_ADMIN {
|
||||
builder.WriteDashboardPermissionFilter(query.User, models.PERMISSION_VIEW)
|
||||
}
|
||||
|
||||
builder.Write(" ORDER BY name ASC")
|
||||
|
||||
if query.Limit != 0 {
|
||||
builder.Write(dialect.Limit(query.Limit))
|
||||
}
|
||||
|
||||
alerts := make([]*models.AlertListItemDTO, 0)
|
||||
if err := sess.SQL(builder.GetSQLString(), builder.params...).Find(&alerts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range alerts {
|
||||
if alerts[i].ExecutionError == " " {
|
||||
alerts[i].ExecutionError = ""
|
||||
}
|
||||
}
|
||||
|
||||
query.Result = alerts
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *SQLStore) SaveAlerts(ctx context.Context, dashID int64, alerts []*models.Alert) error {
|
||||
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||
existingAlerts, err := GetAlertsByDashboardId2(dashID, sess)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := updateAlerts(existingAlerts, alerts, sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := deleteMissingAlerts(existingAlerts, alerts, sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func updateAlerts(existingAlerts []*models.Alert, alerts []*models.Alert, sess *DBSession) error {
|
||||
for _, alert := range alerts {
|
||||
update := false
|
||||
var alertToUpdate *models.Alert
|
||||
|
||||
for _, k := range existingAlerts {
|
||||
if alert.PanelId == k.PanelId {
|
||||
update = true
|
||||
alert.Id = k.Id
|
||||
alertToUpdate = k
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if update {
|
||||
if alertToUpdate.ContainsUpdates(alert) {
|
||||
alert.Updated = timeNow()
|
||||
alert.State = alertToUpdate.State
|
||||
sess.MustCols("message", "for")
|
||||
|
||||
_, err := sess.ID(alert.Id).Update(alert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sqlog.Debug("Alert updated", "name", alert.Name, "id", alert.Id)
|
||||
}
|
||||
} else {
|
||||
alert.Updated = timeNow()
|
||||
alert.Created = timeNow()
|
||||
alert.State = models.AlertStateUnknown
|
||||
alert.NewStateDate = timeNow()
|
||||
|
||||
_, err := sess.Insert(alert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sqlog.Debug("Alert inserted", "name", alert.Name, "id", alert.Id)
|
||||
}
|
||||
tags := alert.GetTagsFromSettings()
|
||||
if _, err := sess.Exec("DELETE FROM alert_rule_tag WHERE alert_id = ?", alert.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
if tags != nil {
|
||||
tags, err := EnsureTagsExist(sess, tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, tag := range tags {
|
||||
if _, err := sess.Exec("INSERT INTO alert_rule_tag (alert_id, tag_id) VALUES(?,?)", alert.Id, tag.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteMissingAlerts(alerts []*models.Alert, existingAlerts []*models.Alert, sess *DBSession) error {
|
||||
for _, missingAlert := range alerts {
|
||||
missing := true
|
||||
|
||||
for _, k := range existingAlerts {
|
||||
if missingAlert.PanelId == k.PanelId {
|
||||
missing = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if missing {
|
||||
if err := deleteAlertByIdInternal(missingAlert.Id, "Removed from dashboard", sess); err != nil {
|
||||
// No use trying to delete more, since we're in a transaction and it will be
|
||||
// rolled back on error.
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetAlertsByDashboardId2(dashboardId int64, sess *DBSession) ([]*models.Alert, error) {
|
||||
alerts := make([]*models.Alert, 0)
|
||||
err := sess.Where("dashboard_id = ?", dashboardId).Find(&alerts)
|
||||
|
||||
if err != nil {
|
||||
return []*models.Alert{}, err
|
||||
}
|
||||
|
||||
return alerts, nil
|
||||
}
|
||||
|
||||
func (ss *SQLStore) SetAlertState(ctx context.Context, cmd *models.SetAlertStateCommand) error {
|
||||
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||
alert := models.Alert{}
|
||||
|
||||
if has, err := sess.ID(cmd.AlertId).Get(&alert); err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return fmt.Errorf("could not find alert")
|
||||
}
|
||||
|
||||
if alert.State == models.AlertStatePaused {
|
||||
return models.ErrCannotChangeStateOnPausedAlert
|
||||
}
|
||||
|
||||
if alert.State == cmd.State {
|
||||
return models.ErrRequiresNewState
|
||||
}
|
||||
|
||||
alert.State = cmd.State
|
||||
alert.StateChanges++
|
||||
alert.NewStateDate = timeNow()
|
||||
alert.EvalData = cmd.EvalData
|
||||
|
||||
if cmd.Error == "" {
|
||||
alert.ExecutionError = " " // without this space, xorm skips updating this field
|
||||
} else {
|
||||
alert.ExecutionError = cmd.Error
|
||||
}
|
||||
|
||||
_, err := sess.ID(alert.Id).Update(&alert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Result = alert
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *SQLStore) PauseAlert(ctx context.Context, cmd *models.PauseAlertCommand) error {
|
||||
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||
if len(cmd.AlertIds) == 0 {
|
||||
return fmt.Errorf("command contains no alertids")
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
params := make([]interface{}, 0)
|
||||
|
||||
buffer.WriteString(`UPDATE alert SET state = ?, new_state_date = ?`)
|
||||
if cmd.Paused {
|
||||
params = append(params, string(models.AlertStatePaused))
|
||||
params = append(params, timeNow().UTC())
|
||||
} else {
|
||||
params = append(params, string(models.AlertStateUnknown))
|
||||
params = append(params, timeNow().UTC())
|
||||
}
|
||||
|
||||
buffer.WriteString(` WHERE id IN (?` + strings.Repeat(",?", len(cmd.AlertIds)-1) + `)`)
|
||||
for _, v := range cmd.AlertIds {
|
||||
params = append(params, v)
|
||||
}
|
||||
|
||||
sqlOrArgs := append([]interface{}{buffer.String()}, params...)
|
||||
|
||||
res, err := sess.Exec(sqlOrArgs...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.ResultCount, _ = res.RowsAffected()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *SQLStore) PauseAllAlerts(ctx context.Context, cmd *models.PauseAllAlertCommand) error {
|
||||
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||
var newState string
|
||||
if cmd.Paused {
|
||||
newState = string(models.AlertStatePaused)
|
||||
} else {
|
||||
newState = string(models.AlertStateUnknown)
|
||||
}
|
||||
|
||||
res, err := sess.Exec(`UPDATE alert SET state = ?, new_state_date = ?`, newState, timeNow().UTC())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.ResultCount, _ = res.RowsAffected()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *SQLStore) GetAlertStatesForDashboard(ctx context.Context, query *models.GetAlertStatesForDashboardQuery) error {
|
||||
return ss.WithDbSession(ctx, func(sess *DBSession) error {
|
||||
var rawSQL = `SELECT
|
||||
id,
|
||||
dashboard_id,
|
||||
panel_id,
|
||||
state,
|
||||
new_state_date
|
||||
FROM alert
|
||||
WHERE org_id = ? AND dashboard_id = ?`
|
||||
|
||||
query.Result = make([]*models.AlertStateInfoDTO, 0)
|
||||
err := sess.SQL(rawSQL, query.OrgId, query.DashboardId).Find(&query.Result)
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
@@ -1,600 +0,0 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
type AlertNotificationStore interface {
|
||||
DeleteAlertNotification(ctx context.Context, cmd *models.DeleteAlertNotificationCommand) error
|
||||
DeleteAlertNotificationWithUid(ctx context.Context, cmd *models.DeleteAlertNotificationWithUidCommand) error
|
||||
GetAlertNotifications(ctx context.Context, query *models.GetAlertNotificationsQuery) error
|
||||
GetAlertNotificationUidWithId(ctx context.Context, query *models.GetAlertNotificationUidQuery) error
|
||||
GetAlertNotificationsWithUid(ctx context.Context, query *models.GetAlertNotificationsWithUidQuery) error
|
||||
GetAllAlertNotifications(ctx context.Context, query *models.GetAllAlertNotificationsQuery) error
|
||||
GetAlertNotificationsWithUidToSend(ctx context.Context, query *models.GetAlertNotificationsWithUidToSendQuery) error
|
||||
CreateAlertNotificationCommand(ctx context.Context, cmd *models.CreateAlertNotificationCommand) error
|
||||
UpdateAlertNotification(ctx context.Context, cmd *models.UpdateAlertNotificationCommand) error
|
||||
UpdateAlertNotificationWithUid(ctx context.Context, cmd *models.UpdateAlertNotificationWithUidCommand) error
|
||||
SetAlertNotificationStateToCompleteCommand(ctx context.Context, cmd *models.SetAlertNotificationStateToCompleteCommand) error
|
||||
SetAlertNotificationStateToPendingCommand(ctx context.Context, cmd *models.SetAlertNotificationStateToPendingCommand) error
|
||||
GetOrCreateAlertNotificationState(ctx context.Context, cmd *models.GetOrCreateNotificationStateQuery) error
|
||||
}
|
||||
|
||||
func (ss *SQLStore) DeleteAlertNotification(ctx context.Context, cmd *models.DeleteAlertNotificationCommand) error {
|
||||
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||
sql := "DELETE FROM alert_notification WHERE alert_notification.org_id = ? AND alert_notification.id = ?"
|
||||
res, err := sess.Exec(sql, cmd.OrgId, cmd.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rowsAffected, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return models.ErrAlertNotificationNotFound
|
||||
}
|
||||
|
||||
if _, err := sess.Exec("DELETE FROM alert_notification_state WHERE alert_notification_state.org_id = ? AND alert_notification_state.notifier_id = ?", cmd.OrgId, cmd.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *SQLStore) DeleteAlertNotificationWithUid(ctx context.Context, cmd *models.DeleteAlertNotificationWithUidCommand) error {
|
||||
existingNotification := &models.GetAlertNotificationsWithUidQuery{OrgId: cmd.OrgId, Uid: cmd.Uid}
|
||||
if err := getAlertNotificationWithUidInternal(ctx, existingNotification, ss.newSession(ctx)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if existingNotification.Result == nil {
|
||||
return models.ErrAlertNotificationNotFound
|
||||
}
|
||||
|
||||
cmd.DeletedAlertNotificationId = existingNotification.Result.Id
|
||||
deleteCommand := &models.DeleteAlertNotificationCommand{
|
||||
Id: existingNotification.Result.Id,
|
||||
OrgId: existingNotification.Result.OrgId,
|
||||
}
|
||||
if err := ss.DeleteAlertNotification(ctx, deleteCommand); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ss *SQLStore) GetAlertNotifications(ctx context.Context, query *models.GetAlertNotificationsQuery) error {
|
||||
return getAlertNotificationInternal(ctx, query, ss.newSession(ctx))
|
||||
}
|
||||
|
||||
func (ss *SQLStore) GetAlertNotificationUidWithId(ctx context.Context, query *models.GetAlertNotificationUidQuery) error {
|
||||
cacheKey := newAlertNotificationUidCacheKey(query.OrgId, query.Id)
|
||||
|
||||
if cached, found := ss.CacheService.Get(cacheKey); found {
|
||||
query.Result = cached.(string)
|
||||
return nil
|
||||
}
|
||||
|
||||
err := getAlertNotificationUidInternal(ctx, query, ss.newSession(ctx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ss.CacheService.Set(cacheKey, query.Result, -1) // Infinite, never changes
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newAlertNotificationUidCacheKey(orgID, notificationId int64) string {
|
||||
return fmt.Sprintf("notification-uid-by-org-%d-and-id-%d", orgID, notificationId)
|
||||
}
|
||||
|
||||
func (ss *SQLStore) GetAlertNotificationsWithUid(ctx context.Context, query *models.GetAlertNotificationsWithUidQuery) error {
|
||||
return getAlertNotificationWithUidInternal(ctx, query, ss.newSession(ctx))
|
||||
}
|
||||
|
||||
func (ss *SQLStore) GetAllAlertNotifications(ctx context.Context, query *models.GetAllAlertNotificationsQuery) error {
|
||||
return ss.WithDbSession(ctx, func(sess *DBSession) error {
|
||||
results := make([]*models.AlertNotification, 0)
|
||||
if err := sess.Where("org_id = ?", query.OrgId).Asc("name").Find(&results); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query.Result = results
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *SQLStore) GetAlertNotificationsWithUidToSend(ctx context.Context, query *models.GetAlertNotificationsWithUidToSendQuery) error {
|
||||
return ss.WithDbSession(ctx, func(sess *DBSession) error {
|
||||
var sql bytes.Buffer
|
||||
params := make([]interface{}, 0)
|
||||
|
||||
sql.WriteString(`SELECT
|
||||
alert_notification.id,
|
||||
alert_notification.uid,
|
||||
alert_notification.org_id,
|
||||
alert_notification.name,
|
||||
alert_notification.type,
|
||||
alert_notification.created,
|
||||
alert_notification.updated,
|
||||
alert_notification.settings,
|
||||
alert_notification.secure_settings,
|
||||
alert_notification.is_default,
|
||||
alert_notification.disable_resolve_message,
|
||||
alert_notification.send_reminder,
|
||||
alert_notification.frequency
|
||||
FROM alert_notification
|
||||
`)
|
||||
|
||||
sql.WriteString(` WHERE alert_notification.org_id = ?`)
|
||||
params = append(params, query.OrgId)
|
||||
|
||||
sql.WriteString(` AND ((alert_notification.is_default = ?)`)
|
||||
params = append(params, dialect.BooleanStr(true))
|
||||
|
||||
if len(query.Uids) > 0 {
|
||||
sql.WriteString(` OR alert_notification.uid IN (?` + strings.Repeat(",?", len(query.Uids)-1) + ")")
|
||||
for _, v := range query.Uids {
|
||||
params = append(params, v)
|
||||
}
|
||||
}
|
||||
sql.WriteString(`)`)
|
||||
|
||||
results := make([]*models.AlertNotification, 0)
|
||||
if err := sess.SQL(sql.String(), params...).Find(&results); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query.Result = results
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func getAlertNotificationUidInternal(ctx context.Context, query *models.GetAlertNotificationUidQuery, sess *DBSession) error {
|
||||
var sql bytes.Buffer
|
||||
params := make([]interface{}, 0)
|
||||
|
||||
sql.WriteString(`SELECT
|
||||
alert_notification.uid
|
||||
FROM alert_notification
|
||||
`)
|
||||
|
||||
sql.WriteString(` WHERE alert_notification.org_id = ?`)
|
||||
params = append(params, query.OrgId)
|
||||
|
||||
sql.WriteString(` AND alert_notification.id = ?`)
|
||||
params = append(params, query.Id)
|
||||
|
||||
results := make([]string, 0)
|
||||
if err := sess.SQL(sql.String(), params...).Find(&results); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(results) == 0 {
|
||||
return models.ErrAlertNotificationFailedTranslateUniqueID
|
||||
}
|
||||
|
||||
query.Result = results[0]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAlertNotificationInternal(ctx context.Context, query *models.GetAlertNotificationsQuery, sess *DBSession) error {
|
||||
var sql bytes.Buffer
|
||||
params := make([]interface{}, 0)
|
||||
|
||||
sql.WriteString(`SELECT
|
||||
alert_notification.id,
|
||||
alert_notification.uid,
|
||||
alert_notification.org_id,
|
||||
alert_notification.name,
|
||||
alert_notification.type,
|
||||
alert_notification.created,
|
||||
alert_notification.updated,
|
||||
alert_notification.settings,
|
||||
alert_notification.secure_settings,
|
||||
alert_notification.is_default,
|
||||
alert_notification.disable_resolve_message,
|
||||
alert_notification.send_reminder,
|
||||
alert_notification.frequency
|
||||
FROM alert_notification
|
||||
`)
|
||||
|
||||
sql.WriteString(` WHERE alert_notification.org_id = ?`)
|
||||
params = append(params, query.OrgId)
|
||||
|
||||
if query.Name != "" || query.Id != 0 {
|
||||
if query.Name != "" {
|
||||
sql.WriteString(` AND alert_notification.name = ?`)
|
||||
params = append(params, query.Name)
|
||||
}
|
||||
|
||||
if query.Id != 0 {
|
||||
sql.WriteString(` AND alert_notification.id = ?`)
|
||||
params = append(params, query.Id)
|
||||
}
|
||||
}
|
||||
|
||||
results := make([]*models.AlertNotification, 0)
|
||||
if err := sess.SQL(sql.String(), params...).Find(&results); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(results) == 0 {
|
||||
query.Result = nil
|
||||
} else {
|
||||
query.Result = results[0]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAlertNotificationWithUidInternal(ctx context.Context, query *models.GetAlertNotificationsWithUidQuery, sess *DBSession) error {
|
||||
var sql bytes.Buffer
|
||||
params := make([]interface{}, 0)
|
||||
|
||||
sql.WriteString(`SELECT
|
||||
alert_notification.id,
|
||||
alert_notification.uid,
|
||||
alert_notification.org_id,
|
||||
alert_notification.name,
|
||||
alert_notification.type,
|
||||
alert_notification.created,
|
||||
alert_notification.updated,
|
||||
alert_notification.settings,
|
||||
alert_notification.secure_settings,
|
||||
alert_notification.is_default,
|
||||
alert_notification.disable_resolve_message,
|
||||
alert_notification.send_reminder,
|
||||
alert_notification.frequency
|
||||
FROM alert_notification
|
||||
`)
|
||||
|
||||
sql.WriteString(` WHERE alert_notification.org_id = ? AND alert_notification.uid = ?`)
|
||||
params = append(params, query.OrgId, query.Uid)
|
||||
|
||||
results := make([]*models.AlertNotification, 0)
|
||||
if err := sess.SQL(sql.String(), params...).Find(&results); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(results) == 0 {
|
||||
query.Result = nil
|
||||
} else {
|
||||
query.Result = results[0]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ss *SQLStore) CreateAlertNotificationCommand(ctx context.Context, cmd *models.CreateAlertNotificationCommand) error {
|
||||
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||
if cmd.Uid == "" {
|
||||
uid, uidGenerationErr := generateNewAlertNotificationUid(ctx, sess, cmd.OrgId)
|
||||
if uidGenerationErr != nil {
|
||||
return uidGenerationErr
|
||||
}
|
||||
|
||||
cmd.Uid = uid
|
||||
}
|
||||
existingQuery := &models.GetAlertNotificationsWithUidQuery{OrgId: cmd.OrgId, Uid: cmd.Uid}
|
||||
err := getAlertNotificationWithUidInternal(ctx, existingQuery, sess)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if existingQuery.Result != nil {
|
||||
return models.ErrAlertNotificationWithSameUIDExists
|
||||
}
|
||||
|
||||
// check if name exists
|
||||
sameNameQuery := &models.GetAlertNotificationsQuery{OrgId: cmd.OrgId, Name: cmd.Name}
|
||||
if err := getAlertNotificationInternal(ctx, sameNameQuery, sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sameNameQuery.Result != nil {
|
||||
return models.ErrAlertNotificationWithSameNameExists
|
||||
}
|
||||
|
||||
var frequency time.Duration
|
||||
if cmd.SendReminder {
|
||||
if cmd.Frequency == "" {
|
||||
return models.ErrNotificationFrequencyNotFound
|
||||
}
|
||||
|
||||
frequency, err = time.ParseDuration(cmd.Frequency)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// delete empty keys
|
||||
for k, v := range cmd.SecureSettings {
|
||||
if v == "" {
|
||||
delete(cmd.SecureSettings, k)
|
||||
}
|
||||
}
|
||||
|
||||
alertNotification := &models.AlertNotification{
|
||||
Uid: cmd.Uid,
|
||||
OrgId: cmd.OrgId,
|
||||
Name: cmd.Name,
|
||||
Type: cmd.Type,
|
||||
Settings: cmd.Settings,
|
||||
SecureSettings: cmd.EncryptedSecureSettings,
|
||||
SendReminder: cmd.SendReminder,
|
||||
DisableResolveMessage: cmd.DisableResolveMessage,
|
||||
Frequency: frequency,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
IsDefault: cmd.IsDefault,
|
||||
}
|
||||
|
||||
if _, err = sess.MustCols("send_reminder").Insert(alertNotification); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Result = alertNotification
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func generateNewAlertNotificationUid(ctx context.Context, sess *DBSession, orgId int64) (string, error) {
|
||||
for i := 0; i < 3; i++ {
|
||||
uid := util.GenerateShortUID()
|
||||
exists, err := sess.Where("org_id=? AND uid=?", orgId, uid).Get(&models.AlertNotification{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return uid, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", models.ErrAlertNotificationFailedGenerateUniqueUid
|
||||
}
|
||||
|
||||
func (ss *SQLStore) UpdateAlertNotification(ctx context.Context, cmd *models.UpdateAlertNotificationCommand) error {
|
||||
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) (err error) {
|
||||
current := models.AlertNotification{}
|
||||
|
||||
if _, err = sess.ID(cmd.Id).Get(¤t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if current.Id == 0 {
|
||||
return models.ErrAlertNotificationNotFound
|
||||
}
|
||||
|
||||
// check if name exists
|
||||
sameNameQuery := &models.GetAlertNotificationsQuery{OrgId: cmd.OrgId, Name: cmd.Name}
|
||||
if err := getAlertNotificationInternal(ctx, sameNameQuery, sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sameNameQuery.Result != nil && sameNameQuery.Result.Id != current.Id {
|
||||
return fmt.Errorf("alert notification name %q already exists", cmd.Name)
|
||||
}
|
||||
|
||||
// delete empty keys
|
||||
for k, v := range cmd.SecureSettings {
|
||||
if v == "" {
|
||||
delete(cmd.SecureSettings, k)
|
||||
}
|
||||
}
|
||||
|
||||
current.Updated = time.Now()
|
||||
current.Settings = cmd.Settings
|
||||
current.SecureSettings = cmd.EncryptedSecureSettings
|
||||
current.Name = cmd.Name
|
||||
current.Type = cmd.Type
|
||||
current.IsDefault = cmd.IsDefault
|
||||
current.SendReminder = cmd.SendReminder
|
||||
current.DisableResolveMessage = cmd.DisableResolveMessage
|
||||
|
||||
if cmd.Uid != "" {
|
||||
current.Uid = cmd.Uid
|
||||
}
|
||||
|
||||
if current.SendReminder {
|
||||
if cmd.Frequency == "" {
|
||||
return models.ErrNotificationFrequencyNotFound
|
||||
}
|
||||
|
||||
frequency, err := time.ParseDuration(cmd.Frequency)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
current.Frequency = frequency
|
||||
}
|
||||
|
||||
sess.UseBool("is_default", "send_reminder", "disable_resolve_message")
|
||||
|
||||
if affected, err := sess.ID(cmd.Id).Update(current); err != nil {
|
||||
return err
|
||||
} else if affected == 0 {
|
||||
return fmt.Errorf("could not update alert notification")
|
||||
}
|
||||
|
||||
cmd.Result = ¤t
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *SQLStore) UpdateAlertNotificationWithUid(ctx context.Context, cmd *models.UpdateAlertNotificationWithUidCommand) error {
|
||||
getAlertNotificationWithUidQuery := &models.GetAlertNotificationsWithUidQuery{OrgId: cmd.OrgId, Uid: cmd.Uid}
|
||||
|
||||
if err := getAlertNotificationWithUidInternal(ctx, getAlertNotificationWithUidQuery, ss.newSession(ctx)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
current := getAlertNotificationWithUidQuery.Result
|
||||
|
||||
if current == nil {
|
||||
return models.ErrAlertNotificationNotFound
|
||||
}
|
||||
|
||||
if cmd.NewUid == "" {
|
||||
cmd.NewUid = cmd.Uid
|
||||
}
|
||||
|
||||
updateNotification := &models.UpdateAlertNotificationCommand{
|
||||
Id: current.Id,
|
||||
Uid: cmd.NewUid,
|
||||
Name: cmd.Name,
|
||||
Type: cmd.Type,
|
||||
SendReminder: cmd.SendReminder,
|
||||
DisableResolveMessage: cmd.DisableResolveMessage,
|
||||
Frequency: cmd.Frequency,
|
||||
IsDefault: cmd.IsDefault,
|
||||
Settings: cmd.Settings,
|
||||
SecureSettings: cmd.SecureSettings,
|
||||
|
||||
OrgId: cmd.OrgId,
|
||||
}
|
||||
|
||||
if err := ss.UpdateAlertNotification(ctx, updateNotification); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Result = updateNotification.Result
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ss *SQLStore) SetAlertNotificationStateToCompleteCommand(ctx context.Context, cmd *models.SetAlertNotificationStateToCompleteCommand) error {
|
||||
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||
version := cmd.Version
|
||||
var current models.AlertNotificationState
|
||||
if _, err := sess.ID(cmd.Id).Get(¤t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newVersion := cmd.Version + 1
|
||||
sql := `UPDATE alert_notification_state SET
|
||||
state = ?,
|
||||
version = ?,
|
||||
updated_at = ?
|
||||
WHERE
|
||||
id = ?`
|
||||
|
||||
_, err := sess.Exec(sql, models.AlertNotificationStateCompleted, newVersion, timeNow().Unix(), cmd.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if current.Version != version {
|
||||
sqlog.Error("notification state out of sync. the notification is marked as complete but has been modified between set as pending and completion.", "notifierId", current.NotifierId)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *SQLStore) SetAlertNotificationStateToPendingCommand(ctx context.Context, cmd *models.SetAlertNotificationStateToPendingCommand) error {
|
||||
return ss.WithDbSession(ctx, func(sess *DBSession) error {
|
||||
newVersion := cmd.Version + 1
|
||||
sql := `UPDATE alert_notification_state SET
|
||||
state = ?,
|
||||
version = ?,
|
||||
updated_at = ?,
|
||||
alert_rule_state_updated_version = ?
|
||||
WHERE
|
||||
id = ? AND
|
||||
(version = ? OR alert_rule_state_updated_version < ?)`
|
||||
|
||||
res, err := sess.Exec(sql,
|
||||
models.AlertNotificationStatePending,
|
||||
newVersion,
|
||||
timeNow().Unix(),
|
||||
cmd.AlertRuleStateUpdatedVersion,
|
||||
cmd.Id,
|
||||
cmd.Version,
|
||||
cmd.AlertRuleStateUpdatedVersion)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
affected, _ := res.RowsAffected()
|
||||
if affected == 0 {
|
||||
return models.ErrAlertNotificationStateVersionConflict
|
||||
}
|
||||
|
||||
cmd.ResultVersion = newVersion
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *SQLStore) GetOrCreateAlertNotificationState(ctx context.Context, cmd *models.GetOrCreateNotificationStateQuery) error {
|
||||
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||
nj := &models.AlertNotificationState{}
|
||||
|
||||
exist, err := getAlertNotificationState(ctx, sess, cmd, nj)
|
||||
|
||||
// if exists, return it, otherwise create it with default values
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if exist {
|
||||
cmd.Result = nj
|
||||
return nil
|
||||
}
|
||||
|
||||
notificationState := &models.AlertNotificationState{
|
||||
OrgId: cmd.OrgId,
|
||||
AlertId: cmd.AlertId,
|
||||
NotifierId: cmd.NotifierId,
|
||||
State: models.AlertNotificationStateUnknown,
|
||||
UpdatedAt: timeNow().Unix(),
|
||||
}
|
||||
|
||||
if _, err := sess.Insert(notificationState); err != nil {
|
||||
if dialect.IsUniqueConstraintViolation(err) {
|
||||
exist, err = getAlertNotificationState(ctx, sess, cmd, nj)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exist {
|
||||
return errors.New("should not happen")
|
||||
}
|
||||
|
||||
cmd.Result = nj
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Result = notificationState
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func getAlertNotificationState(ctx context.Context, sess *DBSession, cmd *models.GetOrCreateNotificationStateQuery, nj *models.AlertNotificationState) (bool, error) {
|
||||
return sess.
|
||||
Where("alert_notification_state.org_id = ?", cmd.OrgId).
|
||||
Where("alert_notification_state.alert_id = ?", cmd.AlertId).
|
||||
Where("alert_notification_state.notifier_id = ?", cmd.NotifierId).
|
||||
Get(nj)
|
||||
}
|
||||
@@ -1,492 +0,0 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
var sqlStore *SQLStore
|
||||
setup := func() { sqlStore = InitTestDB(t) }
|
||||
|
||||
t.Run("Alert notification state", func(t *testing.T) {
|
||||
setup()
|
||||
var alertID int64 = 7
|
||||
var orgID int64 = 5
|
||||
var notifierID int64 = 10
|
||||
oldTimeNow := timeNow
|
||||
now := time.Date(2018, 9, 30, 0, 0, 0, 0, time.UTC)
|
||||
timeNow = func() time.Time { return now }
|
||||
|
||||
defer func() { timeNow = oldTimeNow }()
|
||||
|
||||
t.Run("Get no existing state should create a new state", func(t *testing.T) {
|
||||
query := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID}
|
||||
err := sqlStore.GetOrCreateAlertNotificationState(context.Background(), query)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, query.Result)
|
||||
require.Equal(t, models.AlertNotificationStateUnknown, query.Result.State)
|
||||
require.Equal(t, int64(0), query.Result.Version)
|
||||
require.Equal(t, now.Unix(), query.Result.UpdatedAt)
|
||||
|
||||
t.Run("Get existing state should not create a new state", func(t *testing.T) {
|
||||
query2 := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID}
|
||||
err := sqlStore.GetOrCreateAlertNotificationState(context.Background(), query2)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, query2.Result)
|
||||
require.Equal(t, query.Result.Id, query2.Result.Id)
|
||||
require.Equal(t, now.Unix(), query2.Result.UpdatedAt)
|
||||
})
|
||||
|
||||
t.Run("Update existing state to pending with correct version should update database", func(t *testing.T) {
|
||||
s := *query.Result
|
||||
|
||||
cmd := models.SetAlertNotificationStateToPendingCommand{
|
||||
Id: s.Id,
|
||||
Version: s.Version,
|
||||
AlertRuleStateUpdatedVersion: s.AlertRuleStateUpdatedVersion,
|
||||
}
|
||||
|
||||
err := sqlStore.SetAlertNotificationStateToPendingCommand(context.Background(), &cmd)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, int64(1), cmd.ResultVersion)
|
||||
|
||||
query2 := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID}
|
||||
err = sqlStore.GetOrCreateAlertNotificationState(context.Background(), query2)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, int64(1), query2.Result.Version)
|
||||
require.Equal(t, models.AlertNotificationStatePending, query2.Result.State)
|
||||
require.Equal(t, now.Unix(), query2.Result.UpdatedAt)
|
||||
|
||||
t.Run("Update existing state to completed should update database", func(t *testing.T) {
|
||||
s := *query.Result
|
||||
setStateCmd := models.SetAlertNotificationStateToCompleteCommand{
|
||||
Id: s.Id,
|
||||
Version: cmd.ResultVersion,
|
||||
}
|
||||
err := sqlStore.SetAlertNotificationStateToCompleteCommand(context.Background(), &setStateCmd)
|
||||
require.Nil(t, err)
|
||||
|
||||
query3 := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID}
|
||||
err = sqlStore.GetOrCreateAlertNotificationState(context.Background(), query3)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, int64(2), query3.Result.Version)
|
||||
require.Equal(t, models.AlertNotificationStateCompleted, query3.Result.State)
|
||||
require.Equal(t, now.Unix(), query3.Result.UpdatedAt)
|
||||
})
|
||||
|
||||
t.Run("Update existing state to completed should update database. regardless of version", func(t *testing.T) {
|
||||
s := *query.Result
|
||||
unknownVersion := int64(1000)
|
||||
cmd := models.SetAlertNotificationStateToCompleteCommand{
|
||||
Id: s.Id,
|
||||
Version: unknownVersion,
|
||||
}
|
||||
err := sqlStore.SetAlertNotificationStateToCompleteCommand(context.Background(), &cmd)
|
||||
require.Nil(t, err)
|
||||
|
||||
query3 := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID}
|
||||
err = sqlStore.GetOrCreateAlertNotificationState(context.Background(), query3)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, unknownVersion+1, query3.Result.Version)
|
||||
require.Equal(t, models.AlertNotificationStateCompleted, query3.Result.State)
|
||||
require.Equal(t, now.Unix(), query3.Result.UpdatedAt)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Update existing state to pending with incorrect version should return version mismatch error", func(t *testing.T) {
|
||||
s := *query.Result
|
||||
s.Version = 1000
|
||||
cmd := models.SetAlertNotificationStateToPendingCommand{
|
||||
Id: s.NotifierId,
|
||||
Version: s.Version,
|
||||
AlertRuleStateUpdatedVersion: s.AlertRuleStateUpdatedVersion,
|
||||
}
|
||||
err := sqlStore.SetAlertNotificationStateToPendingCommand(context.Background(), &cmd)
|
||||
require.Equal(t, models.ErrAlertNotificationStateVersionConflict, err)
|
||||
})
|
||||
|
||||
t.Run("Updating existing state to pending with incorrect version since alert rule state update version is higher", func(t *testing.T) {
|
||||
s := *query.Result
|
||||
cmd := models.SetAlertNotificationStateToPendingCommand{
|
||||
Id: s.Id,
|
||||
Version: s.Version,
|
||||
AlertRuleStateUpdatedVersion: 1000,
|
||||
}
|
||||
err := sqlStore.SetAlertNotificationStateToPendingCommand(context.Background(), &cmd)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, int64(1), cmd.ResultVersion)
|
||||
})
|
||||
|
||||
t.Run("different version and same alert state change version should return error", func(t *testing.T) {
|
||||
s := *query.Result
|
||||
s.Version = 1000
|
||||
cmd := models.SetAlertNotificationStateToPendingCommand{
|
||||
Id: s.Id,
|
||||
Version: s.Version,
|
||||
AlertRuleStateUpdatedVersion: s.AlertRuleStateUpdatedVersion,
|
||||
}
|
||||
err := sqlStore.SetAlertNotificationStateToPendingCommand(context.Background(), &cmd)
|
||||
require.Error(t, err)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Alert notifications should be empty", func(t *testing.T) {
|
||||
setup()
|
||||
cmd := &models.GetAlertNotificationsQuery{
|
||||
OrgId: 2,
|
||||
Name: "email",
|
||||
}
|
||||
|
||||
err := sqlStore.GetAlertNotifications(context.Background(), cmd)
|
||||
require.Nil(t, err)
|
||||
require.Nil(t, cmd.Result)
|
||||
})
|
||||
|
||||
t.Run("Cannot save alert notifier with send reminder = true", func(t *testing.T) {
|
||||
setup()
|
||||
cmd := &models.CreateAlertNotificationCommand{
|
||||
Name: "ops",
|
||||
Type: "email",
|
||||
OrgId: 1,
|
||||
SendReminder: true,
|
||||
Settings: simplejson.New(),
|
||||
}
|
||||
|
||||
t.Run("and missing frequency", func(t *testing.T) {
|
||||
err := sqlStore.CreateAlertNotificationCommand(context.Background(), cmd)
|
||||
require.Equal(t, models.ErrNotificationFrequencyNotFound, err)
|
||||
})
|
||||
|
||||
t.Run("invalid frequency", func(t *testing.T) {
|
||||
cmd.Frequency = "invalid duration"
|
||||
err := sqlStore.CreateAlertNotificationCommand(context.Background(), cmd)
|
||||
require.True(t, regexp.MustCompile(`^time: invalid duration "?invalid duration"?$`).MatchString(
|
||||
err.Error()))
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Cannot update alert notifier with send reminder = false", func(t *testing.T) {
|
||||
setup()
|
||||
cmd := &models.CreateAlertNotificationCommand{
|
||||
Name: "ops update",
|
||||
Type: "email",
|
||||
OrgId: 1,
|
||||
SendReminder: false,
|
||||
Settings: simplejson.New(),
|
||||
}
|
||||
|
||||
err := sqlStore.CreateAlertNotificationCommand(context.Background(), cmd)
|
||||
require.Nil(t, err)
|
||||
|
||||
updateCmd := &models.UpdateAlertNotificationCommand{
|
||||
Id: cmd.Result.Id,
|
||||
SendReminder: true,
|
||||
}
|
||||
|
||||
t.Run("and missing frequency", func(t *testing.T) {
|
||||
err := sqlStore.UpdateAlertNotification(context.Background(), updateCmd)
|
||||
require.Equal(t, models.ErrNotificationFrequencyNotFound, err)
|
||||
})
|
||||
|
||||
t.Run("invalid frequency", func(t *testing.T) {
|
||||
updateCmd.Frequency = "invalid duration"
|
||||
|
||||
err := sqlStore.UpdateAlertNotification(context.Background(), updateCmd)
|
||||
require.Error(t, err)
|
||||
require.True(t, regexp.MustCompile(`^time: invalid duration "?invalid duration"?$`).MatchString(
|
||||
err.Error()))
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Can save Alert Notification", func(t *testing.T) {
|
||||
setup()
|
||||
cmd := &models.CreateAlertNotificationCommand{
|
||||
Name: "ops",
|
||||
Type: "email",
|
||||
OrgId: 1,
|
||||
SendReminder: true,
|
||||
Frequency: "10s",
|
||||
Settings: simplejson.New(),
|
||||
}
|
||||
|
||||
err := sqlStore.CreateAlertNotificationCommand(context.Background(), cmd)
|
||||
require.Nil(t, err)
|
||||
require.NotEqual(t, 0, cmd.Result.Id)
|
||||
require.NotEqual(t, 0, cmd.Result.OrgId)
|
||||
require.Equal(t, "email", cmd.Result.Type)
|
||||
require.Equal(t, 10*time.Second, cmd.Result.Frequency)
|
||||
require.False(t, cmd.Result.DisableResolveMessage)
|
||||
require.NotEmpty(t, cmd.Result.Uid)
|
||||
|
||||
t.Run("Cannot save Alert Notification with the same name", func(t *testing.T) {
|
||||
err = sqlStore.CreateAlertNotificationCommand(context.Background(), cmd)
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("Cannot save Alert Notification with the same name and another uid", func(t *testing.T) {
|
||||
anotherUidCmd := &models.CreateAlertNotificationCommand{
|
||||
Name: cmd.Name,
|
||||
Type: cmd.Type,
|
||||
OrgId: 1,
|
||||
SendReminder: cmd.SendReminder,
|
||||
Frequency: cmd.Frequency,
|
||||
Settings: cmd.Settings,
|
||||
Uid: "notifier1",
|
||||
}
|
||||
err = sqlStore.CreateAlertNotificationCommand(context.Background(), anotherUidCmd)
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("Can save Alert Notification with another name and another uid", func(t *testing.T) {
|
||||
anotherUidCmd := &models.CreateAlertNotificationCommand{
|
||||
Name: "another ops",
|
||||
Type: cmd.Type,
|
||||
OrgId: 1,
|
||||
SendReminder: cmd.SendReminder,
|
||||
Frequency: cmd.Frequency,
|
||||
Settings: cmd.Settings,
|
||||
Uid: "notifier2",
|
||||
}
|
||||
err = sqlStore.CreateAlertNotificationCommand(context.Background(), anotherUidCmd)
|
||||
require.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("Can update alert notification", func(t *testing.T) {
|
||||
newCmd := &models.UpdateAlertNotificationCommand{
|
||||
Name: "NewName",
|
||||
Type: "webhook",
|
||||
OrgId: cmd.Result.OrgId,
|
||||
SendReminder: true,
|
||||
DisableResolveMessage: true,
|
||||
Frequency: "60s",
|
||||
Settings: simplejson.New(),
|
||||
Id: cmd.Result.Id,
|
||||
}
|
||||
err := sqlStore.UpdateAlertNotification(context.Background(), newCmd)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "NewName", newCmd.Result.Name)
|
||||
require.Equal(t, 60*time.Second, newCmd.Result.Frequency)
|
||||
require.True(t, newCmd.Result.DisableResolveMessage)
|
||||
})
|
||||
|
||||
t.Run("Can update alert notification to disable sending of reminders", func(t *testing.T) {
|
||||
newCmd := &models.UpdateAlertNotificationCommand{
|
||||
Name: "NewName",
|
||||
Type: "webhook",
|
||||
OrgId: cmd.Result.OrgId,
|
||||
SendReminder: false,
|
||||
Settings: simplejson.New(),
|
||||
Id: cmd.Result.Id,
|
||||
}
|
||||
err := sqlStore.UpdateAlertNotification(context.Background(), newCmd)
|
||||
require.Nil(t, err)
|
||||
require.False(t, newCmd.Result.SendReminder)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Can search using an array of ids", func(t *testing.T) {
|
||||
setup()
|
||||
cmd1 := models.CreateAlertNotificationCommand{Name: "nagios", Type: "webhook", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()}
|
||||
cmd2 := models.CreateAlertNotificationCommand{Name: "slack", Type: "webhook", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()}
|
||||
cmd3 := models.CreateAlertNotificationCommand{Name: "ops2", Type: "email", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()}
|
||||
cmd4 := models.CreateAlertNotificationCommand{IsDefault: true, Name: "default", Type: "email", OrgId: 1, SendReminder: true, Frequency: "10s", Settings: simplejson.New()}
|
||||
|
||||
otherOrg := models.CreateAlertNotificationCommand{Name: "default", Type: "email", OrgId: 2, SendReminder: true, Frequency: "10s", Settings: simplejson.New()}
|
||||
|
||||
require.Nil(t, sqlStore.CreateAlertNotificationCommand(context.Background(), &cmd1))
|
||||
require.Nil(t, sqlStore.CreateAlertNotificationCommand(context.Background(), &cmd2))
|
||||
require.Nil(t, sqlStore.CreateAlertNotificationCommand(context.Background(), &cmd3))
|
||||
require.Nil(t, sqlStore.CreateAlertNotificationCommand(context.Background(), &cmd4))
|
||||
require.Nil(t, sqlStore.CreateAlertNotificationCommand(context.Background(), &otherOrg))
|
||||
|
||||
t.Run("search", func(t *testing.T) {
|
||||
query := &models.GetAlertNotificationsWithUidToSendQuery{
|
||||
Uids: []string{cmd1.Result.Uid, cmd2.Result.Uid, "112341231"},
|
||||
OrgId: 1,
|
||||
}
|
||||
|
||||
err := sqlStore.GetAlertNotificationsWithUidToSend(context.Background(), query)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 3, len(query.Result))
|
||||
})
|
||||
|
||||
t.Run("all", func(t *testing.T) {
|
||||
query := &models.GetAllAlertNotificationsQuery{
|
||||
OrgId: 1,
|
||||
}
|
||||
|
||||
err := sqlStore.GetAllAlertNotifications(context.Background(), query)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 4, len(query.Result))
|
||||
require.Equal(t, cmd4.Name, query.Result[0].Name)
|
||||
require.Equal(t, cmd1.Name, query.Result[1].Name)
|
||||
require.Equal(t, cmd3.Name, query.Result[2].Name)
|
||||
require.Equal(t, cmd2.Name, query.Result[3].Name)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Notification Uid by Id Caching", func(t *testing.T) {
|
||||
setup()
|
||||
ss := InitTestDB(t)
|
||||
|
||||
notification := &models.CreateAlertNotificationCommand{Uid: "aNotificationUid", OrgId: 1, Name: "aNotificationUid"}
|
||||
err := sqlStore.CreateAlertNotificationCommand(context.Background(), notification)
|
||||
require.Nil(t, err)
|
||||
|
||||
byUidQuery := &models.GetAlertNotificationsWithUidQuery{
|
||||
Uid: notification.Uid,
|
||||
OrgId: notification.OrgId,
|
||||
}
|
||||
|
||||
notificationByUidErr := sqlStore.GetAlertNotificationsWithUid(context.Background(), byUidQuery)
|
||||
require.Nil(t, notificationByUidErr)
|
||||
|
||||
t.Run("Can cache notification Uid", func(t *testing.T) {
|
||||
byIdQuery := &models.GetAlertNotificationUidQuery{
|
||||
Id: byUidQuery.Result.Id,
|
||||
OrgId: byUidQuery.Result.OrgId,
|
||||
}
|
||||
|
||||
cacheKey := newAlertNotificationUidCacheKey(byIdQuery.OrgId, byIdQuery.Id)
|
||||
|
||||
resultBeforeCaching, foundBeforeCaching := ss.CacheService.Get(cacheKey)
|
||||
require.False(t, foundBeforeCaching)
|
||||
require.Nil(t, resultBeforeCaching)
|
||||
|
||||
notificationByIdErr := ss.GetAlertNotificationUidWithId(context.Background(), byIdQuery)
|
||||
require.Nil(t, notificationByIdErr)
|
||||
|
||||
resultAfterCaching, foundAfterCaching := ss.CacheService.Get(cacheKey)
|
||||
require.True(t, foundAfterCaching)
|
||||
require.Equal(t, notification.Uid, resultAfterCaching)
|
||||
})
|
||||
|
||||
t.Run("Retrieves from cache when exists", func(t *testing.T) {
|
||||
query := &models.GetAlertNotificationUidQuery{
|
||||
Id: 999,
|
||||
OrgId: 100,
|
||||
}
|
||||
cacheKey := newAlertNotificationUidCacheKey(query.OrgId, query.Id)
|
||||
ss.CacheService.Set(cacheKey, "a-cached-uid", -1)
|
||||
|
||||
err := ss.GetAlertNotificationUidWithId(context.Background(), query)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "a-cached-uid", query.Result)
|
||||
})
|
||||
|
||||
t.Run("Returns an error without populating cache when the notification doesn't exist in the database", func(t *testing.T) {
|
||||
query := &models.GetAlertNotificationUidQuery{
|
||||
Id: -1,
|
||||
OrgId: 100,
|
||||
}
|
||||
|
||||
err := ss.GetAlertNotificationUidWithId(context.Background(), query)
|
||||
require.Equal(t, "", query.Result)
|
||||
require.Error(t, err)
|
||||
require.True(t, errors.Is(err, models.ErrAlertNotificationFailedTranslateUniqueID))
|
||||
|
||||
cacheKey := newAlertNotificationUidCacheKey(query.OrgId, query.Id)
|
||||
result, found := ss.CacheService.Get(cacheKey)
|
||||
require.False(t, found)
|
||||
require.Nil(t, result)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Cannot update non-existing Alert Notification", func(t *testing.T) {
|
||||
setup()
|
||||
updateCmd := &models.UpdateAlertNotificationCommand{
|
||||
Name: "NewName",
|
||||
Type: "webhook",
|
||||
OrgId: 1,
|
||||
SendReminder: true,
|
||||
DisableResolveMessage: true,
|
||||
Frequency: "60s",
|
||||
Settings: simplejson.New(),
|
||||
Id: 1,
|
||||
}
|
||||
err := sqlStore.UpdateAlertNotification(context.Background(), updateCmd)
|
||||
require.Equal(t, models.ErrAlertNotificationNotFound, err)
|
||||
|
||||
t.Run("using UID", func(t *testing.T) {
|
||||
updateWithUidCmd := &models.UpdateAlertNotificationWithUidCommand{
|
||||
Name: "NewName",
|
||||
Type: "webhook",
|
||||
OrgId: 1,
|
||||
SendReminder: true,
|
||||
DisableResolveMessage: true,
|
||||
Frequency: "60s",
|
||||
Settings: simplejson.New(),
|
||||
Uid: "uid",
|
||||
NewUid: "newUid",
|
||||
}
|
||||
err := sqlStore.UpdateAlertNotificationWithUid(context.Background(), updateWithUidCmd)
|
||||
require.Equal(t, models.ErrAlertNotificationNotFound, err)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Can delete Alert Notification", func(t *testing.T) {
|
||||
setup()
|
||||
cmd := &models.CreateAlertNotificationCommand{
|
||||
Name: "ops update",
|
||||
Type: "email",
|
||||
OrgId: 1,
|
||||
SendReminder: false,
|
||||
Settings: simplejson.New(),
|
||||
}
|
||||
|
||||
err := sqlStore.CreateAlertNotificationCommand(context.Background(), cmd)
|
||||
require.Nil(t, err)
|
||||
|
||||
deleteCmd := &models.DeleteAlertNotificationCommand{
|
||||
Id: cmd.Result.Id,
|
||||
OrgId: 1,
|
||||
}
|
||||
err = sqlStore.DeleteAlertNotification(context.Background(), deleteCmd)
|
||||
require.Nil(t, err)
|
||||
|
||||
t.Run("using UID", func(t *testing.T) {
|
||||
err := sqlStore.CreateAlertNotificationCommand(context.Background(), cmd)
|
||||
require.Nil(t, err)
|
||||
|
||||
deleteWithUidCmd := &models.DeleteAlertNotificationWithUidCommand{
|
||||
Uid: cmd.Result.Uid,
|
||||
OrgId: 1,
|
||||
}
|
||||
|
||||
err = sqlStore.DeleteAlertNotificationWithUid(context.Background(), deleteWithUidCmd)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, cmd.Result.Id, deleteWithUidCmd.DeletedAlertNotificationId)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Cannot delete non-existing Alert Notification", func(t *testing.T) {
|
||||
setup()
|
||||
deleteCmd := &models.DeleteAlertNotificationCommand{
|
||||
Id: 1,
|
||||
OrgId: 1,
|
||||
}
|
||||
err := sqlStore.DeleteAlertNotification(context.Background(), deleteCmd)
|
||||
require.Equal(t, models.ErrAlertNotificationNotFound, err)
|
||||
|
||||
t.Run("using UID", func(t *testing.T) {
|
||||
deleteWithUidCmd := &models.DeleteAlertNotificationWithUidCommand{
|
||||
Uid: "uid",
|
||||
OrgId: 1,
|
||||
}
|
||||
err = sqlStore.DeleteAlertNotificationWithUid(context.Background(), deleteWithUidCmd)
|
||||
require.Equal(t, models.ErrAlertNotificationNotFound, err)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -1,359 +0,0 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func mockTimeNow() {
|
||||
var timeSeed int64
|
||||
timeNow = func() time.Time {
|
||||
loc := time.FixedZone("MockZoneUTC-5", -5*60*60)
|
||||
fakeNow := time.Unix(timeSeed, 0).In(loc)
|
||||
timeSeed++
|
||||
return fakeNow
|
||||
}
|
||||
}
|
||||
|
||||
func resetTimeNow() {
|
||||
timeNow = time.Now
|
||||
}
|
||||
|
||||
func TestIntegrationAlertingDataAccess(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
mockTimeNow()
|
||||
defer resetTimeNow()
|
||||
|
||||
var sqlStore *SQLStore
|
||||
var testDash *models.Dashboard
|
||||
var items []*models.Alert
|
||||
|
||||
setup := func(t *testing.T) {
|
||||
sqlStore = InitTestDB(t)
|
||||
|
||||
testDash = insertTestDashboard(t, sqlStore, "dashboard with alerts", 1, 0, false, "alert")
|
||||
evalData, err := simplejson.NewJson([]byte(`{"test": "test"}`))
|
||||
require.Nil(t, err)
|
||||
items = []*models.Alert{
|
||||
{
|
||||
PanelId: 1,
|
||||
DashboardId: testDash.Id,
|
||||
OrgId: testDash.OrgId,
|
||||
Name: "Alerting title",
|
||||
Message: "Alerting message",
|
||||
Settings: simplejson.New(),
|
||||
Frequency: 1,
|
||||
EvalData: evalData,
|
||||
},
|
||||
}
|
||||
|
||||
err = sqlStore.SaveAlerts(context.Background(), testDash.Id, items)
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
||||
t.Run("Can set new states", func(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
// Get alert so we can use its ID in tests
|
||||
alertQuery := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, PanelId: 1, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
|
||||
err2 := sqlStore.HandleAlertsQuery(context.Background(), &alertQuery)
|
||||
require.Nil(t, err2)
|
||||
|
||||
insertedAlert := alertQuery.Result[0]
|
||||
|
||||
t.Run("new state ok", func(t *testing.T) {
|
||||
cmd := &models.SetAlertStateCommand{
|
||||
AlertId: insertedAlert.Id,
|
||||
State: models.AlertStateOK,
|
||||
}
|
||||
|
||||
err := sqlStore.SetAlertState(context.Background(), cmd)
|
||||
require.Nil(t, err)
|
||||
})
|
||||
|
||||
alert, _ := getAlertById(t, insertedAlert.Id, sqlStore)
|
||||
stateDateBeforePause := alert.NewStateDate
|
||||
|
||||
t.Run("can pause all alerts", func(t *testing.T) {
|
||||
err := sqlStore.pauseAllAlerts(t, true)
|
||||
require.Nil(t, err)
|
||||
|
||||
t.Run("cannot updated paused alert", func(t *testing.T) {
|
||||
cmd := &models.SetAlertStateCommand{
|
||||
AlertId: insertedAlert.Id,
|
||||
State: models.AlertStateOK,
|
||||
}
|
||||
|
||||
err = sqlStore.SetAlertState(context.Background(), cmd)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("alert is paused", func(t *testing.T) {
|
||||
alert, _ = getAlertById(t, insertedAlert.Id, sqlStore)
|
||||
currentState := alert.State
|
||||
require.Equal(t, models.AlertStatePaused, currentState)
|
||||
})
|
||||
|
||||
t.Run("pausing alerts should update their NewStateDate", func(t *testing.T) {
|
||||
alert, _ = getAlertById(t, insertedAlert.Id, sqlStore)
|
||||
stateDateAfterPause := alert.NewStateDate
|
||||
require.True(t, stateDateBeforePause.Before(stateDateAfterPause))
|
||||
})
|
||||
|
||||
t.Run("unpausing alerts should update their NewStateDate again", func(t *testing.T) {
|
||||
err := sqlStore.pauseAllAlerts(t, false)
|
||||
require.Nil(t, err)
|
||||
alert, _ = getAlertById(t, insertedAlert.Id, sqlStore)
|
||||
stateDateAfterUnpause := alert.NewStateDate
|
||||
require.True(t, stateDateBeforePause.Before(stateDateAfterUnpause))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Can read properties", func(t *testing.T) {
|
||||
setup(t)
|
||||
alertQuery := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, PanelId: 1, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
|
||||
err2 := sqlStore.HandleAlertsQuery(context.Background(), &alertQuery)
|
||||
|
||||
alert := alertQuery.Result[0]
|
||||
require.Nil(t, err2)
|
||||
require.Greater(t, alert.Id, int64(0))
|
||||
require.Equal(t, testDash.Id, alert.DashboardId)
|
||||
require.Equal(t, int64(1), alert.PanelId)
|
||||
require.Equal(t, "Alerting title", alert.Name)
|
||||
require.Equal(t, models.AlertStateUnknown, alert.State)
|
||||
require.NotNil(t, alert.NewStateDate)
|
||||
require.NotNil(t, alert.EvalData)
|
||||
require.Equal(t, "test", alert.EvalData.Get("test").MustString())
|
||||
require.NotNil(t, alert.EvalDate)
|
||||
require.Equal(t, "", alert.ExecutionError)
|
||||
require.NotNil(t, alert.DashboardUid)
|
||||
require.Equal(t, "dashboard-with-alerts", alert.DashboardSlug)
|
||||
})
|
||||
|
||||
t.Run("Viewer can read alerts", func(t *testing.T) {
|
||||
setup(t)
|
||||
viewerUser := &models.SignedInUser{OrgRole: models.ROLE_VIEWER, OrgId: 1}
|
||||
alertQuery := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, PanelId: 1, OrgId: 1, User: viewerUser}
|
||||
err2 := sqlStore.HandleAlertsQuery(context.Background(), &alertQuery)
|
||||
|
||||
require.Nil(t, err2)
|
||||
require.Equal(t, 1, len(alertQuery.Result))
|
||||
})
|
||||
|
||||
t.Run("Alerts with same dashboard id and panel id should update", func(t *testing.T) {
|
||||
setup(t)
|
||||
modifiedItems := items
|
||||
modifiedItems[0].Name = "Name"
|
||||
|
||||
err := sqlStore.SaveAlerts(context.Background(), testDash.Id, items)
|
||||
|
||||
t.Run("Can save alerts with same dashboard and panel id", func(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("Alerts should be updated", func(t *testing.T) {
|
||||
query := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
|
||||
err2 := sqlStore.HandleAlertsQuery(context.Background(), &query)
|
||||
|
||||
require.Nil(t, err2)
|
||||
require.Equal(t, 1, len(query.Result))
|
||||
require.Equal(t, "Name", query.Result[0].Name)
|
||||
|
||||
t.Run("Alert state should not be updated", func(t *testing.T) {
|
||||
require.Equal(t, models.AlertStateUnknown, query.Result[0].State)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Updates without changes should be ignored", func(t *testing.T) {
|
||||
err3 := sqlStore.SaveAlerts(context.Background(), testDash.Id, items)
|
||||
require.Nil(t, err3)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Multiple alerts per dashboard", func(t *testing.T) {
|
||||
setup(t)
|
||||
multipleItems := []*models.Alert{
|
||||
{
|
||||
DashboardId: testDash.Id,
|
||||
PanelId: 1,
|
||||
Name: "1",
|
||||
OrgId: 1,
|
||||
Settings: simplejson.New(),
|
||||
},
|
||||
{
|
||||
DashboardId: testDash.Id,
|
||||
PanelId: 2,
|
||||
Name: "2",
|
||||
OrgId: 1,
|
||||
Settings: simplejson.New(),
|
||||
},
|
||||
{
|
||||
DashboardId: testDash.Id,
|
||||
PanelId: 3,
|
||||
Name: "3",
|
||||
OrgId: 1,
|
||||
Settings: simplejson.New(),
|
||||
},
|
||||
}
|
||||
|
||||
err := sqlStore.SaveAlerts(context.Background(), testDash.Id, multipleItems)
|
||||
|
||||
t.Run("Should save 3 dashboards", func(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
|
||||
queryForDashboard := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
|
||||
err2 := sqlStore.HandleAlertsQuery(context.Background(), &queryForDashboard)
|
||||
|
||||
require.Nil(t, err2)
|
||||
require.Equal(t, 3, len(queryForDashboard.Result))
|
||||
})
|
||||
|
||||
t.Run("should updated two dashboards and delete one", func(t *testing.T) {
|
||||
missingOneAlert := multipleItems[:2]
|
||||
|
||||
err = sqlStore.SaveAlerts(context.Background(), testDash.Id, missingOneAlert)
|
||||
|
||||
t.Run("should delete the missing alert", func(t *testing.T) {
|
||||
query := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
|
||||
err2 := sqlStore.HandleAlertsQuery(context.Background(), &query)
|
||||
require.Nil(t, err2)
|
||||
require.Equal(t, 2, len(query.Result))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("When dashboard is removed", func(t *testing.T) {
|
||||
setup(t)
|
||||
items := []*models.Alert{
|
||||
{
|
||||
PanelId: 1,
|
||||
DashboardId: testDash.Id,
|
||||
Name: "Alerting title",
|
||||
Message: "Alerting message",
|
||||
},
|
||||
}
|
||||
|
||||
err := sqlStore.SaveAlerts(context.Background(), testDash.Id, items)
|
||||
require.Nil(t, err)
|
||||
|
||||
err = sqlStore.WithDbSession(context.Background(), func(sess *DBSession) error {
|
||||
dash := models.Dashboard{Id: testDash.Id, OrgId: 1}
|
||||
_, err := sess.Delete(dash)
|
||||
return err
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
t.Run("Alerts should be removed", func(t *testing.T) {
|
||||
query := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
|
||||
err2 := sqlStore.HandleAlertsQuery(context.Background(), &query)
|
||||
|
||||
require.Nil(t, err2)
|
||||
require.Equal(t, 0, len(query.Result))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntegrationPausingAlerts(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
mockTimeNow()
|
||||
defer resetTimeNow()
|
||||
|
||||
t.Run("Given an alert", func(t *testing.T) {
|
||||
sqlStore := InitTestDB(t)
|
||||
|
||||
testDash := insertTestDashboard(t, sqlStore, "dashboard with alerts", 1, 0, false, "alert")
|
||||
alert, err := insertTestAlert("Alerting title", "Alerting message", testDash.OrgId, testDash.Id, simplejson.New(), sqlStore)
|
||||
require.Nil(t, err)
|
||||
|
||||
stateDateBeforePause := alert.NewStateDate
|
||||
stateDateAfterPause := stateDateBeforePause
|
||||
|
||||
// Get alert so we can use its ID in tests
|
||||
alertQuery := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, PanelId: 1, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
|
||||
err2 := sqlStore.HandleAlertsQuery(context.Background(), &alertQuery)
|
||||
require.Nil(t, err2)
|
||||
|
||||
insertedAlert := alertQuery.Result[0]
|
||||
|
||||
t.Run("when paused", func(t *testing.T) {
|
||||
_, err := sqlStore.pauseAlert(t, testDash.OrgId, insertedAlert.Id, true)
|
||||
require.Nil(t, err)
|
||||
|
||||
t.Run("the NewStateDate should be updated", func(t *testing.T) {
|
||||
alert, err := getAlertById(t, insertedAlert.Id, sqlStore)
|
||||
require.Nil(t, err)
|
||||
|
||||
stateDateAfterPause = alert.NewStateDate
|
||||
require.True(t, stateDateBeforePause.Before(stateDateAfterPause))
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("when unpaused", func(t *testing.T) {
|
||||
_, err := sqlStore.pauseAlert(t, testDash.OrgId, insertedAlert.Id, false)
|
||||
require.Nil(t, err)
|
||||
|
||||
t.Run("the NewStateDate should be updated again", func(t *testing.T) {
|
||||
alert, err := getAlertById(t, insertedAlert.Id, sqlStore)
|
||||
require.Nil(t, err)
|
||||
|
||||
stateDateAfterUnpause := alert.NewStateDate
|
||||
require.True(t, stateDateAfterPause.Before(stateDateAfterUnpause))
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
func (ss *SQLStore) pauseAlert(t *testing.T, orgId int64, alertId int64, pauseState bool) (int64, error) {
|
||||
cmd := &models.PauseAlertCommand{
|
||||
OrgId: orgId,
|
||||
AlertIds: []int64{alertId},
|
||||
Paused: pauseState,
|
||||
}
|
||||
err := ss.PauseAlert(context.Background(), cmd)
|
||||
require.Nil(t, err)
|
||||
return cmd.ResultCount, err
|
||||
}
|
||||
func insertTestAlert(title string, message string, orgId int64, dashId int64, settings *simplejson.Json, ss *SQLStore) (*models.Alert, error) {
|
||||
items := []*models.Alert{
|
||||
{
|
||||
PanelId: 1,
|
||||
DashboardId: dashId,
|
||||
OrgId: orgId,
|
||||
Name: title,
|
||||
Message: message,
|
||||
Settings: settings,
|
||||
Frequency: 1,
|
||||
},
|
||||
}
|
||||
|
||||
err := ss.SaveAlerts(context.Background(), dashId, items)
|
||||
return items[0], err
|
||||
}
|
||||
|
||||
func getAlertById(t *testing.T, id int64, ss *SQLStore) (*models.Alert, error) {
|
||||
q := &models.GetAlertByIdQuery{
|
||||
Id: id,
|
||||
}
|
||||
err := ss.GetAlertById(context.Background(), q)
|
||||
require.Nil(t, err)
|
||||
return q.Result, err
|
||||
}
|
||||
|
||||
func (ss *SQLStore) pauseAllAlerts(t *testing.T, pauseState bool) error {
|
||||
cmd := &models.PauseAllAlertCommand{
|
||||
Paused: pauseState,
|
||||
}
|
||||
err := ss.PauseAllAlerts(context.Background(), cmd)
|
||||
require.Nil(t, err)
|
||||
return err
|
||||
}
|
||||
@@ -10,5 +10,6 @@ import (
|
||||
type DB interface {
|
||||
WithTransactionalDbSession(ctx context.Context, callback sqlstore.DBTransactionFunc) error
|
||||
WithDbSession(ctx context.Context, callback sqlstore.DBTransactionFunc) error
|
||||
NewSession(ctx context.Context) *sqlstore.DBSession
|
||||
GetDialect() migrator.Dialect
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package sqlstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
@@ -9,6 +10,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
var timeNow = time.Now
|
||||
|
||||
type Store interface {
|
||||
GetAdminStats(ctx context.Context, query *models.GetAdminStatsQuery) error
|
||||
GetAlertNotifiersUsageStats(ctx context.Context, query *models.GetAlertNotifierUsageStatsQuery) error
|
||||
@@ -84,13 +87,6 @@ type Store interface {
|
||||
SearchPlaylists(ctx context.Context, query *models.GetPlaylistsQuery) error
|
||||
// deprecated
|
||||
GetPlaylistItem(ctx context.Context, query *models.GetPlaylistItemsByUidQuery) error
|
||||
GetAlertById(ctx context.Context, query *models.GetAlertByIdQuery) error
|
||||
GetAllAlertQueryHandler(ctx context.Context, query *models.GetAllAlertsQuery) error
|
||||
HandleAlertsQuery(ctx context.Context, query *models.GetAlertsQuery) error
|
||||
SetAlertState(ctx context.Context, cmd *models.SetAlertStateCommand) error
|
||||
PauseAlert(ctx context.Context, cmd *models.PauseAlertCommand) error
|
||||
PauseAllAlerts(ctx context.Context, cmd *models.PauseAllAlertCommand) error
|
||||
GetAlertStatesForDashboard(ctx context.Context, query *models.GetAlertStatesForDashboardQuery) error
|
||||
AddOrgUser(ctx context.Context, cmd *models.AddOrgUserCommand) error
|
||||
UpdateOrgUser(ctx context.Context, cmd *models.UpdateOrgUserCommand) error
|
||||
GetOrgUsers(ctx context.Context, query *models.GetOrgUsersQuery) error
|
||||
@@ -107,19 +103,6 @@ type Store interface {
|
||||
Sync() error
|
||||
Reset() error
|
||||
Quote(value string) string
|
||||
DeleteAlertNotification(ctx context.Context, cmd *models.DeleteAlertNotificationCommand) error
|
||||
DeleteAlertNotificationWithUid(ctx context.Context, cmd *models.DeleteAlertNotificationWithUidCommand) error
|
||||
GetAlertNotifications(ctx context.Context, query *models.GetAlertNotificationsQuery) error
|
||||
GetAlertNotificationUidWithId(ctx context.Context, query *models.GetAlertNotificationUidQuery) error
|
||||
GetAlertNotificationsWithUid(ctx context.Context, query *models.GetAlertNotificationsWithUidQuery) error
|
||||
GetAllAlertNotifications(ctx context.Context, query *models.GetAllAlertNotificationsQuery) error
|
||||
GetAlertNotificationsWithUidToSend(ctx context.Context, query *models.GetAlertNotificationsWithUidToSendQuery) error
|
||||
CreateAlertNotificationCommand(ctx context.Context, cmd *models.CreateAlertNotificationCommand) error
|
||||
UpdateAlertNotification(ctx context.Context, cmd *models.UpdateAlertNotificationCommand) error
|
||||
UpdateAlertNotificationWithUid(ctx context.Context, cmd *models.UpdateAlertNotificationWithUidCommand) error
|
||||
SetAlertNotificationStateToCompleteCommand(ctx context.Context, cmd *models.SetAlertNotificationStateToCompleteCommand) error
|
||||
SetAlertNotificationStateToPendingCommand(ctx context.Context, cmd *models.SetAlertNotificationStateToPendingCommand) error
|
||||
GetOrCreateAlertNotificationState(ctx context.Context, cmd *models.GetOrCreateNotificationStateQuery) error
|
||||
UpdateTempUserStatus(ctx context.Context, cmd *models.UpdateTempUserStatusCommand) error
|
||||
CreateTempUser(ctx context.Context, cmd *models.CreateTempUserCommand) error
|
||||
UpdateTempUserWithEmailSent(ctx context.Context, cmd *models.UpdateTempUserWithEmailSentCommand) error
|
||||
|
||||
Reference in New Issue
Block a user