2016-04-26 10:36:50 -05:00
package api
import (
2021-10-07 09:33:50 -05:00
"context"
2020-10-20 06:53:48 -05:00
"errors"
2016-07-21 03:29:11 -05:00
"fmt"
2021-11-29 03:18:01 -06:00
"net/http"
2018-06-01 07:36:40 -05:00
"strconv"
2016-07-21 03:29:11 -05:00
2016-04-27 06:02:28 -05:00
"github.com/grafana/grafana/pkg/api/dtos"
2021-01-15 07:43:20 -06:00
"github.com/grafana/grafana/pkg/api/response"
2019-08-12 13:03:48 -05:00
"github.com/grafana/grafana/pkg/models"
2016-07-20 09:13:36 -05:00
"github.com/grafana/grafana/pkg/services/alerting"
2023-01-18 06:52:41 -06:00
"github.com/grafana/grafana/pkg/services/dashboards"
2022-06-27 11:23:15 -05:00
"github.com/grafana/grafana/pkg/services/datasources"
2018-01-30 07:41:25 -06:00
"github.com/grafana/grafana/pkg/services/guardian"
2022-07-20 12:58:36 -05:00
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels_config"
2023-01-17 13:47:31 -06:00
"github.com/grafana/grafana/pkg/services/notifications"
2018-06-01 07:36:40 -05:00
"github.com/grafana/grafana/pkg/services/search"
2021-10-07 09:33:50 -05:00
"github.com/grafana/grafana/pkg/setting"
2020-09-11 11:04:43 -05:00
"github.com/grafana/grafana/pkg/util"
2021-10-11 07:30:59 -05:00
"github.com/grafana/grafana/pkg/web"
2016-04-26 10:36:50 -05:00
)
2022-02-04 06:41:15 -06:00
func ( hs * HTTPServer ) ValidateOrgAlert ( c * models . ReqContext ) {
2022-01-14 10:55:57 -06:00
id , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":alertId" ] , 10 , 64 )
if err != nil {
c . JsonApiErr ( http . StatusBadRequest , "alertId is invalid" , nil )
return
}
2019-08-12 13:03:48 -05:00
query := models . GetAlertByIdQuery { Id : id }
2016-04-26 10:36:50 -05:00
2022-08-03 10:17:26 -05:00
if err := hs . AlertEngine . AlertStore . GetAlertById ( c . Req . Context ( ) , & query ) ; err != nil {
2016-04-26 10:36:50 -05:00
c . JsonApiErr ( 404 , "Alert not found" , nil )
return
}
2022-08-11 06:28:55 -05:00
if c . OrgID != query . Result . OrgId {
2016-04-26 10:36:50 -05:00
c . JsonApiErr ( 403 , "You are not allowed to edit/view alert" , nil )
return
2017-10-23 02:56:52 -05:00
}
2016-04-26 10:36:50 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /alerts/states-for-dashboard legacy_alerts getDashboardStates
//
// Get alert states for a dashboard.
//
// Responses:
// Responses:
// 200: getDashboardStatesResponse
// 400: badRequestError
// 500: internalServerError
2022-02-04 06:41:15 -06:00
func ( hs * HTTPServer ) GetAlertStatesForDashboard ( c * models . ReqContext ) response . Response {
2018-03-22 06:37:35 -05:00
dashboardID := c . QueryInt64 ( "dashboardId" )
2016-09-30 10:37:47 -05:00
2018-03-22 06:37:35 -05:00
if dashboardID == 0 {
2021-01-15 07:43:20 -06:00
return response . Error ( 400 , "Missing query parameter dashboardId" , nil )
2016-09-30 10:37:47 -05:00
}
2019-08-12 13:03:48 -05:00
query := models . GetAlertStatesForDashboardQuery {
2022-08-11 06:28:55 -05:00
OrgId : c . OrgID ,
2016-09-30 10:37:47 -05:00
DashboardId : c . QueryInt64 ( "dashboardId" ) ,
}
2022-08-03 10:17:26 -05:00
if err := hs . AlertEngine . AlertStore . GetAlertStatesForDashboard ( c . Req . Context ( ) , & query ) ; err != nil {
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to fetch alert states" , err )
2016-09-30 10:37:47 -05:00
}
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , query . Result )
2016-09-30 10:37:47 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /alerts legacy_alerts getAlerts
//
// Get legacy alerts.
//
// Responses:
// 200: getAlertsResponse
// 401: unauthorisedError
// 500: internalServerError
2022-02-04 06:41:15 -06:00
func ( hs * HTTPServer ) GetAlerts ( c * models . ReqContext ) response . Response {
2018-06-01 07:36:40 -05:00
dashboardQuery := c . Query ( "dashboardQuery" )
dashboardTags := c . QueryStrings ( "dashboardTag" )
stringDashboardIDs := c . QueryStrings ( "dashboardId" )
stringFolderIDs := c . QueryStrings ( "folderId" )
dashboardIDs := make ( [ ] int64 , 0 )
for _ , id := range stringDashboardIDs {
dashboardID , err := strconv . ParseInt ( id , 10 , 64 )
if err == nil {
dashboardIDs = append ( dashboardIDs , dashboardID )
}
}
if dashboardQuery != "" || len ( dashboardTags ) > 0 || len ( stringFolderIDs ) > 0 {
folderIDs := make ( [ ] int64 , 0 )
for _ , id := range stringFolderIDs {
folderID , err := strconv . ParseInt ( id , 10 , 64 )
if err == nil {
folderIDs = append ( folderIDs , folderID )
}
}
searchQuery := search . Query {
Title : dashboardQuery ,
Tags : dashboardTags ,
SignedInUser : c . SignedInUser ,
Limit : 1000 ,
2022-08-11 06:28:55 -05:00
OrgId : c . OrgID ,
2018-06-01 07:36:40 -05:00
DashboardIds : dashboardIDs ,
2022-03-21 10:54:30 -05:00
Type : string ( models . DashHitDB ) ,
2018-06-01 07:36:40 -05:00
FolderIds : folderIDs ,
2019-08-12 13:03:48 -05:00
Permission : models . PERMISSION_VIEW ,
2018-06-01 07:36:40 -05:00
}
2022-02-04 06:41:15 -06:00
err := hs . SearchService . SearchHandler ( c . Req . Context ( ) , & searchQuery )
2018-06-01 07:36:40 -05:00
if err != nil {
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "List alerts failed" , err )
2018-06-01 07:36:40 -05:00
}
for _ , d := range searchQuery . Result {
2022-03-21 10:54:30 -05:00
if d . Type == models . DashHitDB && d . ID > 0 {
2021-02-11 01:49:16 -06:00
dashboardIDs = append ( dashboardIDs , d . ID )
2018-06-01 07:36:40 -05:00
}
}
// if we didn't find any dashboards, return empty result
if len ( dashboardIDs ) == 0 {
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , [ ] * models . AlertListItemDTO { } )
2018-06-01 07:36:40 -05:00
}
}
2019-08-12 13:03:48 -05:00
query := models . GetAlertsQuery {
2022-08-11 06:28:55 -05:00
OrgId : c . OrgID ,
2018-06-01 07:36:40 -05:00
DashboardIDs : dashboardIDs ,
PanelId : c . QueryInt64 ( "panelId" ) ,
Limit : c . QueryInt64 ( "limit" ) ,
User : c . SignedInUser ,
Query : c . Query ( "query" ) ,
2016-04-26 10:36:50 -05:00
}
2016-09-14 07:12:19 -05:00
states := c . QueryStrings ( "state" )
if len ( states ) > 0 {
query . State = states
}
2022-08-03 10:17:26 -05:00
if err := hs . AlertEngine . AlertStore . HandleAlertsQuery ( c . Req . Context ( ) , & query ) ; err != nil {
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "List alerts failed" , err )
2016-04-26 10:36:50 -05:00
}
2018-02-16 06:56:04 -06:00
for _ , alert := range query . Result {
2023-01-18 06:52:41 -06:00
alert . Url = dashboards . GetDashboardURL ( alert . DashboardUid , alert . DashboardSlug )
2018-01-31 03:47:31 -06:00
}
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , query . Result )
2016-04-26 10:36:50 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route POST /alerts/test legacy_alerts testAlert
//
// Test alert.
//
// Responses:
// 200: testAlertResponse
// 400: badRequestError
// 422: unprocessableEntityError
// 403: forbiddenError
// 500: internalServerError
2021-11-29 03:18:01 -06:00
func ( hs * HTTPServer ) AlertTest ( c * models . ReqContext ) response . Response {
dto := dtos . AlertTestCommand { }
if err := web . Bind ( c . Req , & dto ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2016-11-23 07:55:10 -06:00
if _ , idErr := dto . Dashboard . Get ( "id" ) . Int64 ( ) ; idErr != nil {
2021-01-15 07:43:20 -06:00
return response . Error ( 400 , "The dashboard needs to be saved at least once before you can test an alert rule" , nil )
2016-11-23 07:55:10 -06:00
}
2022-08-11 06:28:55 -05:00
res , err := hs . AlertEngine . AlertTest ( c . OrgID , dto . Dashboard , dto . PanelId , c . SignedInUser )
2021-03-08 00:02:49 -06:00
if err != nil {
2020-11-19 06:34:28 -06:00
var validationErr alerting . ValidationError
if errors . As ( err , & validationErr ) {
2021-01-15 07:43:20 -06:00
return response . Error ( 422 , validationErr . Error ( ) , nil )
2016-07-21 06:09:12 -05:00
}
2022-06-27 11:23:15 -05:00
if errors . Is ( err , datasources . ErrDataSourceAccessDenied ) {
2021-01-15 07:43:20 -06:00
return response . Error ( 403 , "Access denied to datasource" , err )
2018-11-05 07:25:19 -06:00
}
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to test rule" , err )
2016-07-20 09:13:36 -05:00
}
2016-07-21 03:29:11 -05:00
dtoRes := & dtos . AlertTestResult {
2016-11-17 08:48:15 -06:00
Firing : res . Firing ,
ConditionEvals : res . ConditionEvals ,
2017-01-13 03:24:40 -06:00
State : res . Rule . State ,
2016-07-21 03:29:11 -05:00
}
if res . Error != nil {
dtoRes . Error = res . Error . Error ( )
}
2016-07-21 06:09:12 -05:00
for _ , log := range res . Logs {
dtoRes . Logs = append ( dtoRes . Logs , & dtos . AlertTestResultLog { Message : log . Message , Data : log . Data } )
}
2016-08-18 04:22:24 -05:00
for _ , match := range res . EvalMatches {
dtoRes . EvalMatches = append ( dtoRes . EvalMatches , & dtos . EvalMatch { Metric : match . Metric , Value : match . Value } )
}
2016-07-21 06:09:12 -05:00
2016-07-26 05:29:52 -05:00
dtoRes . TimeMs = fmt . Sprintf ( "%1.3fms" , res . GetDurationMs ( ) )
2016-07-21 03:29:11 -05:00
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , dtoRes )
2016-07-20 09:13:36 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /alerts/{alert_id} legacy_alerts getAlertByID
//
// Get alert by ID.
//
// “evalMatches” data in the response is cached in the db when and only when the state of the alert changes (e.g. transitioning from “ok” to “alerting” state).
// 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.
//
// Responses:
// 200: getAlertResponse
// 401: unauthorisedError
// 500: internalServerError
2022-02-04 06:41:15 -06:00
func ( hs * HTTPServer ) GetAlert ( c * models . ReqContext ) response . Response {
2022-01-14 10:55:57 -06:00
id , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":alertId" ] , 10 , 64 )
if err != nil {
return response . Error ( http . StatusBadRequest , "alertId is invalid" , err )
}
2019-08-12 13:03:48 -05:00
query := models . GetAlertByIdQuery { Id : id }
2016-04-26 10:36:50 -05:00
2022-08-03 10:17:26 -05:00
if err := hs . AlertEngine . AlertStore . GetAlertById ( c . Req . Context ( ) , & query ) ; err != nil {
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "List alerts failed" , err )
2016-04-26 10:36:50 -05:00
}
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , & query . Result )
2016-04-26 10:36:50 -05:00
}
2016-04-28 01:23:50 -05:00
2022-02-04 06:41:15 -06:00
func ( hs * HTTPServer ) GetAlertNotifiers ( ngalertEnabled bool ) func ( * models . ReqContext ) response . Response {
2021-05-04 06:58:39 -05:00
return func ( _ * models . ReqContext ) response . Response {
if ngalertEnabled {
2022-07-20 12:58:36 -05:00
return response . JSON ( http . StatusOK , channels_config . GetAvailableNotifiers ( ) )
2021-05-04 06:58:39 -05:00
}
// TODO(codesome): This wont be required in 8.0 since ngalert
// will be enabled by default with no disabling. This is to be removed later.
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , alerting . GetNotifiers ( ) )
2021-05-04 06:58:39 -05:00
}
2017-01-06 05:04:25 -06:00
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /alert-notifications/lookup legacy_alerts_notification_channels getAlertNotificationLookup
//
2022-09-12 02:40:35 -05:00
// Get all notification channels (lookup).
2022-07-27 08:54:37 -05:00
//
// Returns all notification channels, but with less detailed information. Accessible by any authenticated user and is mainly used by providing alert notification channels in Grafana UI when configuring alert rule.
//
// Responses:
// 200: getAlertNotificationLookupResponse
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
2022-02-04 06:41:15 -06:00
func ( hs * HTTPServer ) GetAlertNotificationLookup ( c * models . ReqContext ) response . Response {
alertNotifications , err := hs . getAlertNotificationsInternal ( c )
2019-08-12 13:03:48 -05:00
if err != nil {
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to get alert notifications" , err )
2019-08-12 13:03:48 -05:00
}
2016-06-15 03:48:04 -05:00
2019-08-12 13:03:48 -05:00
result := make ( [ ] * dtos . AlertNotificationLookup , 0 )
for _ , notification := range alertNotifications {
result = append ( result , dtos . NewAlertNotificationLookup ( notification ) )
}
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , result )
2019-08-12 13:03:48 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /alert-notifications legacy_alerts_notification_channels getAlertNotificationChannels
//
// Get all notification channels.
//
// Returns all notification channels that the authenticated user has permission to view.
//
// Responses:
// 200: getAlertNotificationChannelsResponse
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
2022-02-04 06:41:15 -06:00
func ( hs * HTTPServer ) GetAlertNotifications ( c * models . ReqContext ) response . Response {
alertNotifications , err := hs . getAlertNotificationsInternal ( c )
2019-08-12 13:03:48 -05:00
if err != nil {
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to get alert notifications" , err )
2016-06-15 03:48:04 -05:00
}
2016-11-06 08:40:02 -06:00
result := make ( [ ] * dtos . AlertNotification , 0 )
2016-06-20 04:31:20 -05:00
2019-08-12 13:03:48 -05:00
for _ , notification := range alertNotifications {
2018-06-04 15:19:27 -05:00
result = append ( result , dtos . NewAlertNotification ( notification ) )
2016-06-20 04:31:20 -05:00
}
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , result )
2016-06-15 03:48:04 -05:00
}
2022-02-04 06:41:15 -06:00
func ( hs * HTTPServer ) getAlertNotificationsInternal ( c * models . ReqContext ) ( [ ] * models . AlertNotification , error ) {
2022-08-11 06:28:55 -05:00
query := & models . GetAllAlertNotificationsQuery { OrgId : c . OrgID }
2019-08-12 13:03:48 -05:00
2022-02-16 11:54:29 -06:00
if err := hs . AlertNotificationService . GetAllAlertNotifications ( c . Req . Context ( ) , query ) ; err != nil {
2019-08-12 13:03:48 -05:00
return nil , err
}
return query . Result , nil
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /alert-notifications/{notification_channel_id} legacy_alerts_notification_channels getAlertNotificationChannelByID
//
// Get notification channel by ID.
//
// Returns the notification channel given the notification channel ID.
//
// Responses:
// 200: getAlertNotificationChannelResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2022-02-04 06:41:15 -06:00
func ( hs * HTTPServer ) GetAlertNotificationByID ( c * models . ReqContext ) response . Response {
2022-01-14 10:55:57 -06:00
notificationId , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":notificationId" ] , 10 , 64 )
if err != nil {
return response . Error ( http . StatusBadRequest , "notificationId is invalid" , err )
}
2019-08-12 13:03:48 -05:00
query := & models . GetAlertNotificationsQuery {
2022-08-11 06:28:55 -05:00
OrgId : c . OrgID ,
2022-01-14 10:55:57 -06:00
Id : notificationId ,
2016-06-16 07:29:20 -05:00
}
2019-03-26 06:37:02 -05:00
if query . Id == 0 {
2021-01-15 07:43:20 -06:00
return response . Error ( 404 , "Alert notification not found" , nil )
2019-03-26 06:37:02 -05:00
}
2022-02-16 11:54:29 -06:00
if err := hs . AlertNotificationService . GetAlertNotifications ( c . Req . Context ( ) , query ) ; err != nil {
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to get alert notifications" , err )
2019-03-26 06:37:02 -05:00
}
if query . Result == nil {
2021-01-15 07:43:20 -06:00
return response . Error ( 404 , "Alert notification not found" , nil )
2019-03-26 06:37:02 -05:00
}
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , dtos . NewAlertNotification ( query . Result ) )
2019-03-26 06:37:02 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /alert-notifications/uid/{notification_channel_uid} legacy_alerts_notification_channels getAlertNotificationChannelByUID
//
2022-09-12 02:40:35 -05:00
// Get notification channel by UID.
2022-07-27 08:54:37 -05:00
//
// Returns the notification channel given the notification channel UID.
//
// Responses:
// 200: getAlertNotificationChannelResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2022-02-04 06:41:15 -06:00
func ( hs * HTTPServer ) GetAlertNotificationByUID ( c * models . ReqContext ) response . Response {
2019-08-12 13:03:48 -05:00
query := & models . GetAlertNotificationsWithUidQuery {
2022-08-11 06:28:55 -05:00
OrgId : c . OrgID ,
2021-10-11 07:30:59 -05:00
Uid : web . Params ( c . Req ) [ ":uid" ] ,
2019-03-26 06:37:02 -05:00
}
if query . Uid == "" {
2021-01-15 07:43:20 -06:00
return response . Error ( 404 , "Alert notification not found" , nil )
2019-03-26 06:37:02 -05:00
}
2022-02-16 11:54:29 -06:00
if err := hs . AlertNotificationService . GetAlertNotificationsWithUid ( c . Req . Context ( ) , query ) ; err != nil {
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to get alert notifications" , err )
2016-06-16 07:29:20 -05:00
}
2019-01-13 13:30:20 -06:00
if query . Result == nil {
2021-01-15 07:43:20 -06:00
return response . Error ( 404 , "Alert notification not found" , nil )
2019-01-13 13:30:20 -06:00
}
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , dtos . NewAlertNotification ( query . Result ) )
2016-06-16 07:29:20 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route POST /alert-notifications legacy_alerts_notification_channels createAlertNotificationChannel
//
// Create notification channel.
//
// You can find the full list of [supported notifiers](https://grafana.com/docs/grafana/latest/alerting/old-alerting/notifications/#list-of-supported-notifiers) on the alert notifiers page.
//
// Responses:
// 200: getAlertNotificationChannelResponse
// 401: unauthorisedError
// 403: forbiddenError
// 409: conflictError
// 500: internalServerError
2022-02-04 06:41:15 -06:00
func ( hs * HTTPServer ) CreateAlertNotification ( c * models . ReqContext ) response . Response {
2021-11-29 03:18:01 -06:00
cmd := models . CreateAlertNotificationCommand { }
if err := web . Bind ( c . Req , & cmd ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2022-08-11 06:28:55 -05:00
cmd . OrgId = c . OrgID
2016-06-15 03:48:04 -05:00
2022-02-16 11:54:29 -06:00
if err := hs . AlertNotificationService . CreateAlertNotificationCommand ( c . Req . Context ( ) , & cmd ) ; err != nil {
2020-10-20 06:53:48 -05:00
if errors . Is ( err , models . ErrAlertNotificationWithSameNameExists ) || errors . Is ( err , models . ErrAlertNotificationWithSameUIDExists ) {
2021-01-15 07:43:20 -06:00
return response . Error ( 409 , "Failed to create alert notification" , err )
2020-10-20 06:53:48 -05:00
}
2021-11-02 08:11:19 -05:00
var alertingErr alerting . ValidationError
if errors . As ( err , & alertingErr ) {
return response . Error ( 400 , err . Error ( ) , err )
}
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to create alert notification" , err )
2016-06-15 03:48:04 -05:00
}
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , dtos . NewAlertNotification ( cmd . Result ) )
2016-06-15 03:48:04 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route PUT /alert-notifications/{notification_channel_id} legacy_alerts_notification_channels updateAlertNotificationChannel
//
// Update notification channel by ID.
//
// Updates an existing notification channel identified by ID.
//
// Responses:
// 200: getAlertNotificationChannelResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2021-11-29 03:18:01 -06:00
func ( hs * HTTPServer ) UpdateAlertNotification ( c * models . ReqContext ) response . Response {
cmd := models . UpdateAlertNotificationCommand { }
if err := web . Bind ( c . Req , & cmd ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2022-08-11 06:28:55 -05:00
cmd . OrgId = c . OrgID
2016-06-15 03:48:04 -05:00
2021-10-07 09:33:50 -05:00
err := hs . fillWithSecureSettingsData ( c . Req . Context ( ) , & cmd )
2020-07-08 03:17:05 -05:00
if err != nil {
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to update alert notification" , err )
2020-07-08 03:17:05 -05:00
}
2022-02-16 11:54:29 -06:00
if err := hs . AlertNotificationService . UpdateAlertNotification ( c . Req . Context ( ) , & cmd ) ; err != nil {
2020-11-19 06:34:28 -06:00
if errors . Is ( err , models . ErrAlertNotificationNotFound ) {
2021-01-15 07:43:20 -06:00
return response . Error ( 404 , err . Error ( ) , err )
2020-09-11 11:04:43 -05:00
}
2021-11-02 08:11:19 -05:00
var alertingErr alerting . ValidationError
if errors . As ( err , & alertingErr ) {
return response . Error ( 400 , err . Error ( ) , err )
}
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to update alert notification" , err )
2016-06-15 03:48:04 -05:00
}
2020-07-08 03:17:05 -05:00
query := models . GetAlertNotificationsQuery {
2022-08-11 06:28:55 -05:00
OrgId : c . OrgID ,
2020-07-08 03:17:05 -05:00
Id : cmd . Id ,
}
2022-02-16 11:54:29 -06:00
if err := hs . AlertNotificationService . GetAlertNotifications ( c . Req . Context ( ) , & query ) ; err != nil {
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to get alert notification" , err )
2020-07-08 03:17:05 -05:00
}
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , dtos . NewAlertNotification ( query . Result ) )
2016-06-15 03:48:04 -05:00
}
2016-06-16 08:21:44 -05:00
2022-07-27 08:54:37 -05:00
// swagger:route PUT /alert-notifications/uid/{notification_channel_uid} legacy_alerts_notification_channels updateAlertNotificationChannelByUID
//
// Update notification channel by UID.
//
// Updates an existing notification channel identified by uid.
//
// Responses:
// 200: getAlertNotificationChannelResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2021-11-29 03:18:01 -06:00
func ( hs * HTTPServer ) UpdateAlertNotificationByUID ( c * models . ReqContext ) response . Response {
cmd := models . UpdateAlertNotificationWithUidCommand { }
if err := web . Bind ( c . Req , & cmd ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2022-08-11 06:28:55 -05:00
cmd . OrgId = c . OrgID
2021-10-11 07:30:59 -05:00
cmd . Uid = web . Params ( c . Req ) [ ":uid" ]
2019-03-26 06:37:02 -05:00
2021-10-07 09:33:50 -05:00
err := hs . fillWithSecureSettingsDataByUID ( c . Req . Context ( ) , & cmd )
2020-07-08 03:17:05 -05:00
if err != nil {
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to update alert notification" , err )
2020-07-08 03:17:05 -05:00
}
2022-02-16 11:54:29 -06:00
if err := hs . AlertNotificationService . UpdateAlertNotificationWithUid ( c . Req . Context ( ) , & cmd ) ; err != nil {
2020-11-19 06:34:28 -06:00
if errors . Is ( err , models . ErrAlertNotificationNotFound ) {
2021-01-15 07:43:20 -06:00
return response . Error ( 404 , err . Error ( ) , nil )
2020-09-11 11:04:43 -05:00
}
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to update alert notification" , err )
2019-03-26 06:37:02 -05:00
}
2020-07-08 03:17:05 -05:00
query := models . GetAlertNotificationsWithUidQuery {
OrgId : cmd . OrgId ,
Uid : cmd . Uid ,
}
2022-02-16 11:54:29 -06:00
if err := hs . AlertNotificationService . GetAlertNotificationsWithUid ( c . Req . Context ( ) , & query ) ; err != nil {
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to get alert notification" , err )
2020-07-08 03:17:05 -05:00
}
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , dtos . NewAlertNotification ( query . Result ) )
2020-07-08 03:17:05 -05:00
}
2021-10-07 09:33:50 -05:00
func ( hs * HTTPServer ) fillWithSecureSettingsData ( ctx context . Context , cmd * models . UpdateAlertNotificationCommand ) error {
2020-07-08 03:17:05 -05:00
if len ( cmd . SecureSettings ) == 0 {
return nil
}
query := & models . GetAlertNotificationsQuery {
OrgId : cmd . OrgId ,
Id : cmd . Id ,
}
2022-02-16 11:54:29 -06:00
if err := hs . AlertNotificationService . GetAlertNotifications ( ctx , query ) ; err != nil {
2021-10-07 09:33:50 -05:00
return err
}
secureSettings , err := hs . EncryptionService . DecryptJsonData ( ctx , query . Result . SecureSettings , setting . SecretKey )
if err != nil {
2020-07-08 03:17:05 -05:00
return err
}
for k , v := range secureSettings {
if _ , ok := cmd . SecureSettings [ k ] ; ! ok {
cmd . SecureSettings [ k ] = v
}
}
return nil
}
2021-10-07 09:33:50 -05:00
func ( hs * HTTPServer ) fillWithSecureSettingsDataByUID ( ctx context . Context , cmd * models . UpdateAlertNotificationWithUidCommand ) error {
2020-07-08 03:17:05 -05:00
if len ( cmd . SecureSettings ) == 0 {
return nil
}
query := & models . GetAlertNotificationsWithUidQuery {
OrgId : cmd . OrgId ,
Uid : cmd . Uid ,
}
2022-02-16 11:54:29 -06:00
if err := hs . AlertNotificationService . GetAlertNotificationsWithUid ( ctx , query ) ; err != nil {
2021-10-07 09:33:50 -05:00
return err
}
secureSettings , err := hs . EncryptionService . DecryptJsonData ( ctx , query . Result . SecureSettings , setting . SecretKey )
if err != nil {
2020-07-08 03:17:05 -05:00
return err
}
for k , v := range secureSettings {
if _ , ok := cmd . SecureSettings [ k ] ; ! ok {
cmd . SecureSettings [ k ] = v
}
}
return nil
2019-03-26 06:37:02 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route DELETE /alert-notifications/{notification_channel_id} legacy_alerts_notification_channels deleteAlertNotificationChannel
//
// Delete alert notification by ID.
//
// Deletes an existing notification channel identified by ID.
//
// Responses:
// 200: okResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2022-02-04 06:41:15 -06:00
func ( hs * HTTPServer ) DeleteAlertNotification ( c * models . ReqContext ) response . Response {
2022-01-14 10:55:57 -06:00
notificationId , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":notificationId" ] , 10 , 64 )
if err != nil {
return response . Error ( http . StatusBadRequest , "notificationId is invalid" , err )
}
2019-08-12 13:03:48 -05:00
cmd := models . DeleteAlertNotificationCommand {
2022-08-11 06:28:55 -05:00
OrgId : c . OrgID ,
2022-01-14 10:55:57 -06:00
Id : notificationId ,
2016-06-16 08:21:44 -05:00
}
2022-02-16 11:54:29 -06:00
if err := hs . AlertNotificationService . DeleteAlertNotification ( c . Req . Context ( ) , & cmd ) ; err != nil {
2020-11-19 06:34:28 -06:00
if errors . Is ( err , models . ErrAlertNotificationNotFound ) {
2021-01-15 07:43:20 -06:00
return response . Error ( 404 , err . Error ( ) , nil )
2020-09-11 11:04:43 -05:00
}
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to delete alert notification" , err )
2016-06-16 08:21:44 -05:00
}
2021-01-15 07:43:20 -06:00
return response . Success ( "Notification deleted" )
2016-06-16 08:21:44 -05:00
}
2016-08-30 02:32:56 -05:00
2022-07-27 08:54:37 -05:00
// swagger:route DELETE /alert-notifications/uid/{notification_channel_uid} legacy_alerts_notification_channels deleteAlertNotificationChannelByUID
//
// Delete alert notification by UID.
//
// Deletes an existing notification channel identified by UID.
//
// Responses:
// 200: deleteAlertNotificationChannelResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2022-02-04 06:41:15 -06:00
func ( hs * HTTPServer ) DeleteAlertNotificationByUID ( c * models . ReqContext ) response . Response {
2019-08-12 13:03:48 -05:00
cmd := models . DeleteAlertNotificationWithUidCommand {
2022-08-11 06:28:55 -05:00
OrgId : c . OrgID ,
2021-10-11 07:30:59 -05:00
Uid : web . Params ( c . Req ) [ ":uid" ] ,
2019-03-26 06:37:02 -05:00
}
2022-02-16 11:54:29 -06:00
if err := hs . AlertNotificationService . DeleteAlertNotificationWithUid ( c . Req . Context ( ) , & cmd ) ; err != nil {
2020-11-19 06:34:28 -06:00
if errors . Is ( err , models . ErrAlertNotificationNotFound ) {
2021-01-15 07:43:20 -06:00
return response . Error ( 404 , err . Error ( ) , nil )
2020-09-11 11:04:43 -05:00
}
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to delete alert notification" , err )
2019-03-26 06:37:02 -05:00
}
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , util . DynMap {
2020-09-11 11:04:43 -05:00
"message" : "Notification deleted" ,
"id" : cmd . DeletedAlertNotificationId ,
} )
2019-03-26 06:37:02 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route POST /alert-notifications/test legacy_alerts_notification_channels notificationChannelTest
//
// Test notification channel.
//
// Sends a test notification to the channel.
//
// Responses:
// 200: okResponse
// 400: badRequestError
// 401: unauthorisedError
// 403: forbiddenError
// 412: SMTPNotEnabledError
// 500: internalServerError
2022-02-04 06:41:15 -06:00
func ( hs * HTTPServer ) NotificationTest ( c * models . ReqContext ) response . Response {
2021-11-29 03:18:01 -06:00
dto := dtos . NotificationTestCommand { }
if err := web . Bind ( c . Req , & dto ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2016-09-05 07:43:53 -05:00
cmd := & alerting . NotificationTestCommand {
2022-08-11 06:28:55 -05:00
OrgID : c . OrgID ,
2020-07-08 03:17:05 -05:00
ID : dto . ID ,
Name : dto . Name ,
Type : dto . Type ,
Settings : dto . Settings ,
SecureSettings : dto . SecureSettings ,
2016-09-05 07:43:53 -05:00
}
2022-04-08 07:30:25 -05:00
if err := hs . AlertNotificationService . HandleNotificationTestCommand ( c . Req . Context ( ) , cmd ) ; err != nil {
2023-01-17 13:47:31 -06:00
if errors . Is ( err , notifications . ErrSmtpNotEnabled ) {
2021-01-15 07:43:20 -06:00
return response . Error ( 412 , err . Error ( ) , err )
2017-04-25 06:16:37 -05:00
}
2021-04-22 09:00:21 -05:00
var alertingErr alerting . ValidationError
if errors . As ( err , & alertingErr ) {
return response . Error ( 400 , err . Error ( ) , err )
}
2021-01-15 07:43:20 -06:00
return response . Error ( 500 , "Failed to send alert notifications" , err )
2016-09-05 07:43:53 -05:00
}
2021-01-15 07:43:20 -06:00
return response . Success ( "Test notification sent" )
2016-09-05 07:43:53 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route POST /alerts/{alert_id}/pause legacy_alerts pauseAlert
//
// Pause/unpause alert by id.
//
// Responses:
// 200: pauseAlertResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2022-07-12 02:01:31 -05:00
func ( hs * HTTPServer ) PauseAlert ( legacyAlertingEnabled * bool ) func ( c * models . ReqContext ) response . Response {
if legacyAlertingEnabled == nil || ! * legacyAlertingEnabled {
return func ( _ * models . ReqContext ) response . Response {
return response . Error ( http . StatusBadRequest , "legacy alerting is disabled, so this call has no effect." , nil )
}
2018-01-30 07:41:25 -06:00
}
2022-07-12 02:01:31 -05:00
return func ( c * models . ReqContext ) response . Response {
dto := dtos . PauseAlertCommand { }
if err := web . Bind ( c . Req , & dto ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
alertID , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":alertId" ] , 10 , 64 )
2018-01-30 07:41:25 -06:00
if err != nil {
2022-07-12 02:01:31 -05:00
return response . Error ( http . StatusBadRequest , "alertId is invalid" , err )
2018-01-30 07:41:25 -06:00
}
2022-07-12 02:01:31 -05:00
result := make ( map [ string ] interface { } )
result [ "alertId" ] = alertID
2018-01-30 07:41:25 -06:00
2022-07-12 02:01:31 -05:00
query := models . GetAlertByIdQuery { Id : alertID }
2022-08-03 10:17:26 -05:00
if err := hs . AlertEngine . AlertStore . GetAlertById ( c . Req . Context ( ) , & query ) ; err != nil {
2022-07-12 02:01:31 -05:00
return response . Error ( 500 , "Get Alert failed" , err )
}
2018-01-30 07:41:25 -06:00
2022-12-15 08:34:17 -06:00
guardian , err := guardian . New ( c . Req . Context ( ) , query . Result . DashboardId , c . OrgID , c . SignedInUser )
if err != nil {
return response . ErrOrFallback ( http . StatusInternalServerError , "Error while creating permission guardian" , err )
}
2022-07-12 02:01:31 -05:00
if canEdit , err := guardian . CanEdit ( ) ; err != nil || ! canEdit {
if err != nil {
return response . Error ( 500 , "Error while checking permissions for Alert" , err )
}
2020-01-09 04:18:51 -06:00
2022-07-12 02:01:31 -05:00
return response . Error ( 403 , "Access denied to this dashboard and alert" , nil )
}
2016-10-10 07:26:09 -05:00
2022-07-12 02:01:31 -05:00
// Alert state validation
if query . Result . State != models . AlertStatePaused && ! dto . Paused {
result [ "state" ] = "un-paused"
result [ "message" ] = "Alert is already un-paused"
return response . JSON ( http . StatusOK , result )
} else if query . Result . State == models . AlertStatePaused && dto . Paused {
result [ "state" ] = models . AlertStatePaused
result [ "message" ] = "Alert is already paused"
return response . JSON ( http . StatusOK , result )
}
2016-10-10 07:26:09 -05:00
2022-07-12 02:01:31 -05:00
cmd := models . PauseAlertCommand {
2022-08-11 06:28:55 -05:00
OrgId : c . OrgID ,
2022-07-12 02:01:31 -05:00
AlertIds : [ ] int64 { alertID } ,
Paused : dto . Paused ,
}
2016-10-10 07:26:09 -05:00
2022-08-03 10:17:26 -05:00
if err := hs . AlertEngine . AlertStore . PauseAlert ( c . Req . Context ( ) , & cmd ) ; err != nil {
2022-07-12 02:01:31 -05:00
return response . Error ( 500 , "" , err )
}
resp := models . AlertStateUnknown
pausedState := "un-paused"
if cmd . Paused {
resp = models . AlertStatePaused
pausedState = "paused"
}
result [ "state" ] = resp
result [ "message" ] = "Alert " + pausedState
return response . JSON ( http . StatusOK , result )
}
2016-10-10 07:26:09 -05:00
}
2016-12-15 10:01:45 -06:00
2022-07-27 08:54:37 -05:00
// swagger:route POST /admin/pause-all-alerts admin pauseAllAlerts
//
// Pause/unpause all (legacy) alerts.
//
// Security:
// - basic:
//
// Responses:
// 200: pauseAlertsResponse
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
2022-07-12 02:01:31 -05:00
func ( hs * HTTPServer ) PauseAllAlerts ( legacyAlertingEnabled * bool ) func ( c * models . ReqContext ) response . Response {
if legacyAlertingEnabled == nil || ! * legacyAlertingEnabled {
return func ( _ * models . ReqContext ) response . Response {
return response . Error ( http . StatusBadRequest , "legacy alerting is disabled, so this call has no effect." , nil )
}
2016-12-15 10:01:45 -06:00
}
2022-07-12 02:01:31 -05:00
return func ( c * models . ReqContext ) response . Response {
dto := dtos . PauseAllAlertsCommand { }
if err := web . Bind ( c . Req , & dto ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
updateCmd := models . PauseAllAlertCommand {
Paused : dto . Paused ,
}
2016-12-15 10:01:45 -06:00
2022-08-03 10:17:26 -05:00
if err := hs . AlertEngine . AlertStore . PauseAllAlerts ( c . Req . Context ( ) , & updateCmd ) ; err != nil {
2022-07-12 02:01:31 -05:00
return response . Error ( 500 , "Failed to pause alerts" , err )
}
2016-12-15 10:01:45 -06:00
2022-07-12 02:01:31 -05:00
resp := models . AlertStatePending
pausedState := "un paused"
if updateCmd . Paused {
resp = models . AlertStatePaused
pausedState = "paused"
}
2016-12-15 10:01:45 -06:00
2022-07-12 02:01:31 -05:00
result := map [ string ] interface { } {
"state" : resp ,
"message" : "alerts " + pausedState ,
"alertsAffected" : updateCmd . ResultCount ,
}
return response . JSON ( http . StatusOK , result )
}
2016-12-15 10:01:45 -06:00
}
2022-07-27 08:54:37 -05:00
// swagger:parameters pauseAllAlerts
type PauseAllAlertsParams struct {
// in:body
// required:true
Body dtos . PauseAllAlertsCommand ` json:"body" `
}
// swagger:parameters deleteAlertNotificationChannel
type DeleteAlertNotificationChannelParams struct {
// in:path
// required:true
NotificationID int64 ` json:"notification_channel_id" `
}
// swagger:parameters getAlertNotificationChannelByID
type GetAlertNotificationChannelByIDParams struct {
// in:path
// required:true
NotificationID int64 ` json:"notification_channel_id" `
}
// swagger:parameters deleteAlertNotificationChannelByUID
type DeleteAlertNotificationChannelByUIDParams struct {
// in:path
// required:true
NotificationUID string ` json:"notification_channel_uid" `
}
// swagger:parameters getAlertNotificationChannelByUID
type GetAlertNotificationChannelByUIDParams struct {
// in:path
// required:true
NotificationUID string ` json:"notification_channel_uid" `
}
// swagger:parameters notificationChannelTest
type NotificationChannelTestParams struct {
// in:body
// required:true
Body dtos . NotificationTestCommand ` json:"body" `
}
// swagger:parameters createAlertNotificationChannel
type CreateAlertNotificationChannelParams struct {
// in:body
// required:true
Body models . CreateAlertNotificationCommand ` json:"body" `
}
// swagger:parameters updateAlertNotificationChannel
type UpdateAlertNotificationChannelParams struct {
// in:body
// required:true
Body models . UpdateAlertNotificationCommand ` json:"body" `
// in:path
// required:true
NotificationID int64 ` json:"notification_channel_id" `
}
// swagger:parameters updateAlertNotificationChannelByUID
type UpdateAlertNotificationChannelByUIDParams struct {
// in:body
// required:true
Body models . UpdateAlertNotificationWithUidCommand ` json:"body" `
// in:path
// required:true
NotificationUID string ` json:"notification_channel_uid" `
}
// swagger:parameters getAlertByID
type GetAlertByIDParams struct {
// in:path
// required:true
AlertID string ` json:"alert_id" `
}
// swagger:parameters pauseAlert
type PauseAlertParams struct {
// in:path
// required:true
AlertID string ` json:"alert_id" `
// in:body
// required:true
Body dtos . PauseAlertCommand ` json:"body" `
}
// swagger:parameters getAlerts
type GetAlertsParams struct {
// Limit response to alerts in specified dashboard(s). You can specify multiple dashboards.
// in:query
// required:false
DashboardID [ ] string ` json:"dashboardId" `
// Limit response to alert for a specified panel on a dashboard.
// in:query
// required:false
PanelID int64 ` json:"panelId" `
// Limit response to alerts having a name like this value.
// in:query
// required: false
Query string ` json:"query" `
// Return alerts with one or more of the following alert states
// in:query
// required:false
// Description:
// * `all`
// * `no_data`
// * `paused`
// * `alerting`
// * `ok`
// * `pending`
// * `unknown`
// enum: all,no_data,paused,alerting,ok,pending,unknown
State string ` json:"state" `
// Limit response to X number of alerts.
// in:query
// required:false
Limit int64 ` json:"limit" `
// Limit response to alerts of dashboards in specified folder(s). You can specify multiple folders
// in:query
// required:false
// type array
// collectionFormat: multi
FolderID [ ] string ` json:"folderId" `
// Limit response to alerts having a dashboard name like this value./ Limit response to alerts having a dashboard name like this value.
// in:query
// required:false
DashboardQuery string ` json:"dashboardQuery" `
// Limit response to alerts of dashboards with specified tags. To do an “AND” filtering with multiple tags, specify the tags parameter multiple times
// in:query
// required:false
// type: array
// collectionFormat: multi
DashboardTag [ ] string ` json:"dashboardTag" `
}
// swagger:parameters testAlert
type TestAlertParams struct {
// in:body
Body dtos . AlertTestCommand ` json:"body" `
}
// swagger:parameters getDashboardStates
type GetDashboardStatesParams struct {
// in:query
// required: true
DashboardID int64 ` json:"dashboardId" `
}
// swagger:response pauseAlertsResponse
type PauseAllAlertsResponse struct {
// in:body
Body struct {
// AlertsAffected is the number of the affected alerts.
// required: true
AlertsAffected int64 ` json:"alertsAffected" `
// required: true
Message string ` json:"message" `
// Alert result state
// required true
State string ` json:"state" `
} ` json:"body" `
}
// swagger:response getAlertNotificationChannelsResponse
type GetAlertNotificationChannelsResponse struct {
// The response message
// in: body
Body [ ] * dtos . AlertNotification ` json:"body" `
}
// swagger:response getAlertNotificationLookupResponse
type LookupAlertNotificationChannelsResponse struct {
// The response message
// in: body
Body [ ] * dtos . AlertNotificationLookup ` json:"body" `
}
// swagger:response getAlertNotificationChannelResponse
type GetAlertNotificationChannelResponse struct {
// The response message
// in: body
Body * dtos . AlertNotification ` json:"body" `
}
// swagger:response deleteAlertNotificationChannelResponse
type DeleteAlertNotificationChannelResponse struct {
// The response message
// in: body
Body struct {
// ID Identifier of the deleted notification channel.
// required: true
// example: 65
ID int64 ` json:"id" `
// Message Message of the deleted notificatiton channel.
// required: true
Message string ` json:"message" `
} ` json:"body" `
}
// swagger:response SMTPNotEnabledError
type SMTPNotEnabledError PreconditionFailedError
// swagger:response getAlertsResponse
type GetAlertsResponse struct {
// The response message
// in: body
Body [ ] * models . AlertListItemDTO ` json:"body" `
}
// swagger:response getAlertResponse
type GetAlertResponse struct {
// The response message
// in: body
Body * models . Alert ` json:"body" `
}
// swagger:response pauseAlertResponse
type PauseAlertResponse struct {
// in:body
Body struct {
// required: true
AlertID int64 ` json:"alertId" `
// required: true
Message string ` json:"message" `
// Alert result state
// required true
State string ` json:"state" `
} ` json:"body" `
}
// swagger:response testAlertResponse
type TestAlertResponse struct {
// The response message
// in: body
Body * dtos . AlertTestResult ` json:"body" `
}
// swagger:response getDashboardStatesResponse
type GetDashboardStatesResponse struct {
// The response message
// in: body
Body [ ] * models . AlertStateInfoDTO ` json:"body" `
}