Browse Source

Handle incoming JIRA webhook requests

kegan/jira-webhook-2
Kegan Dougal 8 years ago
parent
commit
5d08ba7368
  1. 3
      src/github.com/matrix-org/go-neb/api.go
  2. 67
      src/github.com/matrix-org/go-neb/services/jira/jira.go
  3. 30
      src/github.com/matrix-org/go-neb/services/jira/webhook/webhook.go

3
src/github.com/matrix-org/go-neb/api.go

@ -140,6 +140,9 @@ func (wh *webhookHandler) handle(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(500)
return
}
log.WithFields(log.Fields{
"service_id": service.ServiceID(),
}).Print("Incoming webhook")
service.OnReceiveWebhook(w, req, cli)
}

67
src/github.com/matrix-org/go-neb/services/jira/jira.go

@ -10,6 +10,7 @@ import (
"github.com/matrix-org/go-neb/matrix"
"github.com/matrix-org/go-neb/plugin"
"github.com/matrix-org/go-neb/realms/jira"
"github.com/matrix-org/go-neb/realms/jira/urls"
"github.com/matrix-org/go-neb/services/jira/webhook"
"github.com/matrix-org/go-neb/types"
"html"
@ -222,7 +223,46 @@ func (s *jiraService) Plugin(roomID string) plugin.Plugin {
}
func (s *jiraService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *matrix.Client) {
webhook.OnReceiveRequest(w, req, cli)
eventProjectKey, event, httpErr := webhook.OnReceiveRequest(req)
if httpErr != nil {
log.WithError(httpErr).Print("Failed to handle JIRA webhook")
w.WriteHeader(500)
return
}
// grab base jira url
jurl, err := urls.ParseJIRAURL(event.Issue.Self)
if err != nil {
log.WithError(err).Print("Failed to parse base JIRA URL")
w.WriteHeader(500)
return
}
// work out the HTML to send
htmlText := htmlForEvent(event, jurl.Base)
if htmlText == "" {
log.Print("Unable to process event")
w.WriteHeader(200)
return
}
// send message into each configured room
for roomID, roomConfig := range s.Rooms {
for _, realmConfig := range roomConfig.Realms {
for pkey, projectConfig := range realmConfig.Projects {
if pkey != eventProjectKey || !projectConfig.Track {
continue
}
_, msgErr := cli.SendMessageEvent(
roomID, "m.room.message", matrix.GetHTMLMessage("m.notice", htmlText),
)
if msgErr != nil {
log.WithFields(log.Fields{
log.ErrorKey: msgErr,
"project": pkey,
"room_id": roomID,
}).Print("Failed to send notice into room")
}
}
}
}
}
func (s *jiraService) realmIDForProject(roomID, projectKey string) string {
@ -335,6 +375,31 @@ func htmlSummaryForIssue(issue *jira.Issue) string {
)
}
// htmlForEvent formats a webhook event as HTML. Returns an empty string if there is nothing to send/cannot
// be parsed.
func htmlForEvent(whe *webhook.Event, jiraBaseURL string) string {
action := ""
if whe.WebhookEvent == "jira:issue_updated" {
action = "updated"
} else if whe.WebhookEvent == "jira:issue_deleted" {
action = "deleted"
} else if whe.WebhookEvent == "jira:issue_created" {
action = "created"
} else {
return ""
}
summaryHTML := htmlSummaryForIssue(&whe.Issue)
return fmt.Sprintf("%s %s <b>%s</b> - %s %s",
html.EscapeString(whe.User.Name),
html.EscapeString(action),
html.EscapeString(whe.Issue.Key),
summaryHTML,
html.EscapeString(jiraBaseURL+"browse/"+whe.Issue.Key),
)
}
func init() {
types.RegisterService(func(serviceID, webhookEndpointURL string) types.Service {
return &jiraService{id: serviceID, webhookEndpointURL: webhookEndpointURL}

30
src/github.com/matrix-org/go-neb/services/jira/webhook/webhook.go

@ -1,14 +1,15 @@
package webhook
import (
"encoding/json"
"fmt"
log "github.com/Sirupsen/logrus"
"github.com/andygrunwald/go-jira"
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/errors"
"github.com/matrix-org/go-neb/matrix"
"github.com/matrix-org/go-neb/realms/jira"
"net/http"
"strings"
)
type jiraWebhook struct {
@ -21,6 +22,13 @@ type jiraWebhook struct {
Enabled bool `json:"enabled"`
}
type Event struct {
WebhookEvent string `json:"webhookEvent"`
Timestamp int64 `json:"timestamp"`
User jira.User `json:"user"`
Issue jira.Issue `json:"issue"`
}
// RegisterHook checks to see if this user is allowed to track the given projects and then tracks them.
func RegisterHook(jrealm *realms.JIRARealm, projects []string, userID, webhookEndpointURL string) error {
// Tracking means that a webhook may need to be created on the remote JIRA installation.
@ -89,9 +97,23 @@ func RegisterHook(jrealm *realms.JIRARealm, projects []string, userID, webhookEn
return createWebhook(jrealm, webhookEndpointURL, userID)
}
// OnReceiveRequest is called when JIRA hits NEB with an update
func OnReceiveRequest(w http.ResponseWriter, req *http.Request, cli *matrix.Client) {
w.WriteHeader(200) // Do nothing
// OnReceiveRequest is called when JIRA hits NEB with an update.
// Returns the project key and webhook event, or an error.
func OnReceiveRequest(req *http.Request) (string, *Event, *errors.HTTPError) {
// extract the JIRA webhook event JSON
defer req.Body.Close()
var whe Event
err := json.NewDecoder(req.Body).Decode(&whe)
if err != nil {
return "", nil, &errors.HTTPError{err, "Failed to parse request JSON", 400}
}
if err != nil {
return "", nil, &errors.HTTPError{err, "Failed to parse JIRA URL", 400}
}
projKey := strings.Split(whe.Issue.Key, "-")[0]
projKey = strings.ToUpper(projKey)
return projKey, &whe, nil
}
func createWebhook(jrealm *realms.JIRARealm, webhookEndpointURL, userID string) error {

Loading…
Cancel
Save