alert_notification.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. package sqlstore
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "fmt"
  7. "strings"
  8. "time"
  9. "github.com/grafana/grafana/pkg/bus"
  10. m "github.com/grafana/grafana/pkg/models"
  11. "github.com/grafana/grafana/pkg/util"
  12. )
  13. func init() {
  14. bus.AddHandler("sql", GetAlertNotifications)
  15. bus.AddHandler("sql", CreateAlertNotificationCommand)
  16. bus.AddHandler("sql", UpdateAlertNotification)
  17. bus.AddHandler("sql", DeleteAlertNotification)
  18. bus.AddHandler("sql", GetAllAlertNotifications)
  19. bus.AddHandlerCtx("sql", GetOrCreateAlertNotificationState)
  20. bus.AddHandlerCtx("sql", SetAlertNotificationStateToCompleteCommand)
  21. bus.AddHandlerCtx("sql", SetAlertNotificationStateToPendingCommand)
  22. bus.AddHandler("sql", GetAlertNotificationsWithUid)
  23. bus.AddHandler("sql", UpdateAlertNotificationWithUid)
  24. bus.AddHandler("sql", DeleteAlertNotificationWithUid)
  25. bus.AddHandler("sql", GetAlertNotificationsWithUidToSend)
  26. }
  27. func DeleteAlertNotification(cmd *m.DeleteAlertNotificationCommand) error {
  28. return inTransaction(func(sess *DBSession) error {
  29. sql := "DELETE FROM alert_notification WHERE alert_notification.org_id = ? AND alert_notification.id = ?"
  30. if _, err := sess.Exec(sql, cmd.OrgId, cmd.Id); err != nil {
  31. return err
  32. }
  33. 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 {
  34. return err
  35. }
  36. return nil
  37. })
  38. }
  39. func DeleteAlertNotificationWithUid(cmd *m.DeleteAlertNotificationWithUidCommand) error {
  40. existingNotification := &m.GetAlertNotificationsWithUidQuery{OrgId: cmd.OrgId, Uid: cmd.Uid}
  41. if err := getAlertNotificationWithUidInternal(existingNotification, newSession()); err != nil {
  42. return err
  43. }
  44. if existingNotification.Result != nil {
  45. deleteCommand := &m.DeleteAlertNotificationCommand{
  46. Id: existingNotification.Result.Id,
  47. OrgId: existingNotification.Result.OrgId,
  48. }
  49. if err := bus.Dispatch(deleteCommand); err != nil {
  50. return err
  51. }
  52. }
  53. return nil
  54. }
  55. func GetAlertNotifications(query *m.GetAlertNotificationsQuery) error {
  56. return getAlertNotificationInternal(query, newSession())
  57. }
  58. func GetAlertNotificationsWithUid(query *m.GetAlertNotificationsWithUidQuery) error {
  59. return getAlertNotificationWithUidInternal(query, newSession())
  60. }
  61. func GetAllAlertNotifications(query *m.GetAllAlertNotificationsQuery) error {
  62. results := make([]*m.AlertNotification, 0)
  63. if err := x.Where("org_id = ?", query.OrgId).Find(&results); err != nil {
  64. return err
  65. }
  66. query.Result = results
  67. return nil
  68. }
  69. func GetAlertNotificationsWithUidToSend(query *m.GetAlertNotificationsWithUidToSendQuery) error {
  70. var sql bytes.Buffer
  71. params := make([]interface{}, 0)
  72. sql.WriteString(`SELECT
  73. alert_notification.id,
  74. alert_notification.uid,
  75. alert_notification.org_id,
  76. alert_notification.name,
  77. alert_notification.type,
  78. alert_notification.created,
  79. alert_notification.updated,
  80. alert_notification.settings,
  81. alert_notification.is_default,
  82. alert_notification.disable_resolve_message,
  83. alert_notification.send_reminder,
  84. alert_notification.frequency
  85. FROM alert_notification
  86. `)
  87. sql.WriteString(` WHERE alert_notification.org_id = ?`)
  88. params = append(params, query.OrgId)
  89. sql.WriteString(` AND ((alert_notification.is_default = ?)`)
  90. params = append(params, dialect.BooleanStr(true))
  91. if len(query.Uids) > 0 {
  92. sql.WriteString(` OR alert_notification.uid IN (?` + strings.Repeat(",?", len(query.Uids)-1) + ")")
  93. for _, v := range query.Uids {
  94. params = append(params, v)
  95. }
  96. }
  97. sql.WriteString(`)`)
  98. results := make([]*m.AlertNotification, 0)
  99. if err := x.SQL(sql.String(), params...).Find(&results); err != nil {
  100. return err
  101. }
  102. query.Result = results
  103. return nil
  104. }
  105. func getAlertNotificationInternal(query *m.GetAlertNotificationsQuery, sess *DBSession) error {
  106. var sql bytes.Buffer
  107. params := make([]interface{}, 0)
  108. sql.WriteString(`SELECT
  109. alert_notification.id,
  110. alert_notification.uid,
  111. alert_notification.org_id,
  112. alert_notification.name,
  113. alert_notification.type,
  114. alert_notification.created,
  115. alert_notification.updated,
  116. alert_notification.settings,
  117. alert_notification.is_default,
  118. alert_notification.disable_resolve_message,
  119. alert_notification.send_reminder,
  120. alert_notification.frequency
  121. FROM alert_notification
  122. `)
  123. sql.WriteString(` WHERE alert_notification.org_id = ?`)
  124. params = append(params, query.OrgId)
  125. if query.Name != "" || query.Id != 0 {
  126. if query.Name != "" {
  127. sql.WriteString(` AND alert_notification.name = ?`)
  128. params = append(params, query.Name)
  129. }
  130. if query.Id != 0 {
  131. sql.WriteString(` AND alert_notification.id = ?`)
  132. params = append(params, query.Id)
  133. }
  134. }
  135. results := make([]*m.AlertNotification, 0)
  136. if err := sess.SQL(sql.String(), params...).Find(&results); err != nil {
  137. return err
  138. }
  139. if len(results) == 0 {
  140. query.Result = nil
  141. } else {
  142. query.Result = results[0]
  143. }
  144. return nil
  145. }
  146. func getAlertNotificationWithUidInternal(query *m.GetAlertNotificationsWithUidQuery, sess *DBSession) error {
  147. var sql bytes.Buffer
  148. params := make([]interface{}, 0)
  149. sql.WriteString(`SELECT
  150. alert_notification.id,
  151. alert_notification.uid,
  152. alert_notification.org_id,
  153. alert_notification.name,
  154. alert_notification.type,
  155. alert_notification.created,
  156. alert_notification.updated,
  157. alert_notification.settings,
  158. alert_notification.is_default,
  159. alert_notification.disable_resolve_message,
  160. alert_notification.send_reminder,
  161. alert_notification.frequency
  162. FROM alert_notification
  163. `)
  164. sql.WriteString(` WHERE alert_notification.org_id = ? AND alert_notification.uid = ?`)
  165. params = append(params, query.OrgId, query.Uid)
  166. results := make([]*m.AlertNotification, 0)
  167. if err := sess.SQL(sql.String(), params...).Find(&results); err != nil {
  168. return err
  169. }
  170. if len(results) == 0 {
  171. query.Result = nil
  172. } else {
  173. query.Result = results[0]
  174. }
  175. return nil
  176. }
  177. func CreateAlertNotificationCommand(cmd *m.CreateAlertNotificationCommand) error {
  178. return inTransaction(func(sess *DBSession) error {
  179. if cmd.Uid == "" {
  180. if uid, uidGenerationErr := generateNewAlertNotificationUid(sess, cmd.OrgId); uidGenerationErr != nil {
  181. return uidGenerationErr
  182. } else {
  183. cmd.Uid = uid
  184. }
  185. }
  186. existingQuery := &m.GetAlertNotificationsWithUidQuery{OrgId: cmd.OrgId, Uid: cmd.Uid}
  187. err := getAlertNotificationWithUidInternal(existingQuery, sess)
  188. if err != nil {
  189. return err
  190. }
  191. if existingQuery.Result != nil {
  192. return fmt.Errorf("Alert notification uid %s already exists", cmd.Uid)
  193. }
  194. // check if name exists
  195. sameNameQuery := &m.GetAlertNotificationsQuery{OrgId: cmd.OrgId, Name: cmd.Name}
  196. if err := getAlertNotificationInternal(sameNameQuery, sess); err != nil {
  197. return err
  198. }
  199. if sameNameQuery.Result != nil {
  200. return fmt.Errorf("Alert notification name %s already exists", cmd.Name)
  201. }
  202. var frequency time.Duration
  203. if cmd.SendReminder {
  204. if cmd.Frequency == "" {
  205. return m.ErrNotificationFrequencyNotFound
  206. }
  207. frequency, err = time.ParseDuration(cmd.Frequency)
  208. if err != nil {
  209. return err
  210. }
  211. }
  212. alertNotification := &m.AlertNotification{
  213. Uid: cmd.Uid,
  214. OrgId: cmd.OrgId,
  215. Name: cmd.Name,
  216. Type: cmd.Type,
  217. Settings: cmd.Settings,
  218. SendReminder: cmd.SendReminder,
  219. DisableResolveMessage: cmd.DisableResolveMessage,
  220. Frequency: frequency,
  221. Created: time.Now(),
  222. Updated: time.Now(),
  223. IsDefault: cmd.IsDefault,
  224. }
  225. if _, err = sess.MustCols("send_reminder").Insert(alertNotification); err != nil {
  226. return err
  227. }
  228. cmd.Result = alertNotification
  229. return nil
  230. })
  231. }
  232. func generateNewAlertNotificationUid(sess *DBSession, orgId int64) (string, error) {
  233. for i := 0; i < 3; i++ {
  234. uid := util.GenerateShortUID()
  235. exists, err := sess.Where("org_id=? AND uid=?", orgId, uid).Get(&m.AlertNotification{})
  236. if err != nil {
  237. return "", err
  238. }
  239. if !exists {
  240. return uid, nil
  241. }
  242. }
  243. return "", m.ErrAlertNotificationFailedGenerateUniqueUid
  244. }
  245. func UpdateAlertNotification(cmd *m.UpdateAlertNotificationCommand) error {
  246. return inTransaction(func(sess *DBSession) (err error) {
  247. current := m.AlertNotification{}
  248. if _, err = sess.ID(cmd.Id).Get(&current); err != nil {
  249. return err
  250. }
  251. // check if name exists
  252. sameNameQuery := &m.GetAlertNotificationsQuery{OrgId: cmd.OrgId, Name: cmd.Name}
  253. if err := getAlertNotificationInternal(sameNameQuery, sess); err != nil {
  254. return err
  255. }
  256. if sameNameQuery.Result != nil && sameNameQuery.Result.Id != current.Id {
  257. return fmt.Errorf("Alert notification name %s already exists", cmd.Name)
  258. }
  259. current.Updated = time.Now()
  260. current.Settings = cmd.Settings
  261. current.Name = cmd.Name
  262. current.Type = cmd.Type
  263. current.IsDefault = cmd.IsDefault
  264. current.SendReminder = cmd.SendReminder
  265. current.DisableResolveMessage = cmd.DisableResolveMessage
  266. if current.SendReminder {
  267. if cmd.Frequency == "" {
  268. return m.ErrNotificationFrequencyNotFound
  269. }
  270. frequency, err := time.ParseDuration(cmd.Frequency)
  271. if err != nil {
  272. return err
  273. }
  274. current.Frequency = frequency
  275. }
  276. sess.UseBool("is_default", "send_reminder", "disable_resolve_message")
  277. if affected, err := sess.ID(cmd.Id).Update(current); err != nil {
  278. return err
  279. } else if affected == 0 {
  280. return fmt.Errorf("Could not update alert notification")
  281. }
  282. cmd.Result = &current
  283. return nil
  284. })
  285. }
  286. func UpdateAlertNotificationWithUid(cmd *m.UpdateAlertNotificationWithUidCommand) error {
  287. getAlertNotificationWithUidQuery := &m.GetAlertNotificationsWithUidQuery{OrgId: cmd.OrgId, Uid: cmd.Uid}
  288. if err := getAlertNotificationWithUidInternal(getAlertNotificationWithUidQuery, newSession()); err != nil {
  289. return err
  290. }
  291. current := getAlertNotificationWithUidQuery.Result
  292. if current == nil {
  293. return fmt.Errorf("Cannot update, alert notification uid %s doesn't exist", cmd.Uid)
  294. }
  295. updateNotification := &m.UpdateAlertNotificationCommand{
  296. Id: current.Id,
  297. Name: cmd.Name,
  298. Type: cmd.Type,
  299. SendReminder: cmd.SendReminder,
  300. DisableResolveMessage: cmd.DisableResolveMessage,
  301. Frequency: cmd.Frequency,
  302. IsDefault: cmd.IsDefault,
  303. Settings: cmd.Settings,
  304. OrgId: cmd.OrgId,
  305. }
  306. if err := bus.Dispatch(updateNotification); err != nil {
  307. return err
  308. }
  309. return nil
  310. }
  311. func SetAlertNotificationStateToCompleteCommand(ctx context.Context, cmd *m.SetAlertNotificationStateToCompleteCommand) error {
  312. return inTransactionCtx(ctx, func(sess *DBSession) error {
  313. version := cmd.Version
  314. var current m.AlertNotificationState
  315. sess.ID(cmd.Id).Get(&current)
  316. newVersion := cmd.Version + 1
  317. sql := `UPDATE alert_notification_state SET
  318. state = ?,
  319. version = ?,
  320. updated_at = ?
  321. WHERE
  322. id = ?`
  323. _, err := sess.Exec(sql, m.AlertNotificationStateCompleted, newVersion, timeNow().Unix(), cmd.Id)
  324. if err != nil {
  325. return err
  326. }
  327. if current.Version != version {
  328. 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)
  329. }
  330. return nil
  331. })
  332. }
  333. func SetAlertNotificationStateToPendingCommand(ctx context.Context, cmd *m.SetAlertNotificationStateToPendingCommand) error {
  334. return withDbSession(ctx, func(sess *DBSession) error {
  335. newVersion := cmd.Version + 1
  336. sql := `UPDATE alert_notification_state SET
  337. state = ?,
  338. version = ?,
  339. updated_at = ?,
  340. alert_rule_state_updated_version = ?
  341. WHERE
  342. id = ? AND
  343. (version = ? OR alert_rule_state_updated_version < ?)`
  344. res, err := sess.Exec(sql,
  345. m.AlertNotificationStatePending,
  346. newVersion,
  347. timeNow().Unix(),
  348. cmd.AlertRuleStateUpdatedVersion,
  349. cmd.Id,
  350. cmd.Version,
  351. cmd.AlertRuleStateUpdatedVersion)
  352. if err != nil {
  353. return err
  354. }
  355. affected, _ := res.RowsAffected()
  356. if affected == 0 {
  357. return m.ErrAlertNotificationStateVersionConflict
  358. }
  359. cmd.ResultVersion = newVersion
  360. return nil
  361. })
  362. }
  363. func GetOrCreateAlertNotificationState(ctx context.Context, cmd *m.GetOrCreateNotificationStateQuery) error {
  364. return inTransactionCtx(ctx, func(sess *DBSession) error {
  365. nj := &m.AlertNotificationState{}
  366. exist, err := getAlertNotificationState(sess, cmd, nj)
  367. // if exists, return it, otherwise create it with default values
  368. if err != nil {
  369. return err
  370. }
  371. if exist {
  372. cmd.Result = nj
  373. return nil
  374. }
  375. notificationState := &m.AlertNotificationState{
  376. OrgId: cmd.OrgId,
  377. AlertId: cmd.AlertId,
  378. NotifierId: cmd.NotifierId,
  379. State: m.AlertNotificationStateUnknown,
  380. UpdatedAt: timeNow().Unix(),
  381. }
  382. if _, err := sess.Insert(notificationState); err != nil {
  383. if dialect.IsUniqueConstraintViolation(err) {
  384. exist, err = getAlertNotificationState(sess, cmd, nj)
  385. if err != nil {
  386. return err
  387. }
  388. if !exist {
  389. return errors.New("Should not happen")
  390. }
  391. cmd.Result = nj
  392. return nil
  393. }
  394. return err
  395. }
  396. cmd.Result = notificationState
  397. return nil
  398. })
  399. }
  400. func getAlertNotificationState(sess *DBSession, cmd *m.GetOrCreateNotificationStateQuery, nj *m.AlertNotificationState) (bool, error) {
  401. return sess.
  402. Where("alert_notification_state.org_id = ?", cmd.OrgId).
  403. Where("alert_notification_state.alert_id = ?", cmd.AlertId).
  404. Where("alert_notification_state.notifier_id = ?", cmd.NotifierId).
  405. Get(nj)
  406. }