feat(notifications): make it possible to send test alert notifications
closes #5847
This commit is contained in:
@@ -214,6 +214,22 @@ func DeleteAlertNotification(c *middleware.Context) Response {
|
|||||||
return ApiSuccess("Notification deleted")
|
return ApiSuccess("Notification deleted")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//POST /api/alert-notifications/test
|
||||||
|
func NotificationTest(c *middleware.Context, dto dtos.NotificationTestCommand) Response {
|
||||||
|
cmd := &alerting.NotificationTestCommand{
|
||||||
|
Name: dto.Name,
|
||||||
|
Type: dto.Type,
|
||||||
|
Severity: dto.Severity,
|
||||||
|
Settings: dto.Settings,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bus.Dispatch(cmd); err != nil {
|
||||||
|
return ApiError(500, "Failed to send alert notifications", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiSuccess("Test notification sent")
|
||||||
|
}
|
||||||
|
|
||||||
func GetAlertHistory(c *middleware.Context) Response {
|
func GetAlertHistory(c *middleware.Context) Response {
|
||||||
alertId, err := getAlertIdForRequest(c)
|
alertId, err := getAlertIdForRequest(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -259,6 +259,7 @@ func Register(r *macaron.Macaron) {
|
|||||||
r.Get("/alert-notifications", wrap(GetAlertNotifications))
|
r.Get("/alert-notifications", wrap(GetAlertNotifications))
|
||||||
|
|
||||||
r.Group("/alert-notifications", func() {
|
r.Group("/alert-notifications", func() {
|
||||||
|
r.Post("/test", bind(dtos.NotificationTestCommand{}), wrap(NotificationTest))
|
||||||
r.Post("/", bind(m.CreateAlertNotificationCommand{}), wrap(CreateAlertNotification))
|
r.Post("/", bind(m.CreateAlertNotificationCommand{}), wrap(CreateAlertNotification))
|
||||||
r.Put("/:notificationId", bind(m.UpdateAlertNotificationCommand{}), wrap(UpdateAlertNotification))
|
r.Put("/:notificationId", bind(m.UpdateAlertNotificationCommand{}), wrap(UpdateAlertNotification))
|
||||||
r.Get("/:notificationId", wrap(GetAlertNotificationById))
|
r.Get("/:notificationId", wrap(GetAlertNotificationById))
|
||||||
|
|||||||
@@ -63,3 +63,10 @@ type AlertHistory struct {
|
|||||||
|
|
||||||
Data *simplejson.Json `json:"data"`
|
Data *simplejson.Json `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NotificationTestCommand struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Settings *simplejson.Json `json:"settings"`
|
||||||
|
Severity string `json:"severity"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,6 +46,10 @@ func (n *RootNotifier) Notify(context *EvalContext) {
|
|||||||
n.log.Error("Failed to upload alert panel image", "error", err)
|
n.log.Error("Failed to upload alert panel image", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n.sendNotifications(notifiers, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *RootNotifier) sendNotifications(notifiers []Notifier, context *EvalContext) {
|
||||||
for _, notifier := range notifiers {
|
for _, notifier := range notifiers {
|
||||||
n.log.Info("Sending notification", "firing", context.Firing, "type", notifier.GetType())
|
n.log.Info("Sending notification", "firing", context.Firing, "type", notifier.GetType())
|
||||||
go notifier.Notify(context)
|
go notifier.Notify(context)
|
||||||
@@ -53,7 +57,6 @@ func (n *RootNotifier) Notify(context *EvalContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *RootNotifier) uploadImage(context *EvalContext) error {
|
func (n *RootNotifier) uploadImage(context *EvalContext) error {
|
||||||
|
|
||||||
uploader, _ := imguploader.NewImageUploader()
|
uploader, _ := imguploader.NewImageUploader()
|
||||||
|
|
||||||
imageUrl, err := context.GetImageUrl()
|
imageUrl, err := context.GetImageUrl()
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package alerting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
|
"github.com/grafana/grafana/pkg/log"
|
||||||
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NotificationTestCommand struct {
|
||||||
|
Severity string
|
||||||
|
Name string
|
||||||
|
Type string
|
||||||
|
Settings *simplejson.Json
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
bus.AddHandler("alerting", handleNotificationTestCommand)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleNotificationTestCommand(cmd *NotificationTestCommand) error {
|
||||||
|
notifier := NewRootNotifier()
|
||||||
|
|
||||||
|
model := &models.AlertNotification{
|
||||||
|
Name: cmd.Name,
|
||||||
|
Type: cmd.Type,
|
||||||
|
Settings: cmd.Settings,
|
||||||
|
}
|
||||||
|
|
||||||
|
notifiers, err := notifier.getNotifierFor(model)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error2("Failed to create notifier", "error", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
severity := models.AlertSeverityType(cmd.Severity)
|
||||||
|
notifier.sendNotifications([]Notifier{notifiers}, createTestEvalContext(severity))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTestEvalContext(severity models.AlertSeverityType) *EvalContext {
|
||||||
|
state := models.AlertStateOK
|
||||||
|
firing := false
|
||||||
|
if severity == models.AlertSeverityCritical {
|
||||||
|
state = models.AlertStateCritical
|
||||||
|
firing = true
|
||||||
|
}
|
||||||
|
if severity == models.AlertSeverityWarning {
|
||||||
|
state = models.AlertStateWarning
|
||||||
|
firing = true
|
||||||
|
}
|
||||||
|
|
||||||
|
testRule := &Rule{
|
||||||
|
DashboardId: 1,
|
||||||
|
PanelId: 1,
|
||||||
|
Name: "Test notification",
|
||||||
|
Message: "Someone is testing the alert notification within grafana.",
|
||||||
|
State: state,
|
||||||
|
Severity: severity,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := NewEvalContext(testRule)
|
||||||
|
ctx.ImagePublicUrl = "http://grafana.org/assets/img/blog/mixed_styles.png"
|
||||||
|
|
||||||
|
ctx.IsTestRun = true
|
||||||
|
ctx.Firing = firing
|
||||||
|
ctx.Error = nil
|
||||||
|
ctx.EvalMatches = evalMatchesBasedOnSeverity(severity)
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalMatchesBasedOnSeverity(severity models.AlertSeverityType) []*EvalMatch {
|
||||||
|
matches := make([]*EvalMatch, 0)
|
||||||
|
if severity == models.AlertSeverityOK {
|
||||||
|
return matches
|
||||||
|
}
|
||||||
|
|
||||||
|
matches = append(matches, &EvalMatch{
|
||||||
|
Metric: "High value",
|
||||||
|
Value: 100,
|
||||||
|
})
|
||||||
|
|
||||||
|
matches = append(matches, &EvalMatch{
|
||||||
|
Metric: "Higher Value",
|
||||||
|
Value: 200,
|
||||||
|
})
|
||||||
|
|
||||||
|
return matches
|
||||||
|
}
|
||||||
@@ -7,6 +7,8 @@ import config from 'app/core/config';
|
|||||||
|
|
||||||
export class AlertNotificationEditCtrl {
|
export class AlertNotificationEditCtrl {
|
||||||
model: any;
|
model: any;
|
||||||
|
showTest: boolean = false;
|
||||||
|
testSeverity: string = "critical";
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private $routeParams, private backendSrv, private $scope, private $location) {
|
constructor(private $routeParams, private backendSrv, private $scope, private $location) {
|
||||||
@@ -47,6 +49,24 @@ export class AlertNotificationEditCtrl {
|
|||||||
typeChanged() {
|
typeChanged() {
|
||||||
this.model.settings = {};
|
this.model.settings = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleTest() {
|
||||||
|
this.showTest = !this.showTest;
|
||||||
|
}
|
||||||
|
|
||||||
|
testNotification() {
|
||||||
|
var payload = {
|
||||||
|
name: this.model.name,
|
||||||
|
type: this.model.type,
|
||||||
|
settings: this.model.settings,
|
||||||
|
severity: this.testSeverity
|
||||||
|
};
|
||||||
|
|
||||||
|
this.backendSrv.post(`/api/alert-notifications/test`, payload)
|
||||||
|
.then(res => {
|
||||||
|
this.$scope.appEvent('alert-succes', ['Test notification sent', '']);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
coreModule.controller('AlertNotificationEditCtrl', AlertNotificationEditCtrl);
|
coreModule.controller('AlertNotificationEditCtrl', AlertNotificationEditCtrl);
|
||||||
|
|||||||
@@ -60,7 +60,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form-button-row">
|
<div class="gf-form-group">
|
||||||
<button ng-click="ctrl.save()" class="btn btn-success">Save</button>
|
<div class="gf-form-inline">
|
||||||
|
<div class="gf-form width-6">
|
||||||
|
<button ng-click="ctrl.save()" class="btn btn-success">Save</button>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form width-8">
|
||||||
|
<button ng-click="ctrl.toggleTest()" class="btn btn-secondary">Test</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form width-20" ng-show="ctrl.showTest">
|
||||||
|
<span class="gf-form-label width-13">Severity for test notification</span>
|
||||||
|
<div class="gf-form-select-wrapper width-7">
|
||||||
|
<select class="gf-form-input"
|
||||||
|
ng-model="ctrl.testSeverity"
|
||||||
|
ng-options="t for t in ['critical', 'warning', 'ok']">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form" ng-show="ctrl.showTest">
|
||||||
|
<button ng-click="ctrl.testNotification()" class="btn btn-secondary">Send</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user