From 1bf982439b3a729f5930936a3be4fb2ab9a8f01a Mon Sep 17 00:00:00 2001 From: Andrzej Ressel Date: Thu, 23 Mar 2017 21:53:54 +0100 Subject: [PATCH] Sending image --- pkg/components/null/float.go | 9 +++ pkg/models/notifications.go | 13 ++-- pkg/services/alerting/notifiers/discord.go | 83 ++++++++++++++++++--- pkg/services/notifications/notifications.go | 13 ++-- pkg/services/notifications/webhook.go | 21 ++++-- 5 files changed, 107 insertions(+), 32 deletions(-) diff --git a/pkg/components/null/float.go b/pkg/components/null/float.go index 1e78946e878..b7b2f44a741 100644 --- a/pkg/components/null/float.go +++ b/pkg/components/null/float.go @@ -106,6 +106,15 @@ func (f Float) String() string { return fmt.Sprintf("%1.3f", f.Float64) } +// FullString returns float as string in full precision +func (f Float) FullString() string { + if !f.Valid { + return "null" + } + + return fmt.Sprintf("%f", f.Float64) +} + // SetValid changes this Float's value and also sets it to be non-null. func (f *Float) SetValid(n float64) { f.Float64 = n diff --git a/pkg/models/notifications.go b/pkg/models/notifications.go index ad7aed3bc50..a3fc917f0f4 100644 --- a/pkg/models/notifications.go +++ b/pkg/models/notifications.go @@ -18,12 +18,13 @@ type SendEmailCommandSync struct { } type SendWebhookSync struct { - Url string - User string - Password string - Body string - HttpMethod string - HttpHeader map[string]string + Url string + User string + Password string + Body string + HttpMethod string + HttpHeader map[string]string + ContentType string } type SendResetPasswordEmailCommand struct { diff --git a/pkg/services/alerting/notifiers/discord.go b/pkg/services/alerting/notifiers/discord.go index ce9849bbe6c..17080511656 100644 --- a/pkg/services/alerting/notifiers/discord.go +++ b/pkg/services/alerting/notifiers/discord.go @@ -1,6 +1,10 @@ package notifiers import ( + "bytes" + "io" + "mime/multipart" + "os" "strconv" "strings" @@ -64,9 +68,10 @@ func (this *DiscordNotifier) Notify(evalContext *alerting.EvalContext) error { fields := make([]map[string]interface{}, 0) for _, evt := range evalContext.EvalMatches { + fields = append(fields, map[string]interface{}{ "name": evt.Metric, - "value": evt.Value, + "value": evt.Value.FullString(), "inline": true, }) } @@ -78,10 +83,6 @@ func (this *DiscordNotifier) Notify(evalContext *alerting.EvalContext) error { color, _ := strconv.ParseInt(strings.TrimLeft(evalContext.GetStateModel().Color, "#"), 16, 0) - image := map[string]interface{}{ - "url": evalContext.ImagePublicUrl, - } - embed := simplejson.New() embed.Set("title", evalContext.GetNotificationTitle()) //Discord takes integer for color @@ -91,22 +92,80 @@ func (this *DiscordNotifier) Notify(evalContext *alerting.EvalContext) error { embed.Set("type", "rich") embed.Set("fields", fields) embed.Set("footer", footer) - embed.Set("image", image) + + var image = make(map[string]interface{}) + + var embeddedImage = false + + if evalContext.ImagePublicUrl != "" { + image = map[string]interface{}{ + "url": evalContext.ImagePublicUrl, + } + embed.Set("image", image) + } else { + image = map[string]interface{}{ + "url": "attachment://graph.png", + } + embed.Set("image", image) + embeddedImage = true + } bodyJSON.Set("embeds", []interface{}{embed}) - body, _ := bodyJSON.MarshalJSON() + json, _ := bodyJSON.MarshalJSON() + + content_type := "application/json" + + var body []byte + + if embeddedImage { + + var b bytes.Buffer + + w := multipart.NewWriter(&b) - this.log.Info("Message", string(body)) + f, err := os.Open(evalContext.ImageOnDiskPath) + + if err != nil { + this.log.Error("Can't open graph file", err) + return err + } + + defer f.Close() + + fw, err := w.CreateFormField("payload_json") + if err != nil { + return err + } + + if _, err = fw.Write([]byte(string(json))); err != nil { + return err + } + + fw, err = w.CreateFormFile("file", "graph.png") + + if _, err = io.Copy(fw, f); err != nil { + return err + } + + w.Close() + + body = b.Bytes() + content_type = w.FormDataContentType() + + } else { + body = json + } cmd := &m.SendWebhookSync{ - Url: this.WebhookURL, - Body: string(body), - HttpMethod: "POST", + Url: this.WebhookURL, + Body: string(body), + HttpMethod: "POST", + ContentType: content_type, } if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil { - this.log.Error("Failed to send notification to Discord", "error", err, "body", string(body)) + this.log.Error("Failed to send notification to Discord", "error", err) return err } diff --git a/pkg/services/notifications/notifications.go b/pkg/services/notifications/notifications.go index c765774d062..decb74e8be2 100644 --- a/pkg/services/notifications/notifications.go +++ b/pkg/services/notifications/notifications.go @@ -60,12 +60,13 @@ func Init() error { func SendWebhookSync(ctx context.Context, cmd *m.SendWebhookSync) error { return sendWebRequestSync(ctx, &Webhook{ - Url: cmd.Url, - User: cmd.User, - Password: cmd.Password, - Body: cmd.Body, - HttpMethod: cmd.HttpMethod, - HttpHeader: cmd.HttpHeader, + Url: cmd.Url, + User: cmd.User, + Password: cmd.Password, + Body: cmd.Body, + HttpMethod: cmd.HttpMethod, + HttpHeader: cmd.HttpHeader, + ContentType: cmd.ContentType, }) } diff --git a/pkg/services/notifications/webhook.go b/pkg/services/notifications/webhook.go index c74804ab828..f532ffc2f9e 100644 --- a/pkg/services/notifications/webhook.go +++ b/pkg/services/notifications/webhook.go @@ -16,12 +16,13 @@ import ( ) type Webhook struct { - Url string - User string - Password string - Body string - HttpMethod string - HttpHeader map[string]string + Url string + User string + Password string + Body string + HttpMethod string + HttpHeader map[string]string + ContentType string } var netTransport = &http.Transport{ @@ -61,7 +62,7 @@ func processWebhookQueue() { } func sendWebRequestSync(ctx context.Context, webhook *Webhook) error { - webhookLog.Debug("Sending webhook", "url", webhook.Url, "http method", webhook.HttpMethod) + webhookLog.Debug("Sending webhook", "url", webhook.Url, "http method", webhook.HttpMethod, "content type", webhook.ContentType) if webhook.HttpMethod == "" { webhook.HttpMethod = http.MethodPost @@ -72,7 +73,11 @@ func sendWebRequestSync(ctx context.Context, webhook *Webhook) error { return err } - request.Header.Add("Content-Type", "application/json") + if webhook.ContentType == "" { + webhook.ContentType = "application/json" + } + + request.Header.Add("Content-Type", webhook.ContentType) request.Header.Add("User-Agent", "Grafana") if webhook.User != "" && webhook.Password != "" { request.Header.Add("Authorization", util.GetBasicAuthHeader(webhook.User, webhook.Password))