grafana/pkg/api/alerting.go
Alexander Weaver 7a171fd14a
Regenerate openapidocs at 1.21.8 to match ci (#84037)
* Regenerate openapidocs at 1.21.8 to match ci

* Adjust trigger to work on the actual outputted files

* Also put go.mod and go.sum in the triggers

* manually fix

* Make an arbitrary change rather than touching the trigger to force a run

* Drop all triggers - run all the time

* Print diff - taken from @papagian's PR

* Manual fixes to swagger doc

---------

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2024-03-06 16:08:45 -06:00

1051 lines
33 KiB
Go

package api
import (
"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/infra/metrics"
"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/dashboards/dashboardaccess"
"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"
"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.SignedInUser.GetOrgID() != 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(http.StatusBadRequest, "Missing query parameter dashboardId", nil)
}
query := alertmodels.GetAlertStatesForDashboardQuery{
OrgID: c.SignedInUser.GetOrgID(),
DashboardID: c.QueryInt64("dashboardId"),
}
res, err := hs.AlertEngine.AlertStore.GetAlertStatesForDashboard(c.Req.Context(), &query)
if err != nil {
return response.Error(http.StatusInternalServerError, "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)
metrics.MFolderIDsAPICount.WithLabelValues(metrics.GetAlerts).Inc()
}
}
searchQuery := search.Query{
Title: dashboardQuery,
Tags: dashboardTags,
SignedInUser: c.SignedInUser,
Limit: 1000,
OrgId: c.SignedInUser.GetOrgID(),
DashboardIds: dashboardIDs,
Type: string(model.DashHitDB),
FolderIds: folderIDs, // nolint:staticcheck
Permission: dashboardaccess.PERMISSION_VIEW,
}
hits, err := hs.SearchService.SearchHandler(c.Req.Context(), &searchQuery)
if err != nil {
return response.Error(http.StatusInternalServerError, "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.SignedInUser.GetOrgID(),
DashboardIDs: dashboardIDs,
PanelID: c.QueryInt64("panelId"),
Limit: c.QueryInt64("limit"),
User: c.SignedInUser,
Query: c.Query("query"),
}
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(http.StatusInternalServerError, "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(http.StatusBadRequest, "The dashboard needs to be saved at least once before you can test an alert rule", nil)
}
res, err := hs.AlertEngine.AlertTest(c.SignedInUser.GetOrgID(), dto.Dashboard, dto.PanelId, c.SignedInUser)
if err != nil {
var validationErr alerting.ValidationError
if errors.As(err, &validationErr) {
return response.Error(http.StatusUnprocessableEntity, validationErr.Error(), nil)
}
if errors.Is(err, datasources.ErrDataSourceAccessDenied) {
return response.Error(http.StatusForbidden, "Access denied to datasource", err)
}
return response.Error(http.StatusInternalServerError, "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 internal 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(http.StatusInternalServerError, "List alerts failed", err)
}
return response.JSON(http.StatusOK, &res)
}
func (hs *HTTPServer) GetLegacyAlertNotifiers() func(*contextmodel.ReqContext) response.Response {
return func(_ *contextmodel.ReqContext) response.Response {
return response.JSON(http.StatusOK, alerting.GetNotifiers())
}
}
func (hs *HTTPServer) GetAlertNotifiers() func(*contextmodel.ReqContext) response.Response {
return func(_ *contextmodel.ReqContext) response.Response {
return response.JSON(http.StatusOK, channels_config.GetAvailableNotifiers())
}
}
// 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(http.StatusInternalServerError, "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(http.StatusInternalServerError, "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.SignedInUser.GetOrgID()}
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.SignedInUser.GetOrgID(),
ID: notificationId,
}
if query.ID == 0 {
return response.Error(http.StatusNotFound, "Alert notification not found", nil)
}
res, err := hs.AlertNotificationService.GetAlertNotifications(c.Req.Context(), query)
if err != nil {
return response.Error(http.StatusInternalServerError, "Failed to get alert notifications", err)
}
if res == nil {
return response.Error(http.StatusNotFound, "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.SignedInUser.GetOrgID(),
UID: web.Params(c.Req)[":uid"],
}
if query.UID == "" {
return response.Error(http.StatusNotFound, "Alert notification not found", nil)
}
res, err := hs.AlertNotificationService.GetAlertNotificationsWithUid(c.Req.Context(), query)
if err != nil {
return response.Error(http.StatusInternalServerError, "Failed to get alert notifications", err)
}
if res == nil {
return response.Error(http.StatusNotFound, "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.SignedInUser.GetOrgID()
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(http.StatusConflict, "Failed to create alert notification", err)
}
var alertingErr alerting.ValidationError
if errors.As(err, &alertingErr) {
return response.Error(http.StatusBadRequest, err.Error(), err)
}
return response.Error(http.StatusInternalServerError, "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.SignedInUser.GetOrgID()
err := hs.fillWithSecureSettingsData(c.Req.Context(), &cmd)
if err != nil {
return response.Error(http.StatusInternalServerError, "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(http.StatusNotFound, err.Error(), err)
}
var alertingErr alerting.ValidationError
if errors.As(err, &alertingErr) {
return response.Error(http.StatusBadRequest, err.Error(), err)
}
return response.Error(http.StatusInternalServerError, "Failed to update alert notification", err)
}
query := alertmodels.GetAlertNotificationsQuery{
OrgID: c.SignedInUser.GetOrgID(),
ID: cmd.ID,
}
res, err := hs.AlertNotificationService.GetAlertNotifications(c.Req.Context(), &query)
if err != nil {
return response.Error(http.StatusInternalServerError, "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.SignedInUser.GetOrgID()
cmd.UID = web.Params(c.Req)[":uid"]
err := hs.fillWithSecureSettingsDataByUID(c.Req.Context(), &cmd)
if err != nil {
return response.Error(http.StatusInternalServerError, "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(http.StatusNotFound, err.Error(), nil)
}
return response.Error(http.StatusInternalServerError, "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(http.StatusInternalServerError, "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 {
return err
}
secureSettings, err := hs.EncryptionService.DecryptJsonData(ctx, res.SecureSettings, hs.Cfg.SecretKey)
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 {
return err
}
secureSettings, err := hs.EncryptionService.DecryptJsonData(ctx, res.SecureSettings, hs.Cfg.SecretKey)
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.SignedInUser.GetOrgID(),
ID: notificationId,
}
if err := hs.AlertNotificationService.DeleteAlertNotification(c.Req.Context(), &cmd); err != nil {
if errors.Is(err, alertmodels.ErrAlertNotificationNotFound) {
return response.Error(http.StatusNotFound, err.Error(), nil)
}
return response.Error(http.StatusInternalServerError, "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.SignedInUser.GetOrgID(),
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(http.StatusNotFound, err.Error(), nil)
}
return response.Error(http.StatusInternalServerError, "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.SignedInUser.GetOrgID(),
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(http.StatusPreconditionFailed, err.Error(), err)
}
var alertingErr alerting.ValidationError
if errors.As(err, &alertingErr) {
return response.Error(http.StatusBadRequest, err.Error(), err)
}
return response.Error(http.StatusInternalServerError, "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]any)
result["alertId"] = alertID
query := alertmodels.GetAlertByIdQuery{ID: alertID}
res, err := hs.AlertEngine.AlertStore.GetAlertById(c.Req.Context(), &query)
if err != nil {
return response.Error(http.StatusInternalServerError, "Get Alert failed", err)
}
guardian, err := guardian.New(c.Req.Context(), res.DashboardID, c.SignedInUser.GetOrgID(), 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(http.StatusInternalServerError, "Error while checking permissions for Alert", err)
}
return response.Error(http.StatusForbidden, "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.SignedInUser.GetOrgID(),
AlertIDs: []int64{alertID},
Paused: dto.Paused,
}
if err := hs.AlertEngine.AlertStore.PauseAlert(c.Req.Context(), &cmd); err != nil {
return response.Error(http.StatusInternalServerError, "", 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(http.StatusInternalServerError, "Failed to pause alerts", err)
}
resp := alertmodels.AlertStatePending
pausedState := "un paused"
if updateCmd.Paused {
resp = alertmodels.AlertStatePaused
pausedState = "paused"
}
result := map[string]any{
"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
//
// Deprecated: use FolderUID instead
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"`
}