diff --git a/pkg/services/ngalert/notifier/channels/line.go b/pkg/services/ngalert/notifier/channels/line.go index 0ec73c9feb3..108a8407b74 100644 --- a/pkg/services/ngalert/notifier/channels/line.go +++ b/pkg/services/ngalert/notifier/channels/line.go @@ -12,6 +12,7 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" + ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/notifications" ) @@ -32,7 +33,7 @@ func LineFactory(fc FactoryConfig) (NotificationChannel, error) { Cfg: *fc.Config, } } - return NewLineNotifier(cfg, fc.NotificationService, fc.Template), nil + return NewLineNotifier(cfg, fc.ImageStore, fc.NotificationService, fc.Template), nil } func NewLineConfig(config *NotificationChannelConfig, decryptFunc GetDecryptedValueFn) (*LineConfig, error) { @@ -47,7 +48,7 @@ func NewLineConfig(config *NotificationChannelConfig, decryptFunc GetDecryptedVa } // NewLineNotifier is the constructor for the LINE notifier -func NewLineNotifier(config *LineConfig, ns notifications.WebhookSender, t *template.Template) *LineNotifier { +func NewLineNotifier(config *LineConfig, images ImageStore, ns notifications.WebhookSender, t *template.Template) *LineNotifier { return &LineNotifier{ Base: NewBase(&models.AlertNotification{ Uid: config.UID, @@ -56,10 +57,11 @@ func NewLineNotifier(config *LineConfig, ns notifications.WebhookSender, t *temp DisableResolveMessage: config.DisableResolveMessage, Settings: config.Settings, }), - Token: config.Token, - log: log.New("alerting.notifier.line"), - ns: ns, - tmpl: t, + Token: config.Token, + log: log.New("alerting.notifier.line"), + images: images, + ns: ns, + tmpl: t, } } @@ -67,10 +69,11 @@ func NewLineNotifier(config *LineConfig, ns notifications.WebhookSender, t *temp // alert notifications to LINE. type LineNotifier struct { *Base - Token string - log log.Logger - ns notifications.WebhookSender - tmpl *template.Template + Token string + log log.Logger + images ImageStore + ns notifications.WebhookSender + tmpl *template.Template } // Notify send an alert notification to LINE @@ -95,6 +98,20 @@ func (ln *LineNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, e form := url.Values{} form.Add("message", body) + _ = withStoredImages(ctx, ln.log, ln.images, + func(index int, image ngmodels.Image) error { + // If there is an image for this alert and the image has been uploaded + // to a public URL then add it to the request. We cannot add more than + // one image per request. + if image.URL != "" { + form.Add("imageThumbnail", image.URL) + form.Add("imageFullsize", image.URL) + return ErrImagesDone + } + return nil + }, + as...) + cmd := &models.SendWebhookSync{ Url: LineNotifyURL, HttpMethod: "POST", diff --git a/pkg/services/ngalert/notifier/channels/line_test.go b/pkg/services/ngalert/notifier/channels/line_test.go index 259045d3d93..d5b33690178 100644 --- a/pkg/services/ngalert/notifier/channels/line_test.go +++ b/pkg/services/ngalert/notifier/channels/line_test.go @@ -18,6 +18,8 @@ import ( func TestLineNotifier(t *testing.T) { tmpl := templateForTests(t) + images := newFakeImageStore(2) + externalURL, err := url.Parse("http://localhost") require.NoError(t, err) tmpl.ExternalURL = externalURL @@ -32,7 +34,7 @@ func TestLineNotifier(t *testing.T) { expMsgError error }{ { - name: "One alert", + name: "A single alert", settings: `{"token": "sometoken"}`, alerts: []*types.Alert{ { @@ -70,6 +72,46 @@ func TestLineNotifier(t *testing.T) { }, expMsg: "message=%5BFIRING%3A2%5D++%0Ahttp%3A%2Flocalhost%2Falerting%2Flist%0A%0A%2A%2AFiring%2A%2A%0A%0AValue%3A+%5Bno+value%5D%0ALabels%3A%0A+-+alertname+%3D+alert1%0A+-+lbl1+%3D+val1%0AAnnotations%3A%0A+-+ann1+%3D+annv1%0ASilence%3A+http%3A%2F%2Flocalhost%2Falerting%2Fsilence%2Fnew%3Falertmanager%3Dgrafana%26matcher%3Dalertname%253Dalert1%26matcher%3Dlbl1%253Dval1%0A%0AValue%3A+%5Bno+value%5D%0ALabels%3A%0A+-+alertname+%3D+alert1%0A+-+lbl1+%3D+val2%0AAnnotations%3A%0A+-+ann1+%3D+annv2%0ASilence%3A+http%3A%2F%2Flocalhost%2Falerting%2Fsilence%2Fnew%3Falertmanager%3Dgrafana%26matcher%3Dalertname%253Dalert1%26matcher%3Dlbl1%253Dval2%0A", expMsgError: nil, + }, { + name: "A single alert with an image", + settings: `{"token": "sometoken"}`, + alerts: []*types.Alert{ + { + Alert: model.Alert{ + Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"}, + Annotations: model.LabelSet{"ann1": "annv1", "__dashboardUid__": "abcd", "__panelId__": "efgh", "__alertScreenshotToken__": "test-image-1"}, + }, + }, + }, + expHeaders: map[string]string{ + "Authorization": "Bearer sometoken", + "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", + }, + expMsg: "imageFullsize=https%3A%2F%2Fwww.example.com%2Ftest-image-1.jpg&imageThumbnail=https%3A%2F%2Fwww.example.com%2Ftest-image-1.jpg&message=%5BFIRING%3A1%5D++%28val1%29%0Ahttp%3A%2Flocalhost%2Falerting%2Flist%0A%0A%2A%2AFiring%2A%2A%0A%0AValue%3A+%5Bno+value%5D%0ALabels%3A%0A+-+alertname+%3D+alert1%0A+-+lbl1+%3D+val1%0AAnnotations%3A%0A+-+ann1+%3D+annv1%0ASilence%3A+http%3A%2F%2Flocalhost%2Falerting%2Fsilence%2Fnew%3Falertmanager%3Dgrafana%26matcher%3Dalertname%253Dalert1%26matcher%3Dlbl1%253Dval1%0ADashboard%3A+http%3A%2F%2Flocalhost%2Fd%2Fabcd%0APanel%3A+http%3A%2F%2Flocalhost%2Fd%2Fabcd%3FviewPanel%3Defgh%0A", + expMsgError: nil, + }, { + name: "Multiple alerts with an image", + settings: `{"token": "sometoken"}`, + alerts: []*types.Alert{ + { + Alert: model.Alert{ + Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"}, + Annotations: model.LabelSet{"ann1": "annv1", "__dashboardUid__": "abcd", "__panelId__": "efgh", "__alertScreenshotToken__": "test-image-1"}, + }, + }, + { + Alert: model.Alert{ + Labels: model.LabelSet{"alertname": "alert2", "lbl2": "val2"}, + Annotations: model.LabelSet{"ann2": "annv2", "__dashboardUid__": "abcd", "__panelId__": "efgh", "__alertScreenshotToken__": "test-image-2"}, + }, + }, + }, + expHeaders: map[string]string{ + "Authorization": "Bearer sometoken", + "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", + }, + expMsg: "imageFullsize=https%3A%2F%2Fwww.example.com%2Ftest-image-1.jpg&imageThumbnail=https%3A%2F%2Fwww.example.com%2Ftest-image-1.jpg&message=%5BFIRING%3A2%5D++%0Ahttp%3A%2Flocalhost%2Falerting%2Flist%0A%0A%2A%2AFiring%2A%2A%0A%0AValue%3A+%5Bno+value%5D%0ALabels%3A%0A+-+alertname+%3D+alert1%0A+-+lbl1+%3D+val1%0AAnnotations%3A%0A+-+ann1+%3D+annv1%0ASilence%3A+http%3A%2F%2Flocalhost%2Falerting%2Fsilence%2Fnew%3Falertmanager%3Dgrafana%26matcher%3Dalertname%253Dalert1%26matcher%3Dlbl1%253Dval1%0ADashboard%3A+http%3A%2F%2Flocalhost%2Fd%2Fabcd%0APanel%3A+http%3A%2F%2Flocalhost%2Fd%2Fabcd%3FviewPanel%3Defgh%0A%0AValue%3A+%5Bno+value%5D%0ALabels%3A%0A+-+alertname+%3D+alert2%0A+-+lbl2+%3D+val2%0AAnnotations%3A%0A+-+ann2+%3D+annv2%0ASilence%3A+http%3A%2F%2Flocalhost%2Falerting%2Fsilence%2Fnew%3Falertmanager%3Dgrafana%26matcher%3Dalertname%253Dalert2%26matcher%3Dlbl2%253Dval2%0ADashboard%3A+http%3A%2F%2Flocalhost%2Fd%2Fabcd%0APanel%3A+http%3A%2F%2Flocalhost%2Fd%2Fabcd%3FviewPanel%3Defgh%0A", + expMsgError: nil, }, { name: "Token missing", settings: `{}`, @@ -103,7 +145,7 @@ func TestLineNotifier(t *testing.T) { ctx := notify.WithGroupKey(context.Background(), "alertname") ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""}) - pn := NewLineNotifier(cfg, webhookSender, tmpl) + pn := NewLineNotifier(cfg, images, webhookSender, tmpl) ok, err := pn.Notify(ctx, c.alerts...) if c.expMsgError != nil { require.False(t, ok)