Skip to content

Commit d11bc57

Browse files
committed
feat(notifications): make it possible to send test alert notifications
closes grafana#5847
1 parent 4c5461d commit d11bc57

File tree

7 files changed

+163
-3
lines changed

7 files changed

+163
-3
lines changed

pkg/api/alerting.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,22 @@ func DeleteAlertNotification(c *middleware.Context) Response {
214214
return ApiSuccess("Notification deleted")
215215
}
216216

217+
//POST /api/alert-notifications/test
218+
func NotificationTest(c *middleware.Context, dto dtos.NotificationTestCommand) Response {
219+
cmd := &alerting.NotificationTestCommand{
220+
Name: dto.Name,
221+
Type: dto.Type,
222+
Severity: dto.Severity,
223+
Settings: dto.Settings,
224+
}
225+
226+
if err := bus.Dispatch(cmd); err != nil {
227+
return ApiError(500, "Failed to send alert notifications", err)
228+
}
229+
230+
return ApiSuccess("Test notification sent")
231+
}
232+
217233
func GetAlertHistory(c *middleware.Context) Response {
218234
alertId, err := getAlertIdForRequest(c)
219235
if err != nil {

pkg/api/api.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ func Register(r *macaron.Macaron) {
259259
r.Get("/alert-notifications", wrap(GetAlertNotifications))
260260

261261
r.Group("/alert-notifications", func() {
262+
r.Post("/test", bind(dtos.NotificationTestCommand{}), wrap(NotificationTest))
262263
r.Post("/", bind(m.CreateAlertNotificationCommand{}), wrap(CreateAlertNotification))
263264
r.Put("/:notificationId", bind(m.UpdateAlertNotificationCommand{}), wrap(UpdateAlertNotification))
264265
r.Get("/:notificationId", wrap(GetAlertNotificationById))

pkg/api/dtos/alerting.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,10 @@ type AlertHistory struct {
6363

6464
Data *simplejson.Json `json:"data"`
6565
}
66+
67+
type NotificationTestCommand struct {
68+
Name string `json:"name"`
69+
Type string `json:"type"`
70+
Settings *simplejson.Json `json:"settings"`
71+
Severity string `json:"severity"`
72+
}

pkg/services/alerting/notifier.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,17 @@ func (n *RootNotifier) Notify(context *EvalContext) {
4646
n.log.Error("Failed to upload alert panel image", "error", err)
4747
}
4848

49+
n.sendNotifications(notifiers, context)
50+
}
51+
52+
func (n *RootNotifier) sendNotifications(notifiers []Notifier, context *EvalContext) {
4953
for _, notifier := range notifiers {
5054
n.log.Info("Sending notification", "firing", context.Firing, "type", notifier.GetType())
5155
go notifier.Notify(context)
5256
}
5357
}
5458

5559
func (n *RootNotifier) uploadImage(context *EvalContext) error {
56-
5760
uploader, _ := imguploader.NewImageUploader()
5861

5962
imageUrl, err := context.GetImageUrl()
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package alerting
2+
3+
import (
4+
"github.com/grafana/grafana/pkg/bus"
5+
"github.com/grafana/grafana/pkg/components/simplejson"
6+
"github.com/grafana/grafana/pkg/log"
7+
"github.com/grafana/grafana/pkg/models"
8+
)
9+
10+
type NotificationTestCommand struct {
11+
Severity string
12+
Name string
13+
Type string
14+
Settings *simplejson.Json
15+
}
16+
17+
func init() {
18+
bus.AddHandler("alerting", handleNotificationTestCommand)
19+
20+
}
21+
22+
func handleNotificationTestCommand(cmd *NotificationTestCommand) error {
23+
notifier := NewRootNotifier()
24+
25+
model := &models.AlertNotification{
26+
Name: cmd.Name,
27+
Type: cmd.Type,
28+
Settings: cmd.Settings,
29+
}
30+
31+
notifiers, err := notifier.getNotifierFor(model)
32+
33+
if err != nil {
34+
log.Error2("Failed to create notifier", "error", err.Error())
35+
return err
36+
}
37+
38+
severity := models.AlertSeverityType(cmd.Severity)
39+
notifier.sendNotifications([]Notifier{notifiers}, createTestEvalContext(severity))
40+
41+
return nil
42+
}
43+
44+
func createTestEvalContext(severity models.AlertSeverityType) *EvalContext {
45+
state := models.AlertStateOK
46+
firing := false
47+
if severity == models.AlertSeverityCritical {
48+
state = models.AlertStateCritical
49+
firing = true
50+
}
51+
if severity == models.AlertSeverityWarning {
52+
state = models.AlertStateWarning
53+
firing = true
54+
}
55+
56+
testRule := &Rule{
57+
DashboardId: 1,
58+
PanelId: 1,
59+
Name: "Test notification",
60+
Message: "Someone is testing the alert notification within grafana.",
61+
State: state,
62+
Severity: severity,
63+
}
64+
65+
ctx := NewEvalContext(testRule)
66+
ctx.ImagePublicUrl = "http://grafana.org/assets/img/blog/mixed_styles.png"
67+
68+
ctx.IsTestRun = true
69+
ctx.Firing = firing
70+
ctx.Error = nil
71+
ctx.EvalMatches = evalMatchesBasedOnSeverity(severity)
72+
73+
return ctx
74+
}
75+
76+
func evalMatchesBasedOnSeverity(severity models.AlertSeverityType) []*EvalMatch {
77+
matches := make([]*EvalMatch, 0)
78+
if severity == models.AlertSeverityOK {
79+
return matches
80+
}
81+
82+
matches = append(matches, &EvalMatch{
83+
Metric: "High value",
84+
Value: 100,
85+
})
86+
87+
matches = append(matches, &EvalMatch{
88+
Metric: "Higher Value",
89+
Value: 200,
90+
})
91+
92+
return matches
93+
}

public/app/features/alerting/notification_edit_ctrl.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import config from 'app/core/config';
77

88
export class AlertNotificationEditCtrl {
99
model: any;
10+
showTest: boolean = false;
11+
testSeverity: string = "critical";
1012

1113
/** @ngInject */
1214
constructor(private $routeParams, private backendSrv, private $scope, private $location) {
@@ -47,6 +49,24 @@ export class AlertNotificationEditCtrl {
4749
typeChanged() {
4850
this.model.settings = {};
4951
}
52+
53+
toggleTest() {
54+
this.showTest = !this.showTest;
55+
}
56+
57+
testNotification() {
58+
var payload = {
59+
name: this.model.name,
60+
type: this.model.type,
61+
settings: this.model.settings,
62+
severity: this.testSeverity
63+
};
64+
65+
this.backendSrv.post(`/api/alert-notifications/test`, payload)
66+
.then(res => {
67+
this.$scope.appEvent('alert-succes', ['Test notification sent', '']);
68+
});
69+
}
5070
}
5171

5272
coreModule.controller('AlertNotificationEditCtrl', AlertNotificationEditCtrl);

public/app/features/alerting/partials/notification_edit.html

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,27 @@ <h3 class="page-heading">Email addresses</h3>
6060
</div>
6161
</div>
6262

63-
<div class="gf-form-button-row">
64-
<button ng-click="ctrl.save()" class="btn btn-success">Save</button>
63+
<div class="gf-form-group">
64+
<div class="gf-form-inline">
65+
<div class="gf-form width-6">
66+
<button ng-click="ctrl.save()" class="btn btn-success">Save</button>
67+
</div>
68+
<div class="gf-form width-8">
69+
<button ng-click="ctrl.toggleTest()" class="btn btn-secondary">Test</button>
70+
</div>
71+
72+
<div class="gf-form width-20" ng-show="ctrl.showTest">
73+
<span class="gf-form-label width-13">Severity for test notification</span>
74+
<div class="gf-form-select-wrapper width-7">
75+
<select class="gf-form-input"
76+
ng-model="ctrl.testSeverity"
77+
ng-options="t for t in ['critical', 'warning', 'ok']">
78+
</select>
79+
</div>
80+
</div>
81+
<div class="gf-form" ng-show="ctrl.showTest">
82+
<button ng-click="ctrl.testNotification()" class="btn btn-secondary">Send</button>
83+
</div>
84+
</div>
6585
</div>
6686
</div>

0 commit comments

Comments
 (0)