The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/pkg/api/alerting.go

1046 lines
32 KiB

package api
import (
Encryption: Refactor securejsondata.SecureJsonData to stop relying on global functions (#38865) * Encryption: Add support to encrypt/decrypt sjd * Add datasources.Service as a proxy to datasources db operations * Encrypt ds.SecureJsonData before calling SQLStore * Move ds cache code into ds service * Fix tlsmanager tests * Fix pluginproxy tests * Remove some securejsondata.GetEncryptedJsonData usages * Add pluginsettings.Service as a proxy for plugin settings db operations * Add AlertNotificationService as a proxy for alert notification db operations * Remove some securejsondata.GetEncryptedJsonData usages * Remove more securejsondata.GetEncryptedJsonData usages * Fix lint errors * Minor fixes * Remove encryption global functions usages from ngalert * Fix lint errors * Minor fixes * Minor fixes * Remove securejsondata.DecryptedValue usage * Refactor the refactor * Remove securejsondata.DecryptedValue usage * Move securejsondata to migrations package * Move securejsondata to migrations package * Minor fix * Fix integration test * Fix integration tests * Undo undesired changes * Fix tests * Add context.Context into encryption methods * Fix tests * Fix tests * Fix tests * Trigger CI * Fix test * Add names to params of encryption service interface * Remove bus from CacheServiceImpl * Add logging * Add keys to logger Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Add missing key to logger Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Undo changes in markdown files * Fix formatting * Add context to secrets service * Rename decryptSecureJsonData to decryptSecureJsonDataFn * Name args in GetDecryptedValueFn * Add template back to NewAlertmanagerNotifier * Copy GetDecryptedValueFn to ngalert * Add logging to pluginsettings * Fix pluginsettings test Co-authored-by: Tania B <yalyna.ts@gmail.com> Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
4 years ago
"context"
"errors"
"fmt"
"net/http"
"strconv"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/services/alerting"
alertmodels "github.com/grafana/grafana/pkg/services/alerting/models"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels_config"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/grafana/grafana/pkg/services/search"
"github.com/grafana/grafana/pkg/services/search/model"
Encryption: Refactor securejsondata.SecureJsonData to stop relying on global functions (#38865) * Encryption: Add support to encrypt/decrypt sjd * Add datasources.Service as a proxy to datasources db operations * Encrypt ds.SecureJsonData before calling SQLStore * Move ds cache code into ds service * Fix tlsmanager tests * Fix pluginproxy tests * Remove some securejsondata.GetEncryptedJsonData usages * Add pluginsettings.Service as a proxy for plugin settings db operations * Add AlertNotificationService as a proxy for alert notification db operations * Remove some securejsondata.GetEncryptedJsonData usages * Remove more securejsondata.GetEncryptedJsonData usages * Fix lint errors * Minor fixes * Remove encryption global functions usages from ngalert * Fix lint errors * Minor fixes * Minor fixes * Remove securejsondata.DecryptedValue usage * Refactor the refactor * Remove securejsondata.DecryptedValue usage * Move securejsondata to migrations package * Move securejsondata to migrations package * Minor fix * Fix integration test * Fix integration tests * Undo undesired changes * Fix tests * Add context.Context into encryption methods * Fix tests * Fix tests * Fix tests * Trigger CI * Fix test * Add names to params of encryption service interface * Remove bus from CacheServiceImpl * Add logging * Add keys to logger Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Add missing key to logger Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Undo changes in markdown files * Fix formatting * Add context to secrets service * Rename decryptSecureJsonData to decryptSecureJsonDataFn * Name args in GetDecryptedValueFn * Add template back to NewAlertmanagerNotifier * Copy GetDecryptedValueFn to ngalert * Add logging to pluginsettings * Fix pluginsettings test Co-authored-by: Tania B <yalyna.ts@gmail.com> Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
4 years ago
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/web"
)
func (hs *HTTPServer) ValidateOrgAlert(c *contextmodel.ReqContext) {
id, err := strconv.ParseInt(web.Params(c.Req)[":alertId"], 10, 64)
if err != nil {
c.JsonApiErr(http.StatusBadRequest, "alertId is invalid", nil)
return
}
query := alertmodels.GetAlertByIdQuery{ID: id}
res, err := hs.AlertEngine.AlertStore.GetAlertById(c.Req.Context(), &query)
if err != nil {
c.JsonApiErr(404, "Alert not found", nil)
return
}
if c.OrgID != res.OrgID {
c.JsonApiErr(403, "You are not allowed to edit/view alert", nil)
return
}
}
// swagger:route GET /alerts/states-for-dashboard legacy_alerts getDashboardStates
//
// Get alert states for a dashboard.
//
// Responses:
// Responses:
// 200: getDashboardStatesResponse
// 400: badRequestError
// 500: internalServerError
func (hs *HTTPServer) GetAlertStatesForDashboard(c *contextmodel.ReqContext) response.Response {
dashboardID := c.QueryInt64("dashboardId")
if dashboardID == 0 {
return response.Error(400, "Missing query parameter dashboardId", nil)
}
query := alertmodels.GetAlertStatesForDashboardQuery{
OrgID: c.OrgID,
DashboardID: c.QueryInt64("dashboardId"),
}
res, err := hs.AlertEngine.AlertStore.GetAlertStatesForDashboard(c.Req.Context(), &query)
if err != nil {
return response.Error(500, "Failed to fetch alert states", err)
}
return response.JSON(http.StatusOK, res)
}
// swagger:route GET /alerts legacy_alerts getAlerts
//
// Get legacy alerts.
//
// Responses:
// 200: getAlertsResponse
// 401: unauthorisedError
// 500: internalServerError
func (hs *HTTPServer) GetAlerts(c *contextmodel.ReqContext) response.Response {
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,
OrgId: c.OrgID,
DashboardIds: dashboardIDs,
Type: string(model.DashHitDB),
FolderIds: folderIDs,
Permission: dashboards.PERMISSION_VIEW,
}
hits, err := hs.SearchService.SearchHandler(c.Req.Context(), &searchQuery)
if err != nil {
return response.Error(500, "List alerts failed", err)
}
for _, d := range hits {
if d.Type == model.DashHitDB && d.ID > 0 {
dashboardIDs = append(dashboardIDs, d.ID)
}
}
// if we didn't find any dashboards, return empty result
if len(dashboardIDs) == 0 {
return response.JSON(http.StatusOK, []*alertmodels.AlertListItemDTO{})
}
}
query := alertmodels.GetAlertsQuery{
OrgID: c.OrgID,
DashboardIDs: dashboardIDs,
PanelID: c.QueryInt64("panelId"),
Limit: c.QueryInt64("limit"),
User: c.SignedInUser,
Query: c.Query("query"),
}
9 years ago
states := c.QueryStrings("state")
if len(states) > 0 {
query.State = states
}
res, err := hs.AlertEngine.AlertStore.HandleAlertsQuery(c.Req.Context(), &query)
if err != nil {
return response.Error(500, "List alerts failed", err)
}
for _, alert := range res {
alert.URL = dashboards.GetDashboardURL(alert.DashboardUID, alert.DashboardSlug)
}
return response.JSON(http.StatusOK, res)
}
// swagger:route POST /alerts/test legacy_alerts testAlert
//
// Test alert.
//
// Responses:
// 200: testAlertResponse
// 400: badRequestError
// 422: unprocessableEntityError
// 403: forbiddenError
// 500: internalServerError
func (hs *HTTPServer) AlertTest(c *contextmodel.ReqContext) response.Response {
dto := dtos.AlertTestCommand{}
if err := web.Bind(c.Req, &dto); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if _, idErr := dto.Dashboard.Get("id").Int64(); idErr != nil {
return response.Error(400, "The dashboard needs to be saved at least once before you can test an alert rule", nil)
}
res, err := hs.AlertEngine.AlertTest(c.OrgID, dto.Dashboard, dto.PanelId, c.SignedInUser)
if err != nil {
var validationErr alerting.ValidationError
if errors.As(err, &validationErr) {
return response.Error(422, validationErr.Error(), nil)
}
if errors.Is(err, datasources.ErrDataSourceAccessDenied) {
return response.Error(403, "Access denied to datasource", err)
}
return response.Error(500, "Failed to test rule", err)
}
dtoRes := &dtos.AlertTestResult{
Firing: res.Firing,
ConditionEvals: res.ConditionEvals,
State: res.Rule.State,
}
if res.Error != nil {
dtoRes.Error = res.Error.Error()
}
for _, log := range res.Logs {
dtoRes.Logs = append(dtoRes.Logs, &dtos.AlertTestResultLog{Message: log.Message, Data: log.Data})
}
for _, match := range res.EvalMatches {
dtoRes.EvalMatches = append(dtoRes.EvalMatches, &dtos.EvalMatch{Metric: match.Metric, Value: match.Value})
}
dtoRes.TimeMs = fmt.Sprintf("%1.3fms", res.GetDurationMs())
return response.JSON(http.StatusOK, dtoRes)
}
// 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
func (hs *HTTPServer) GetAlert(c *contextmodel.ReqContext) response.Response {
id, err := strconv.ParseInt(web.Params(c.Req)[":alertId"], 10, 64)
if err != nil {
return response.Error(http.StatusBadRequest, "alertId is invalid", err)
}
query := alertmodels.GetAlertByIdQuery{ID: id}
res, err := hs.AlertEngine.AlertStore.GetAlertById(c.Req.Context(), &query)
if err != nil {
return response.Error(500, "List alerts failed", err)
}
return response.JSON(http.StatusOK, &res)
}
func (hs *HTTPServer) GetAlertNotifiers(ngalertEnabled bool) func(*contextmodel.ReqContext) response.Response {
return func(_ *contextmodel.ReqContext) response.Response {
if ngalertEnabled {
return response.JSON(http.StatusOK, channels_config.GetAvailableNotifiers())
}
// 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.
return response.JSON(http.StatusOK, alerting.GetNotifiers())
}
}
// swagger:route GET /alert-notifications/lookup legacy_alerts_notification_channels getAlertNotificationLookup
//
// Get all notification channels (lookup).
//
// 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
func (hs *HTTPServer) GetAlertNotificationLookup(c *contextmodel.ReqContext) response.Response {
alertNotifications, err := hs.getAlertNotificationsInternal(c)
if err != nil {
return response.Error(500, "Failed to get alert notifications", err)
}
result := make([]*dtos.AlertNotificationLookup, 0)
for _, notification := range alertNotifications {
result = append(result, dtos.NewAlertNotificationLookup(notification))
}
return response.JSON(http.StatusOK, result)
}
// 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
func (hs *HTTPServer) GetAlertNotifications(c *contextmodel.ReqContext) response.Response {
alertNotifications, err := hs.getAlertNotificationsInternal(c)
if err != nil {
return response.Error(500, "Failed to get alert notifications", err)
}
result := make([]*dtos.AlertNotification, 0)
for _, notification := range alertNotifications {
result = append(result, dtos.NewAlertNotification(notification))
}
return response.JSON(http.StatusOK, result)
}
func (hs *HTTPServer) getAlertNotificationsInternal(c *contextmodel.ReqContext) ([]*alertmodels.AlertNotification, error) {
query := &alertmodels.GetAllAlertNotificationsQuery{OrgID: c.OrgID}
return hs.AlertNotificationService.GetAllAlertNotifications(c.Req.Context(), query)
}
// 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
func (hs *HTTPServer) GetAlertNotificationByID(c *contextmodel.ReqContext) response.Response {
notificationId, err := strconv.ParseInt(web.Params(c.Req)[":notificationId"], 10, 64)
if err != nil {
return response.Error(http.StatusBadRequest, "notificationId is invalid", err)
}
query := &alertmodels.GetAlertNotificationsQuery{
OrgID: c.OrgID,
ID: notificationId,
}
if query.ID == 0 {
return response.Error(404, "Alert notification not found", nil)
}
res, err := hs.AlertNotificationService.GetAlertNotifications(c.Req.Context(), query)
if err != nil {
return response.Error(500, "Failed to get alert notifications", err)
}
if res == nil {
return response.Error(404, "Alert notification not found", nil)
}
return response.JSON(http.StatusOK, dtos.NewAlertNotification(res))
}
// swagger:route GET /alert-notifications/uid/{notification_channel_uid} legacy_alerts_notification_channels getAlertNotificationChannelByUID
//
// Get notification channel by UID.
//
// Returns the notification channel given the notification channel UID.
//
// Responses:
// 200: getAlertNotificationChannelResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
func (hs *HTTPServer) GetAlertNotificationByUID(c *contextmodel.ReqContext) response.Response {
query := &alertmodels.GetAlertNotificationsWithUidQuery{
OrgID: c.OrgID,
UID: web.Params(c.Req)[":uid"],
}
if query.UID == "" {
return response.Error(404, "Alert notification not found", nil)
}
res, err := hs.AlertNotificationService.GetAlertNotificationsWithUid(c.Req.Context(), query)
if err != nil {
return response.Error(500, "Failed to get alert notifications", err)
}
if res == nil {
return response.Error(404, "Alert notification not found", nil)
}
return response.JSON(http.StatusOK, dtos.NewAlertNotification(res))
}
// 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
func (hs *HTTPServer) CreateAlertNotification(c *contextmodel.ReqContext) response.Response {
cmd := alertmodels.CreateAlertNotificationCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.OrgID = c.OrgID
res, err := hs.AlertNotificationService.CreateAlertNotificationCommand(c.Req.Context(), &cmd)
if err != nil {
if errors.Is(err, alertmodels.ErrAlertNotificationWithSameNameExists) || errors.Is(err, alertmodels.ErrAlertNotificationWithSameUIDExists) {
return response.Error(409, "Failed to create alert notification", err)
}
var alertingErr alerting.ValidationError
if errors.As(err, &alertingErr) {
return response.Error(400, err.Error(), err)
}
return response.Error(500, "Failed to create alert notification", err)
}
return response.JSON(http.StatusOK, dtos.NewAlertNotification(res))
}
// 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
func (hs *HTTPServer) UpdateAlertNotification(c *contextmodel.ReqContext) response.Response {
cmd := alertmodels.UpdateAlertNotificationCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.OrgID = c.OrgID
Encryption: Refactor securejsondata.SecureJsonData to stop relying on global functions (#38865) * Encryption: Add support to encrypt/decrypt sjd * Add datasources.Service as a proxy to datasources db operations * Encrypt ds.SecureJsonData before calling SQLStore * Move ds cache code into ds service * Fix tlsmanager tests * Fix pluginproxy tests * Remove some securejsondata.GetEncryptedJsonData usages * Add pluginsettings.Service as a proxy for plugin settings db operations * Add AlertNotificationService as a proxy for alert notification db operations * Remove some securejsondata.GetEncryptedJsonData usages * Remove more securejsondata.GetEncryptedJsonData usages * Fix lint errors * Minor fixes * Remove encryption global functions usages from ngalert * Fix lint errors * Minor fixes * Minor fixes * Remove securejsondata.DecryptedValue usage * Refactor the refactor * Remove securejsondata.DecryptedValue usage * Move securejsondata to migrations package * Move securejsondata to migrations package * Minor fix * Fix integration test * Fix integration tests * Undo undesired changes * Fix tests * Add context.Context into encryption methods * Fix tests * Fix tests * Fix tests * Trigger CI * Fix test * Add names to params of encryption service interface * Remove bus from CacheServiceImpl * Add logging * Add keys to logger Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Add missing key to logger Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Undo changes in markdown files * Fix formatting * Add context to secrets service * Rename decryptSecureJsonData to decryptSecureJsonDataFn * Name args in GetDecryptedValueFn * Add template back to NewAlertmanagerNotifier * Copy GetDecryptedValueFn to ngalert * Add logging to pluginsettings * Fix pluginsettings test Co-authored-by: Tania B <yalyna.ts@gmail.com> Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
4 years ago
err := hs.fillWithSecureSettingsData(c.Req.Context(), &cmd)
if err != nil {
return response.Error(500, "Failed to update alert notification", err)
}
if _, err := hs.AlertNotificationService.UpdateAlertNotification(c.Req.Context(), &cmd); err != nil {
if errors.Is(err, alertmodels.ErrAlertNotificationNotFound) {
return response.Error(404, err.Error(), err)
}
var alertingErr alerting.ValidationError
if errors.As(err, &alertingErr) {
return response.Error(400, err.Error(), err)
}
return response.Error(500, "Failed to update alert notification", err)
}
query := alertmodels.GetAlertNotificationsQuery{
OrgID: c.OrgID,
ID: cmd.ID,
}
res, err := hs.AlertNotificationService.GetAlertNotifications(c.Req.Context(), &query)
if err != nil {
return response.Error(500, "Failed to get alert notification", err)
}
return response.JSON(http.StatusOK, dtos.NewAlertNotification(res))
}
// 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
func (hs *HTTPServer) UpdateAlertNotificationByUID(c *contextmodel.ReqContext) response.Response {
cmd := alertmodels.UpdateAlertNotificationWithUidCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.OrgID = c.OrgID
cmd.UID = web.Params(c.Req)[":uid"]
Encryption: Refactor securejsondata.SecureJsonData to stop relying on global functions (#38865) * Encryption: Add support to encrypt/decrypt sjd * Add datasources.Service as a proxy to datasources db operations * Encrypt ds.SecureJsonData before calling SQLStore * Move ds cache code into ds service * Fix tlsmanager tests * Fix pluginproxy tests * Remove some securejsondata.GetEncryptedJsonData usages * Add pluginsettings.Service as a proxy for plugin settings db operations * Add AlertNotificationService as a proxy for alert notification db operations * Remove some securejsondata.GetEncryptedJsonData usages * Remove more securejsondata.GetEncryptedJsonData usages * Fix lint errors * Minor fixes * Remove encryption global functions usages from ngalert * Fix lint errors * Minor fixes * Minor fixes * Remove securejsondata.DecryptedValue usage * Refactor the refactor * Remove securejsondata.DecryptedValue usage * Move securejsondata to migrations package * Move securejsondata to migrations package * Minor fix * Fix integration test * Fix integration tests * Undo undesired changes * Fix tests * Add context.Context into encryption methods * Fix tests * Fix tests * Fix tests * Trigger CI * Fix test * Add names to params of encryption service interface * Remove bus from CacheServiceImpl * Add logging * Add keys to logger Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Add missing key to logger Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Undo changes in markdown files * Fix formatting * Add context to secrets service * Rename decryptSecureJsonData to decryptSecureJsonDataFn * Name args in GetDecryptedValueFn * Add template back to NewAlertmanagerNotifier * Copy GetDecryptedValueFn to ngalert * Add logging to pluginsettings * Fix pluginsettings test Co-authored-by: Tania B <yalyna.ts@gmail.com> Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
4 years ago
err := hs.fillWithSecureSettingsDataByUID(c.Req.Context(), &cmd)
if err != nil {
return response.Error(500, "Failed to update alert notification", err)
}
if _, err := hs.AlertNotificationService.UpdateAlertNotificationWithUid(c.Req.Context(), &cmd); err != nil {
if errors.Is(err, alertmodels.ErrAlertNotificationNotFound) {
return response.Error(404, err.Error(), nil)
}
return response.Error(500, "Failed to update alert notification", err)
}
query := alertmodels.GetAlertNotificationsWithUidQuery{
OrgID: cmd.OrgID,
UID: cmd.UID,
}
res, err := hs.AlertNotificationService.GetAlertNotificationsWithUid(c.Req.Context(), &query)
if err != nil {
return response.Error(500, "Failed to get alert notification", err)
}
return response.JSON(http.StatusOK, dtos.NewAlertNotification(res))
}
func (hs *HTTPServer) fillWithSecureSettingsData(ctx context.Context, cmd *alertmodels.UpdateAlertNotificationCommand) error {
if len(cmd.SecureSettings) == 0 {
return nil
}
query := &alertmodels.GetAlertNotificationsQuery{
OrgID: cmd.OrgID,
ID: cmd.ID,
}
res, err := hs.AlertNotificationService.GetAlertNotifications(ctx, query)
if err != nil {
Encryption: Refactor securejsondata.SecureJsonData to stop relying on global functions (#38865) * Encryption: Add support to encrypt/decrypt sjd * Add datasources.Service as a proxy to datasources db operations * Encrypt ds.SecureJsonData before calling SQLStore * Move ds cache code into ds service * Fix tlsmanager tests * Fix pluginproxy tests * Remove some securejsondata.GetEncryptedJsonData usages * Add pluginsettings.Service as a proxy for plugin settings db operations * Add AlertNotificationService as a proxy for alert notification db operations * Remove some securejsondata.GetEncryptedJsonData usages * Remove more securejsondata.GetEncryptedJsonData usages * Fix lint errors * Minor fixes * Remove encryption global functions usages from ngalert * Fix lint errors * Minor fixes * Minor fixes * Remove securejsondata.DecryptedValue usage * Refactor the refactor * Remove securejsondata.DecryptedValue usage * Move securejsondata to migrations package * Move securejsondata to migrations package * Minor fix * Fix integration test * Fix integration tests * Undo undesired changes * Fix tests * Add context.Context into encryption methods * Fix tests * Fix tests * Fix tests * Trigger CI * Fix test * Add names to params of encryption service interface * Remove bus from CacheServiceImpl * Add logging * Add keys to logger Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Add missing key to logger Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Undo changes in markdown files * Fix formatting * Add context to secrets service * Rename decryptSecureJsonData to decryptSecureJsonDataFn * Name args in GetDecryptedValueFn * Add template back to NewAlertmanagerNotifier * Copy GetDecryptedValueFn to ngalert * Add logging to pluginsettings * Fix pluginsettings test Co-authored-by: Tania B <yalyna.ts@gmail.com> Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
4 years ago
return err
}
secureSettings, err := hs.EncryptionService.DecryptJsonData(ctx, res.SecureSettings, setting.SecretKey)
Encryption: Refactor securejsondata.SecureJsonData to stop relying on global functions (#38865) * Encryption: Add support to encrypt/decrypt sjd * Add datasources.Service as a proxy to datasources db operations * Encrypt ds.SecureJsonData before calling SQLStore * Move ds cache code into ds service * Fix tlsmanager tests * Fix pluginproxy tests * Remove some securejsondata.GetEncryptedJsonData usages * Add pluginsettings.Service as a proxy for plugin settings db operations * Add AlertNotificationService as a proxy for alert notification db operations * Remove some securejsondata.GetEncryptedJsonData usages * Remove more securejsondata.GetEncryptedJsonData usages * Fix lint errors * Minor fixes * Remove encryption global functions usages from ngalert * Fix lint errors * Minor fixes * Minor fixes * Remove securejsondata.DecryptedValue usage * Refactor the refactor * Remove securejsondata.DecryptedValue usage * Move securejsondata to migrations package * Move securejsondata to migrations package * Minor fix * Fix integration test * Fix integration tests * Undo undesired changes * Fix tests * Add context.Context into encryption methods * Fix tests * Fix tests * Fix tests * Trigger CI * Fix test * Add names to params of encryption service interface * Remove bus from CacheServiceImpl * Add logging * Add keys to logger Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Add missing key to logger Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Undo changes in markdown files * Fix formatting * Add context to secrets service * Rename decryptSecureJsonData to decryptSecureJsonDataFn * Name args in GetDecryptedValueFn * Add template back to NewAlertmanagerNotifier * Copy GetDecryptedValueFn to ngalert * Add logging to pluginsettings * Fix pluginsettings test Co-authored-by: Tania B <yalyna.ts@gmail.com> Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
4 years ago
if err != nil {
return err
}
for k, v := range secureSettings {
if _, ok := cmd.SecureSettings[k]; !ok {
cmd.SecureSettings[k] = v
}
}
return nil
}
func (hs *HTTPServer) fillWithSecureSettingsDataByUID(ctx context.Context, cmd *alertmodels.UpdateAlertNotificationWithUidCommand) error {
if len(cmd.SecureSettings) == 0 {
return nil
}
query := &alertmodels.GetAlertNotificationsWithUidQuery{
OrgID: cmd.OrgID,
UID: cmd.UID,
}
res, err := hs.AlertNotificationService.GetAlertNotificationsWithUid(ctx, query)
if err != nil {
Encryption: Refactor securejsondata.SecureJsonData to stop relying on global functions (#38865) * Encryption: Add support to encrypt/decrypt sjd * Add datasources.Service as a proxy to datasources db operations * Encrypt ds.SecureJsonData before calling SQLStore * Move ds cache code into ds service * Fix tlsmanager tests * Fix pluginproxy tests * Remove some securejsondata.GetEncryptedJsonData usages * Add pluginsettings.Service as a proxy for plugin settings db operations * Add AlertNotificationService as a proxy for alert notification db operations * Remove some securejsondata.GetEncryptedJsonData usages * Remove more securejsondata.GetEncryptedJsonData usages * Fix lint errors * Minor fixes * Remove encryption global functions usages from ngalert * Fix lint errors * Minor fixes * Minor fixes * Remove securejsondata.DecryptedValue usage * Refactor the refactor * Remove securejsondata.DecryptedValue usage * Move securejsondata to migrations package * Move securejsondata to migrations package * Minor fix * Fix integration test * Fix integration tests * Undo undesired changes * Fix tests * Add context.Context into encryption methods * Fix tests * Fix tests * Fix tests * Trigger CI * Fix test * Add names to params of encryption service interface * Remove bus from CacheServiceImpl * Add logging * Add keys to logger Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Add missing key to logger Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Undo changes in markdown files * Fix formatting * Add context to secrets service * Rename decryptSecureJsonData to decryptSecureJsonDataFn * Name args in GetDecryptedValueFn * Add template back to NewAlertmanagerNotifier * Copy GetDecryptedValueFn to ngalert * Add logging to pluginsettings * Fix pluginsettings test Co-authored-by: Tania B <yalyna.ts@gmail.com> Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
4 years ago
return err
}
secureSettings, err := hs.EncryptionService.DecryptJsonData(ctx, res.SecureSettings, setting.SecretKey)
Encryption: Refactor securejsondata.SecureJsonData to stop relying on global functions (#38865) * Encryption: Add support to encrypt/decrypt sjd * Add datasources.Service as a proxy to datasources db operations * Encrypt ds.SecureJsonData before calling SQLStore * Move ds cache code into ds service * Fix tlsmanager tests * Fix pluginproxy tests * Remove some securejsondata.GetEncryptedJsonData usages * Add pluginsettings.Service as a proxy for plugin settings db operations * Add AlertNotificationService as a proxy for alert notification db operations * Remove some securejsondata.GetEncryptedJsonData usages * Remove more securejsondata.GetEncryptedJsonData usages * Fix lint errors * Minor fixes * Remove encryption global functions usages from ngalert * Fix lint errors * Minor fixes * Minor fixes * Remove securejsondata.DecryptedValue usage * Refactor the refactor * Remove securejsondata.DecryptedValue usage * Move securejsondata to migrations package * Move securejsondata to migrations package * Minor fix * Fix integration test * Fix integration tests * Undo undesired changes * Fix tests * Add context.Context into encryption methods * Fix tests * Fix tests * Fix tests * Trigger CI * Fix test * Add names to params of encryption service interface * Remove bus from CacheServiceImpl * Add logging * Add keys to logger Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Add missing key to logger Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Undo changes in markdown files * Fix formatting * Add context to secrets service * Rename decryptSecureJsonData to decryptSecureJsonDataFn * Name args in GetDecryptedValueFn * Add template back to NewAlertmanagerNotifier * Copy GetDecryptedValueFn to ngalert * Add logging to pluginsettings * Fix pluginsettings test Co-authored-by: Tania B <yalyna.ts@gmail.com> Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
4 years ago
if err != nil {
return err
}
for k, v := range secureSettings {
if _, ok := cmd.SecureSettings[k]; !ok {
cmd.SecureSettings[k] = v
}
}
return nil
}
// 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
func (hs *HTTPServer) DeleteAlertNotification(c *contextmodel.ReqContext) response.Response {
notificationId, err := strconv.ParseInt(web.Params(c.Req)[":notificationId"], 10, 64)
if err != nil {
return response.Error(http.StatusBadRequest, "notificationId is invalid", err)
}
cmd := alertmodels.DeleteAlertNotificationCommand{
OrgID: c.OrgID,
ID: notificationId,
}
if err := hs.AlertNotificationService.DeleteAlertNotification(c.Req.Context(), &cmd); err != nil {
if errors.Is(err, alertmodels.ErrAlertNotificationNotFound) {
return response.Error(404, err.Error(), nil)
}
return response.Error(500, "Failed to delete alert notification", err)
}
return response.Success("Notification deleted")
}
// 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
func (hs *HTTPServer) DeleteAlertNotificationByUID(c *contextmodel.ReqContext) response.Response {
cmd := alertmodels.DeleteAlertNotificationWithUidCommand{
OrgID: c.OrgID,
UID: web.Params(c.Req)[":uid"],
}
if err := hs.AlertNotificationService.DeleteAlertNotificationWithUid(c.Req.Context(), &cmd); err != nil {
if errors.Is(err, alertmodels.ErrAlertNotificationNotFound) {
return response.Error(404, err.Error(), nil)
}
return response.Error(500, "Failed to delete alert notification", err)
}
return response.JSON(http.StatusOK, util.DynMap{
"message": "Notification deleted",
"id": cmd.DeletedAlertNotificationID,
})
}
// 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
func (hs *HTTPServer) NotificationTest(c *contextmodel.ReqContext) response.Response {
dto := dtos.NotificationTestCommand{}
if err := web.Bind(c.Req, &dto); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd := &alerting.NotificationTestCommand{
OrgID: c.OrgID,
ID: dto.ID,
Name: dto.Name,
Type: dto.Type,
Settings: dto.Settings,
SecureSettings: dto.SecureSettings,
}
if err := hs.AlertNotificationService.HandleNotificationTestCommand(c.Req.Context(), cmd); err != nil {
if errors.Is(err, notifications.ErrSmtpNotEnabled) {
return response.Error(412, err.Error(), err)
}
Slack: Use chat.postMessage API by default (#32511) * Slack: Use only chat.postMessage API Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Slack: Check for response error Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Slack: Support custom webhook URL Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Simplify Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Rewrite tests to use stdlib Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/services/alerting/notifiers/slack.go Co-authored-by: Dimitris Sotirakis <sotirakis.dim@gmail.com> * Clarify URL field name Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix linting issue Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix test Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix up new Slack notifier Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Improve tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix lint Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Slack: Make token not required Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Alerting: Send validation errors back to client Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Document how token is required Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Make recipient required when using Slack API Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix field description Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> Co-authored-by: Dimitris Sotirakis <sotirakis.dim@gmail.com>
4 years ago
var alertingErr alerting.ValidationError
if errors.As(err, &alertingErr) {
return response.Error(400, err.Error(), err)
}
return response.Error(500, "Failed to send alert notifications", err)
}
return response.Success("Test notification sent")
}
// 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
func (hs *HTTPServer) PauseAlert(legacyAlertingEnabled *bool) func(c *contextmodel.ReqContext) response.Response {
if legacyAlertingEnabled == nil || !*legacyAlertingEnabled {
return func(_ *contextmodel.ReqContext) response.Response {
return response.Error(http.StatusBadRequest, "legacy alerting is disabled, so this call has no effect.", nil)
}
}
return func(c *contextmodel.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)
if err != nil {
return response.Error(http.StatusBadRequest, "alertId is invalid", err)
}
result := make(map[string]interface{})
result["alertId"] = alertID
query := alertmodels.GetAlertByIdQuery{ID: alertID}
res, err := hs.AlertEngine.AlertStore.GetAlertById(c.Req.Context(), &query)
if err != nil {
return response.Error(500, "Get Alert failed", err)
}
guardian, err := guardian.New(c.Req.Context(), res.DashboardID, c.OrgID, c.SignedInUser)
if err != nil {
return response.ErrOrFallback(http.StatusInternalServerError, "Error while creating permission guardian", err)
}
if canEdit, err := guardian.CanEdit(); err != nil || !canEdit {
if err != nil {
return response.Error(500, "Error while checking permissions for Alert", err)
}
return response.Error(403, "Access denied to this dashboard and alert", nil)
}
// Alert state validation
if res.State != alertmodels.AlertStatePaused && !dto.Paused {
result["state"] = "un-paused"
result["message"] = "Alert is already un-paused"
return response.JSON(http.StatusOK, result)
} else if res.State == alertmodels.AlertStatePaused && dto.Paused {
result["state"] = alertmodels.AlertStatePaused
result["message"] = "Alert is already paused"
return response.JSON(http.StatusOK, result)
}
cmd := alertmodels.PauseAlertCommand{
OrgID: c.OrgID,
AlertIDs: []int64{alertID},
Paused: dto.Paused,
}
if err := hs.AlertEngine.AlertStore.PauseAlert(c.Req.Context(), &cmd); err != nil {
return response.Error(500, "", err)
}
resp := alertmodels.AlertStateUnknown
pausedState := "un-paused"
if cmd.Paused {
resp = alertmodels.AlertStatePaused
pausedState = "paused"
}
result["state"] = resp
result["message"] = "Alert " + pausedState
return response.JSON(http.StatusOK, result)
}
}
// 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
func (hs *HTTPServer) PauseAllAlerts(legacyAlertingEnabled *bool) func(c *contextmodel.ReqContext) response.Response {
if legacyAlertingEnabled == nil || !*legacyAlertingEnabled {
return func(_ *contextmodel.ReqContext) response.Response {
return response.Error(http.StatusBadRequest, "legacy alerting is disabled, so this call has no effect.", nil)
}
}
return func(c *contextmodel.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 := alertmodels.PauseAllAlertCommand{
Paused: dto.Paused,
}
if err := hs.AlertEngine.AlertStore.PauseAllAlerts(c.Req.Context(), &updateCmd); err != nil {
return response.Error(500, "Failed to pause alerts", err)
}
resp := alertmodels.AlertStatePending
pausedState := "un paused"
if updateCmd.Paused {
resp = alertmodels.AlertStatePaused
pausedState = "paused"
}
result := map[string]interface{}{
"state": resp,
"message": "alerts " + pausedState,
"alertsAffected": updateCmd.ResultCount,
}
return response.JSON(http.StatusOK, result)
}
}
// 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 alertmodels.CreateAlertNotificationCommand `json:"body"`
}
// swagger:parameters updateAlertNotificationChannel
type UpdateAlertNotificationChannelParams struct {
// in:body
// required:true
Body alertmodels.UpdateAlertNotificationCommand `json:"body"`
// in:path
// required:true
NotificationID int64 `json:"notification_channel_id"`
}
// swagger:parameters updateAlertNotificationChannelByUID
type UpdateAlertNotificationChannelByUIDParams struct {
// in:body
// required:true
Body alertmodels.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 []*alertmodels.AlertListItemDTO `json:"body"`
}
// swagger:response getAlertResponse
type GetAlertResponse struct {
// The response message
// in: body
Body *alertmodels.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 []*alertmodels.AlertStateInfoDTO `json:"body"`
}