alert_notification.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  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.org_id,
  111. alert_notification.name,
  112. alert_notification.type,
  113. alert_notification.created,
  114. alert_notification.updated,
  115. alert_notification.settings,
  116. alert_notification.is_default,
  117. alert_notification.disable_resolve_message,
  118. alert_notification.send_reminder,
  119. alert_notification.frequency
  120. FROM alert_notification
  121. `)
  122. sql.WriteString(` WHERE alert_notification.org_id = ?`)
  123. params = append(params, query.OrgId)
  124. if query.Name != "" || query.Id != 0 {
  125. if query.Name != "" {
  126. sql.WriteString(` AND alert_notification.name = ?`)
  127. params = append(params, query.Name)
  128. }
  129. if query.Id != 0 {
  130. sql.WriteString(` AND alert_notification.id = ?`)
  131. params = append(params, query.Id)
  132. }
  133. }
  134. results := make([]*m.AlertNotification, 0)
  135. if err := sess.SQL(sql.String(), params...).Find(&results); err != nil {
  136. return err
  137. }
  138. if len(results) == 0 {
  139. query.Result = nil
  140. } else {
  141. query.Result = results[0]
  142. }
  143. return nil
  144. }
  145. func getAlertNotificationWithUidInternal(query *m.GetAlertNotificationsWithUidQuery, sess *DBSession) error {
  146. var sql bytes.Buffer
  147. params := make([]interface{}, 0)
  148. sql.WriteString(`SELECT
  149. alert_notification.id,
  150. alert_notification.uid,
  151. alert_notification.org_id,
  152. alert_notification.name,
  153. alert_notification.type,
  154. alert_notification.created,
  155. alert_notification.updated,
  156. alert_notification.settings,
  157. alert_notification.is_default,
  158. alert_notification.disable_resolve_message,
  159. alert_notification.send_reminder,
  160. alert_notification.frequency
  161. FROM alert_notification
  162. `)
  163. sql.WriteString(` WHERE alert_notification.org_id = ? AND alert_notification.uid = ?`)
  164. params = append(params, query.OrgId, query.Uid)
  165. results := make([]*m.AlertNotification, 0)
  166. if err := sess.SQL(sql.String(), params...).Find(&results); err != nil {
  167. return err
  168. }
  169. if len(results) == 0 {
  170. query.Result = nil
  171. } else {
  172. query.Result = results[0]
  173. }
  174. return nil
  175. }
  176. func CreateAlertNotificationCommand(cmd *m.CreateAlertNotificationCommand) error {
  177. return inTransaction(func(sess *DBSession) error {
  178. if cmd.Uid == "" {
  179. if uid, uidGenerationErr := generateNewAlertNotificationUid(sess, cmd.OrgId); uidGenerationErr != nil {
  180. return uidGenerationErr
  181. } else {
  182. cmd.Uid = uid
  183. }
  184. }
  185. existingQuery := &m.GetAlertNotificationsWithUidQuery{OrgId: cmd.OrgId, Uid: cmd.Uid}
  186. err := getAlertNotificationWithUidInternal(existingQuery, sess)
  187. if err != nil {
  188. return err
  189. }
  190. if existingQuery.Result != nil {
  191. return fmt.Errorf("Alert notification uid %s already exists", cmd.Uid)
  192. }
  193. // check if name exists
  194. sameNameQuery := &m.GetAlertNotificationsQuery{OrgId: cmd.OrgId, Name: cmd.Name}
  195. if err := getAlertNotificationInternal(sameNameQuery, sess); err != nil {
  196. return err
  197. }
  198. if sameNameQuery.Result != nil {
  199. return fmt.Errorf("Alert notification name %s already exists", cmd.Name)
  200. }
  201. var frequency time.Duration
  202. if cmd.SendReminder {
  203. if cmd.Frequency == "" {
  204. return m.ErrNotificationFrequencyNotFound
  205. }
  206. frequency, err = time.ParseDuration(cmd.Frequency)
  207. if err != nil {
  208. return err
  209. }
  210. }
  211. alertNotification := &m.AlertNotification{
  212. Uid: cmd.Uid,
  213. OrgId: cmd.OrgId,
  214. Name: cmd.Name,
  215. Type: cmd.Type,
  216. Settings: cmd.Settings,
  217. SendReminder: cmd.SendReminder,
  218. DisableResolveMessage: cmd.DisableResolveMessage,
  219. Frequency: frequency,
  220. Created: time.Now(),
  221. Updated: time.Now(),
  222. IsDefault: cmd.IsDefault,
  223. }
  224. if _, err = sess.MustCols("send_reminder").Insert(alertNotification); err != nil {
  225. return err
  226. }
  227. cmd.Result = alertNotification
  228. return nil
  229. })
  230. }
  231. func generateNewAlertNotificationUid(sess *DBSession, orgId int64) (string, error) {
  232. for i := 0; i < 3; i++ {
  233. uid := util.GenerateShortUID()
  234. exists, err := sess.Where("org_id=? AND uid=?", orgId, uid).Get(&m.AlertNotification{})
  235. if err != nil {
  236. return "", err
  237. }
  238. if !exists {
  239. return uid, nil
  240. }
  241. }
  242. return "", m.ErrAlertNotificationFailedGenerateUniqueUid
  243. }
  244. func UpdateAlertNotification(cmd *m.UpdateAlertNotificationCommand) error {
  245. return inTransaction(func(sess *DBSession) (err error) {
  246. current := m.AlertNotification{}
  247. if _, err = sess.ID(cmd.Id).Get(&current); err != nil {
  248. return err
  249. }
  250. // check if name exists
  251. sameNameQuery := &m.GetAlertNotificationsQuery{OrgId: cmd.OrgId, Name: cmd.Name}
  252. if err := getAlertNotificationInternal(sameNameQuery, sess); err != nil {
  253. return err
  254. }
  255. if sameNameQuery.Result != nil && sameNameQuery.Result.Id != current.Id {
  256. return fmt.Errorf("Alert notification name %s already exists", cmd.Name)
  257. }
  258. current.Updated = time.Now()
  259. current.Settings = cmd.Settings
  260. current.Name = cmd.Name
  261. current.Type = cmd.Type
  262. current.IsDefault = cmd.IsDefault
  263. current.SendReminder = cmd.SendReminder
  264. current.DisableResolveMessage = cmd.DisableResolveMessage
  265. if current.SendReminder {
  266. if cmd.Frequency == "" {
  267. return m.ErrNotificationFrequencyNotFound
  268. }
  269. frequency, err := time.ParseDuration(cmd.Frequency)
  270. if err != nil {
  271. return err
  272. }
  273. current.Frequency = frequency
  274. }
  275. sess.UseBool("is_default", "send_reminder", "disable_resolve_message")
  276. if affected, err := sess.ID(cmd.Id).Update(current); err != nil {
  277. return err
  278. } else if affected == 0 {
  279. return fmt.Errorf("Could not update alert notification")
  280. }
  281. cmd.Result = &current
  282. return nil
  283. })
  284. }
  285. func UpdateAlertNotificationWithUid(cmd *m.UpdateAlertNotificationWithUidCommand) error {
  286. getAlertNotificationWithUidQuery := &m.GetAlertNotificationsWithUidQuery{OrgId: cmd.OrgId, Uid: cmd.Uid}
  287. if err := getAlertNotificationWithUidInternal(getAlertNotificationWithUidQuery, newSession()); err != nil {
  288. return err
  289. }
  290. current := getAlertNotificationWithUidQuery.Result
  291. if current == nil {
  292. return fmt.Errorf("Cannot update, alert notification uid %s doesn't exist", cmd.Uid)
  293. }
  294. updateNotification := &m.UpdateAlertNotificationCommand{
  295. Id: current.Id,
  296. Name: cmd.Name,
  297. Type: cmd.Type,
  298. SendReminder: cmd.SendReminder,
  299. DisableResolveMessage: cmd.DisableResolveMessage,
  300. Frequency: cmd.Frequency,
  301. IsDefault: cmd.IsDefault,
  302. Settings: cmd.Settings,
  303. OrgId: cmd.OrgId,
  304. }
  305. if err := bus.Dispatch(updateNotification); err != nil {
  306. return err
  307. }
  308. return nil
  309. }
  310. func SetAlertNotificationStateToCompleteCommand(ctx context.Context, cmd *m.SetAlertNotificationStateToCompleteCommand) error {
  311. return inTransactionCtx(ctx, func(sess *DBSession) error {
  312. version := cmd.Version
  313. var current m.AlertNotificationState
  314. sess.ID(cmd.Id).Get(&current)
  315. newVersion := cmd.Version + 1
  316. sql := `UPDATE alert_notification_state SET
  317. state = ?,
  318. version = ?,
  319. updated_at = ?
  320. WHERE
  321. id = ?`
  322. _, err := sess.Exec(sql, m.AlertNotificationStateCompleted, newVersion, timeNow().Unix(), cmd.Id)
  323. if err != nil {
  324. return err
  325. }
  326. if current.Version != version {
  327. 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)
  328. }
  329. return nil
  330. })
  331. }
  332. func SetAlertNotificationStateToPendingCommand(ctx context.Context, cmd *m.SetAlertNotificationStateToPendingCommand) error {
  333. return withDbSession(ctx, func(sess *DBSession) error {
  334. newVersion := cmd.Version + 1
  335. sql := `UPDATE alert_notification_state SET
  336. state = ?,
  337. version = ?,
  338. updated_at = ?,
  339. alert_rule_state_updated_version = ?
  340. WHERE
  341. id = ? AND
  342. (version = ? OR alert_rule_state_updated_version < ?)`
  343. res, err := sess.Exec(sql,
  344. m.AlertNotificationStatePending,
  345. newVersion,
  346. timeNow().Unix(),
  347. cmd.AlertRuleStateUpdatedVersion,
  348. cmd.Id,
  349. cmd.Version,
  350. cmd.AlertRuleStateUpdatedVersion)
  351. if err != nil {
  352. return err
  353. }
  354. affected, _ := res.RowsAffected()
  355. if affected == 0 {
  356. return m.ErrAlertNotificationStateVersionConflict
  357. }
  358. cmd.ResultVersion = newVersion
  359. return nil
  360. })
  361. }
  362. func GetOrCreateAlertNotificationState(ctx context.Context, cmd *m.GetOrCreateNotificationStateQuery) error {
  363. return inTransactionCtx(ctx, func(sess *DBSession) error {
  364. nj := &m.AlertNotificationState{}
  365. exist, err := getAlertNotificationState(sess, cmd, nj)
  366. // if exists, return it, otherwise create it with default values
  367. if err != nil {
  368. return err
  369. }
  370. if exist {
  371. cmd.Result = nj
  372. return nil
  373. }
  374. notificationState := &m.AlertNotificationState{
  375. OrgId: cmd.OrgId,
  376. AlertId: cmd.AlertId,
  377. NotifierId: cmd.NotifierId,
  378. State: m.AlertNotificationStateUnknown,
  379. UpdatedAt: timeNow().Unix(),
  380. }
  381. if _, err := sess.Insert(notificationState); err != nil {
  382. if dialect.IsUniqueConstraintViolation(err) {
  383. exist, err = getAlertNotificationState(sess, cmd, nj)
  384. if err != nil {
  385. return err
  386. }
  387. if !exist {
  388. return errors.New("Should not happen")
  389. }
  390. cmd.Result = nj
  391. return nil
  392. }
  393. return err
  394. }
  395. cmd.Result = notificationState
  396. return nil
  397. })
  398. }
  399. func getAlertNotificationState(sess *DBSession, cmd *m.GetOrCreateNotificationStateQuery, nj *m.AlertNotificationState) (bool, error) {
  400. return sess.
  401. Where("alert_notification_state.org_id = ?", cmd.OrgId).
  402. Where("alert_notification_state.alert_id = ?", cmd.AlertId).
  403. Where("alert_notification_state.notifier_id = ?", cmd.NotifierId).
  404. Get(nj)
  405. }