From 2ae63e70c0df3ce2e1cf478d4173be72428c3dd9 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 26 Mar 2019 18:37:02 +0700 Subject: [PATCH] Alerting: Notification channel http api enhancements (#16219) Now returns uid in response to get notification channel by id. Adds GET/PUT/DELETE support for notification channel by uid, /api/alert-notifications/uid/:uid. Break apart alerting and alert notification http api docs in two pages and update documentation to make it up to date with current implementation. Fixes #16012 --- docs/sources/http_api/admin.md | 10 +- docs/sources/http_api/alerting.md | 196 +--------- .../alerting_notification_channels.md | 369 ++++++++++++++++++ docs/sources/http_api/index.md | 1 + pkg/api/alerting.go | 49 +++ pkg/api/api.go | 3 + pkg/models/alert_notifications.go | 16 +- pkg/services/sqlstore/alert_notification.go | 1 + 8 files changed, 443 insertions(+), 202 deletions(-) create mode 100644 docs/sources/http_api/alerting_notification_channels.md diff --git a/docs/sources/http_api/admin.md b/docs/sources/http_api/admin.md index c2d540c452b..1cb20a7f7e9 100644 --- a/docs/sources/http_api/admin.md +++ b/docs/sources/http_api/admin.md @@ -319,7 +319,7 @@ Only works with Basic Authentication (username and password). See [introduction] **Example Request**: -```json +```http POST /api/admin/pause-all-alerts HTTP/1.1 Accept: application/json Content-Type: application/json @@ -335,11 +335,15 @@ JSON Body schema: **Example Response**: -```json +```http HTTP/1.1 200 Content-Type: application/json -{"state": "new state", "message": "alerts pause/un paused", "alertsAffected": 100} +{ + "state": "Paused", + "message": "alert paused", + "alertsAffected": 1 +} ``` ## Auth tokens for User diff --git a/docs/sources/http_api/alerting.md b/docs/sources/http_api/alerting.md index 69868746bfa..49254f9e231 100644 --- a/docs/sources/http_api/alerting.md +++ b/docs/sources/http_api/alerting.md @@ -1,7 +1,7 @@ +++ title = "Alerting HTTP API " -description = "Grafana Alerting HTTP API" -keywords = ["grafana", "http", "documentation", "api", "alerting"] +description = "Grafana Alerts HTTP API" +keywords = ["grafana", "http", "documentation", "api", "alerting", "alerts"] aliases = ["/http_api/alerting/"] type = "docs" [menu.docs] @@ -9,14 +9,11 @@ name = "Alerting" parent = "http_api" +++ - # Alerting API You can use the Alerting API to get information about alerts and their states but this API cannot be used to modify the alert. To create new alerts or modify them you need to update the dashboard json that contains the alerts. -This API can also be used to create, update and delete alert notifications. - ## Get alerts `GET /api/alerts/` @@ -69,7 +66,7 @@ Content-Type: application/json ] ``` -## Get one alert +## Get alert by id `GET /api/alerts/:id` @@ -120,7 +117,7 @@ Content-Type: application/json If data from one server triggers the alert first and, before that server is seen leaving alerting state, a second server also enters a state that would trigger the alert, the second server will not be visible in "evalMatches" data. -## Pause alert +## Pause alert by id `POST /api/alerts/:id/pause` @@ -158,187 +155,4 @@ Content-Type: application/json ## Pause all alerts -`POST /api/admin/pause-all-alerts` - -Only works with Basic Authentication (username and password). See [introduction](http://docs.grafana.org/http_api/admin/#admin-api) for an explanation. - -**Example Request**: - -```http -POST /api/admin/pause-all-alerts HTTP/1.1 -Accept: application/json -Content-Type: application/json - -{ - "paused": true -} -``` - -JSON Body Schema: - -- **paused** – Can be `true` or `false`. True to pause an alert. False to unpause an alert. - -**Example Response**: - -```http -HTTP/1.1 200 -Content-Type: application/json - -{ - "state": "Paused", - "message": "alert paused", - "alertsAffected": 1 -} -``` - -## Get alert notifications - -`GET /api/alert-notifications` - -**Example Request**: - -```http -GET /api/alert-notifications HTTP/1.1 -Accept: application/json -Content-Type: application/json -Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk -``` - - -**Example Response**: - -```http -HTTP/1.1 200 -Content-Type: application/json - -[ - { - "id": 1, - "name": "Team A", - "type": "email", - "isDefault": false, - "sendReminder": false, - "settings": { - "addresses": "carl@grafana.com;dev@grafana.com" - }, - "created": "2018-04-23T14:44:09+02:00", - "updated": "2018-08-20T15:47:49+02:00" - } -] - -``` - -## Create alert notification - -You can find the full list of [supported notifiers](/alerting/notifications/#all-supported-notifier) at the alert notifiers page. - -`POST /api/alert-notifications` - -**Example Request**: - -```http -POST /api/alert-notifications HTTP/1.1 -Accept: application/json -Content-Type: application/json -Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk - -{ - "name": "new alert notification", //Required - "type": "email", //Required - "isDefault": false, - "sendReminder": false, - "settings": { - "addresses": "carl@grafana.com;dev@grafana.com" - } -} -``` - -**Example Response**: - -```http -HTTP/1.1 200 -Content-Type: application/json - -{ - "id": 1, - "name": "new alert notification", - "type": "email", - "isDefault": false, - "sendReminder": false, - "settings": { - "addresses": "carl@grafana.com;dev@grafana.com" - }, - "created": "2018-04-23T14:44:09+02:00", - "updated": "2018-08-20T15:47:49+02:00" -} -``` - -## Update alert notification - -`PUT /api/alert-notifications/:id` - -**Example Request**: - -```http -PUT /api/alert-notifications/1 HTTP/1.1 -Accept: application/json -Content-Type: application/json -Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk - -{ - "id": 1, - "name": "new alert notification", //Required - "type": "email", //Required - "isDefault": false, - "sendReminder": true, - "frequency": "15m", - "settings": { - "addresses": "carl@grafana.com;dev@grafana.com" - } -} -``` - -**Example Response**: - -```http -HTTP/1.1 200 -Content-Type: application/json - -{ - "id": 1, - "name": "new alert notification", - "type": "email", - "isDefault": false, - "sendReminder": true, - "frequency": "15m", - "settings": { - "addresses": "carl@grafana.com;dev@grafana.com" - }, - "created": "2017-01-01 12:34", - "updated": "2017-01-01 12:34" -} -``` - -## Delete alert notification - -`DELETE /api/alert-notifications/:id` - -**Example Request**: - -```http -DELETE /api/alert-notifications/1 HTTP/1.1 -Accept: application/json -Content-Type: application/json -Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk -``` - -**Example Response**: - -```http -HTTP/1.1 200 -Content-Type: application/json - -{ - "message": "Notification deleted" -} -``` +See [Admin API]({{< relref "http_api/admin.md#pause-all-alerts" >}}). diff --git a/docs/sources/http_api/alerting_notification_channels.md b/docs/sources/http_api/alerting_notification_channels.md new file mode 100644 index 00000000000..633bd58a5c7 --- /dev/null +++ b/docs/sources/http_api/alerting_notification_channels.md @@ -0,0 +1,369 @@ ++++ +title = "Alerting Notification Channels HTTP API " +description = "Grafana Alerting Notification Channel HTTP API" +keywords = ["grafana", "http", "documentation", "api", "alerting", "alerts", "notifications"] +aliases = [] +type = "docs" +[menu.docs] +name = "Alerting Notification Channels" +parent = "http_api" ++++ + +# Alerting Notification Channels API + +## Identifier (id) vs unique identifier (uid) + +The identifier (id) of a notification channel is an auto-incrementing numeric value and is only unique per Grafana install. + +The unique identifier (uid) of a notification channel can be used for uniquely identify a notification channel between +multiple Grafana installs. It's automatically generated if not provided when creating a notification channel. The uid +allows having consistent URL's for accessing notification channels and when syncing notification channels between multiple +Grafana installs, see [alert notification channel provisioning](/administration/provisioning/#alert-notification-channels) +for more information. + +The uid can have a maximum length of 40 characters. + +## Get all notification channels + +Returns all notification channels that the authenticated user has permission to view. + +`GET /api/alert-notifications` + +**Example Request**: + +```http +GET /api/alert-notifications HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +``` + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json + +[ + { + "id": 1, + "uid": "team-a-email-notifier", + "name": "Team A", + "type": "email", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "settings": { + "addresses": "carl@grafana.com;dev@grafana.com" + }, + "created": "2018-04-23T14:44:09+02:00", + "updated": "2018-08-20T15:47:49+02:00" + } +] + +``` + +## Get notification channel by uid + +`GET /api/alert-notifications/uid/:uid` + +Will return the notification channel given the notification channel uid. + +**Example Request**: + +```http +GET /api/alert-notifications/uid/team-a-email-notifier HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +``` + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json + +{ + "id": 1, + "uid": "team-a-email-notifier", + "name": "Team A", + "type": "email", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "settings": { + "addresses": "carl@grafana.com;dev@grafana.com" + }, + "created": "2018-04-23T14:44:09+02:00", + "updated": "2018-08-20T15:47:49+02:00" +} +``` + +## Get notification channel by id + +`GET /api/alert-notifications/:id` + +Will return the notification channel given the notification channel id. + +**Example Request**: + +```http +GET /api/alert-notifications/1 HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +``` + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json + +{ + "id": 1, + "uid": "team-a-email-notifier", + "name": "Team A", + "type": "email", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "settings": { + "addresses": "carl@grafana.com;dev@grafana.com" + }, + "created": "2018-04-23T14:44:09+02:00", + "updated": "2018-08-20T15:47:49+02:00" +} +``` + +## Create notification channel + +You can find the full list of [supported notifiers](/alerting/notifications/#all-supported-notifier) at the alert notifiers page. + +`POST /api/alert-notifications` + +**Example Request**: + +```http +POST /api/alert-notifications HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk + +{ + "name": "new alert notification", //Required + "type": "email", //Required + "isDefault": false, + "sendReminder": false, + "settings": { + "addresses": "carl@grafana.com;dev@grafana.com" + } +} +``` + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json + +{ + "id": 1, + "uid": "cIBgcSjkk", + "name": "new alert notification", + "type": "email", + "isDefault": false, + "sendReminder": false, + "settings": { + "addresses": "carl@grafana.com;dev@grafana.com" + }, + "created": "2018-04-23T14:44:09+02:00", + "updated": "2018-08-20T15:47:49+02:00" +} +``` + +## Update notification channel by uid + +`PUT /api/alert-notifications/uid/:uid` + +Updates an existing notification channel identified by uid. + +**Example Request**: + +```http +PUT /api/alert-notifications/uid/cIBgcSjkk HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk + +{ + "name": "new alert notification", //Required + "type": "email", //Required + "isDefault": false, + "sendReminder": true, + "frequency": "15m", + "settings": { + "addresses": "carl@grafana.com;dev@grafana.com" + } +} +``` + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json + +{ + "id": 1, + "uid": "cIBgcSjkk", + "name": "new alert notification", + "type": "email", + "isDefault": false, + "sendReminder": true, + "frequency": "15m", + "settings": { + "addresses": "carl@grafana.com;dev@grafana.com" + }, + "created": "2017-01-01 12:34", + "updated": "2017-01-01 12:34" +} +``` + +## Update notification channel by id + +`PUT /api/alert-notifications/:id` + +Updates an existing notification channel identified by id. + +**Example Request**: + +```http +PUT /api/alert-notifications/1 HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk + +{ + "id": 1, + "uid": "cIBgcSjkk", + "name": "new alert notification", //Required + "type": "email", //Required + "isDefault": false, + "sendReminder": true, + "frequency": "15m", + "settings": { + "addresses": "carl@grafana.com;dev@grafana.com" + } +} +``` + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json + +{ + "id": 1, + "uid": "cIBgcSjkk", + "name": "new alert notification", + "type": "email", + "isDefault": false, + "sendReminder": true, + "frequency": "15m", + "settings": { + "addresses": "carl@grafana.com;dev@grafana.com" + }, + "created": "2017-01-01 12:34", + "updated": "2017-01-01 12:34" +} +``` + +## Delete alert notification by uid + +`DELETE /api/alert-notifications/uid/:uid` + +Deletes an existing notification channel identified by uid. + +**Example Request**: + +```http +DELETE /api/alert-notifications/uid/team-a-email-notifier HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +``` + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json + +{ + "message": "Notification deleted" +} +``` + +## Delete alert notification by id + +`DELETE /api/alert-notifications/:id` + +Deletes an existing notification channel identified by id. + +**Example Request**: + +```http +DELETE /api/alert-notifications/1 HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +``` + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json + +{ + "message": "Notification deleted" +} +``` + +## Test notification channel + +Sends a test notification message for the given notification channel type and settings. +You can find the full list of [supported notifiers](/alerting/notifications/#all-supported-notifier) at the alert notifiers page. + +`POST /api/alert-notifications/test` + +**Example Request**: + +```http +POST /api/alert-notifications/test HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk + +{ + "type": "email", + "settings": { + "addresses": "carl@grafana.com;dev@grafana.com" + } +} +``` + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json + +{ + "message": "Test notification sent" +} +``` + diff --git a/docs/sources/http_api/index.md b/docs/sources/http_api/index.md index 9e3d69363e8..b29722548cb 100644 --- a/docs/sources/http_api/index.md +++ b/docs/sources/http_api/index.md @@ -30,6 +30,7 @@ dashboards, creating users and updating data sources. * [Snapshot API]({{< relref "http_api/snapshot.md" >}}) * [Annotations API]({{< relref "http_api/annotations.md" >}}) * [Alerting API]({{< relref "http_api/alerting.md" >}}) +* [Alert Notification Channels API]({{< relref "http_api/alert_notification_channels.md" >}}) * [User API]({{< relref "http_api/user.md" >}}) * [Team API]({{< relref "http_api/team.md" >}}) * [Admin API]({{< relref "http_api/admin.md" >}}) diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go index 19fb4efd7e8..64f80598821 100644 --- a/pkg/api/alerting.go +++ b/pkg/api/alerting.go @@ -208,6 +208,31 @@ func GetAlertNotificationByID(c *m.ReqContext) Response { Id: c.ParamsInt64("notificationId"), } + if query.Id == 0 { + return Error(404, "Alert notification not found", nil) + } + + if err := bus.Dispatch(query); err != nil { + return Error(500, "Failed to get alert notifications", err) + } + + if query.Result == nil { + return Error(404, "Alert notification not found", nil) + } + + return JSON(200, dtos.NewAlertNotification(query.Result)) +} + +func GetAlertNotificationByUID(c *m.ReqContext) Response { + query := &m.GetAlertNotificationsWithUidQuery{ + OrgId: c.OrgId, + Uid: c.Params("uid"), + } + + if query.Uid == "" { + return Error(404, "Alert notification not found", nil) + } + if err := bus.Dispatch(query); err != nil { return Error(500, "Failed to get alert notifications", err) } @@ -239,6 +264,17 @@ func UpdateAlertNotification(c *m.ReqContext, cmd m.UpdateAlertNotificationComma return JSON(200, dtos.NewAlertNotification(cmd.Result)) } +func UpdateAlertNotificationByUID(c *m.ReqContext, cmd m.UpdateAlertNotificationWithUidCommand) Response { + cmd.OrgId = c.OrgId + cmd.Uid = c.Params("uid") + + if err := bus.Dispatch(&cmd); err != nil { + return Error(500, "Failed to update alert notification", err) + } + + return JSON(200, dtos.NewAlertNotification(cmd.Result)) +} + func DeleteAlertNotification(c *m.ReqContext) Response { cmd := m.DeleteAlertNotificationCommand{ OrgId: c.OrgId, @@ -252,6 +288,19 @@ func DeleteAlertNotification(c *m.ReqContext) Response { return Success("Notification deleted") } +func DeleteAlertNotificationByUID(c *m.ReqContext) Response { + cmd := m.DeleteAlertNotificationWithUidCommand{ + OrgId: c.OrgId, + Uid: c.Params("uid"), + } + + if err := bus.Dispatch(&cmd); err != nil { + return Error(500, "Failed to delete alert notification", err) + } + + return Success("Notification deleted") +} + //POST /api/alert-notifications/test func NotificationTest(c *m.ReqContext, dto dtos.NotificationTestCommand) Response { cmd := &alerting.NotificationTestCommand{ diff --git a/pkg/api/api.go b/pkg/api/api.go index 86bc83f558b..6c60a6757e8 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -350,6 +350,9 @@ func (hs *HTTPServer) registerRoutes() { alertNotifications.Put("/:notificationId", bind(m.UpdateAlertNotificationCommand{}), Wrap(UpdateAlertNotification)) alertNotifications.Get("/:notificationId", Wrap(GetAlertNotificationByID)) alertNotifications.Delete("/:notificationId", Wrap(DeleteAlertNotification)) + alertNotifications.Get("/uid/:uid", Wrap(GetAlertNotificationByUID)) + alertNotifications.Put("/uid/:uid", bind(m.UpdateAlertNotificationWithUidCommand{}), Wrap(UpdateAlertNotificationByUID)) + alertNotifications.Delete("/uid/:uid", Wrap(DeleteAlertNotificationByUID)) }, reqEditorRole) apiRoute.Get("/annotations", Wrap(GetAnnotations)) diff --git a/pkg/models/alert_notifications.go b/pkg/models/alert_notifications.go index 0a26276e787..3b8f071c75b 100644 --- a/pkg/models/alert_notifications.go +++ b/pkg/models/alert_notifications.go @@ -67,14 +67,14 @@ type UpdateAlertNotificationCommand struct { } type UpdateAlertNotificationWithUidCommand struct { - Uid string - Name string - Type string - SendReminder bool - DisableResolveMessage bool - Frequency string - IsDefault bool - Settings *simplejson.Json + Uid string `json:"-"` + Name string `json:"name" binding:"Required"` + Type string `json:"type" binding:"Required"` + SendReminder bool `json:"sendReminder"` + DisableResolveMessage bool `json:"disableResolveMessage"` + Frequency string `json:"frequency"` + IsDefault bool `json:"isDefault"` + Settings *simplejson.Json `json:"settings" binding:"Required"` OrgId int64 Result *AlertNotification diff --git a/pkg/services/sqlstore/alert_notification.go b/pkg/services/sqlstore/alert_notification.go index 2685c6b52a1..b90e3c8d20b 100644 --- a/pkg/services/sqlstore/alert_notification.go +++ b/pkg/services/sqlstore/alert_notification.go @@ -130,6 +130,7 @@ func getAlertNotificationInternal(query *m.GetAlertNotificationsQuery, sess *DBS sql.WriteString(`SELECT alert_notification.id, + alert_notification.uid, alert_notification.org_id, alert_notification.name, alert_notification.type,