diff --git a/handler.go b/handler.go index 4be467e..ad6d3c9 100644 --- a/handler.go +++ b/handler.go @@ -1,22 +1,11 @@ package main import ( - "encoding/json" - "io/ioutil" "net/http" ) -type Response struct { - Message string - Code int -} - -func errorResponse() Response { - return Response{"", http.StatusInternalServerError} -} - -// interface for parser functions (grafana, prometheus, ...) -type parserFunc func(*http.Request) (string, error, Response) +// interface for parser functions +type parserFunc func(*http.Request) (string, error) type messageHandler struct { messages chan<- string // chan to xmpp client @@ -26,14 +15,16 @@ type messageHandler struct { // http request handler func (h *messageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // parse/generate message from http request - m, err, response := h.parserFunc(r) + m, err := h.parserFunc(r) if err != nil { w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte(err.Error())) + } else { + // send message to xmpp client + h.messages <- m + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("ok")) } - // send message to xmpp client - h.messages <- m - w.WriteHeader(response.Code) - w.Write([]byte(response.Message)) } // returns new handler with a given parser function @@ -43,98 +34,3 @@ func newMessageHandler(m chan<- string, f parserFunc) *messageHandler { parserFunc: f, } } - -/************* -GRAFANA PARSER -*************/ -func grafanaParserFunc(r *http.Request) (string, error, Response) { - // get alert data from request - body, err := ioutil.ReadAll(r.Body) - if err != nil { - return "", err, errorResponse() - } - - // grafana alert struct - alert := &struct { - Title string `json:"title"` - RuleURL string `json:"ruleUrl"` - State string `json:"state"` - Message string `json:"message"` - }{} - - // parse body into the alert struct - err = json.Unmarshal(body, &alert) - if err != nil { - return "", err, errorResponse() - } - - // contruct alert message - var message string - switch alert.State { - case "ok": - message = ":) " + alert.Title - default: - message = ":( " + alert.Title + "\n" + alert.Message + "\n" + alert.RuleURL - } - - return message, nil, Response{"", http.StatusNoContent} -} - -/************* -SLACK PARSER -*************/ -type SlackMessage struct { - Channel string `json:"channel"` - IconEmoji string `json:"icon_emoji"` - Username string `json:"username"` - Text string `json:"text"` - Attachments []SlackAttachment `json:"attachments"` -} - -type SlackAttachment struct { - Color string `json:"color"` - Title string `json:"title"` - TitleLink string `json:"title_link"` - Text string `json:"text"` -} - -func nonemptyAppendNewline(message string) string { - if len(message) == 0 { - return message - } - - return message + "\n" -} - -func slackParserFunc(r *http.Request) (string, error, Response) { - // get alert data from request - body, err := ioutil.ReadAll(r.Body) - if err != nil { - return "", err, errorResponse() - } - - // grafana alert struct - alert := SlackMessage{} - - // parse body into the alert struct - err = json.Unmarshal(body, &alert) - if err != nil { - return "", err, Response{"", http.StatusInternalServerError} - } - - // contruct alert message - message := "" - hasText := (alert.Text != "") - if hasText { - message = alert.Text - } - - for _, attachment := range alert.Attachments { - message = nonemptyAppendNewline(message) - message += attachment.Title + "\n" - message += attachment.TitleLink + "\n\n" - message += attachment.Text - } - - return message, nil, Response{"ok", http.StatusOK} -} diff --git a/main.go b/main.go index 8fae602..dc5868b 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "encoding/xml" + "github.com/opthomas-prime/xmpp-webhook/parser" "io" "log" "mellium.im/sasl" @@ -79,11 +80,11 @@ func main() { log.Fatal("XMPP_ID, XMPP_PASS or XMPP_RECEIVERS not set") } - address, err := jid.Parse(xi) + myjid, err := jid.Parse(xi) panicOnErr(err) // connect to xmpp server - xmppSession, err := initXMPP(address, xp, skipTLSVerify, useXMPPS) + xmppSession, err := initXMPP(myjid, xp, skipTLSVerify, useXMPPS) panicOnErr(err) defer closeXMPP(xmppSession) @@ -115,7 +116,7 @@ func main() { reply := MessageBody{ Message: stanza.Message{ To: msg.From.Bare(), - From: address, + From: myjid, Type: stanza.ChatMessage, }, Body: msg.Body, @@ -141,7 +142,7 @@ func main() { _ = xmppSession.Encode(MessageBody{ Message: stanza.Message{ To: recipient, - From: address, + From: myjid, Type: stanza.ChatMessage, }, Body: m, @@ -150,9 +151,9 @@ func main() { } }() - // initialize handler for grafana alerts - http.Handle("/grafana", newMessageHandler(messages, grafanaParserFunc)) - http.Handle("/slack", newMessageHandler(messages, slackParserFunc)) + // initialize handlers with accociated parser functions + http.Handle("/grafana", newMessageHandler(messages, parser.GrafanaParserFunc)) + http.Handle("/slack", newMessageHandler(messages, parser.SlackParserFunc)) // listen for requests _ = http.ListenAndServe(":4321", nil) diff --git a/parser/common.go b/parser/common.go new file mode 100644 index 0000000..c25e5a2 --- /dev/null +++ b/parser/common.go @@ -0,0 +1,4 @@ +package parser + +const readErr string = "failed to read alert body" +const parseErr string = "failed to parse alert body" diff --git a/parser/grafana.go b/parser/grafana.go new file mode 100644 index 0000000..e1c93f9 --- /dev/null +++ b/parser/grafana.go @@ -0,0 +1,42 @@ +package parser + +import ( + "encoding/json" + "errors" + "io/ioutil" + "net/http" +) + +func GrafanaParserFunc(r *http.Request) (string, error) { + // get alert data from request + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return "", errors.New(readErr) + } + + alert := &struct { + Title string `json:"title"` + RuleURL string `json:"ruleUrl"` + State string `json:"state"` + Message string `json:"message"` + }{} + + // parse body into the alert struct + err = json.Unmarshal(body, &alert) + if err != nil { + return "", errors.New(parseErr) + } + + // contruct alert message + var message string + switch alert.State { + case "ok": + message = ":) " + alert.Title + default: + message = ":( " + alert.Title + "\n\n" + message += alert.Message + "\n\n" + message += alert.RuleURL + } + + return message, nil +} diff --git a/parser/slack-compatible.go b/parser/slack-compatible.go new file mode 100644 index 0000000..9ff703b --- /dev/null +++ b/parser/slack-compatible.go @@ -0,0 +1,44 @@ +package parser + +import ( + "encoding/json" + "errors" + "io/ioutil" + "net/http" +) + +func SlackParserFunc(r *http.Request) (string, error) { + // get alert data from request + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return "", errors.New(readErr) + } + + alert := struct { + Text string `json:"text"` + Attachments []struct { + Title string `json:"title"` + TitleLink string `json:"title_link"` + Text string `json:"text"` + } `json:"attachments"` + }{} + + // parse body into the alert struct + err = json.Unmarshal(body, &alert) + if err != nil { + return "", errors.New(parseErr) + } + + // contruct alert message + message := alert.Text + for _, attachment := range alert.Attachments { + if len(message) > 0 { + message = message + "\n" + } + message += attachment.Title + "\n" + message += attachment.TitleLink + "\n\n" + message += attachment.Text + } + + return message, nil +}