Browse Source

Merge pull request #5 from matrix-org/kegan/github-webhook

Send GH webhook notifications based on GH service config
pull/6/head
Kegsay 8 years ago
committed by GitHub
parent
commit
e9ebaceecf
  1. 12
      src/github.com/matrix-org/go-neb/api.go
  2. 2
      src/github.com/matrix-org/go-neb/clients/clients.go
  3. 2
      src/github.com/matrix-org/go-neb/goneb.go
  4. 2
      src/github.com/matrix-org/go-neb/services/echo/echo.go
  5. 43
      src/github.com/matrix-org/go-neb/services/github/github.go
  6. 34
      src/github.com/matrix-org/go-neb/services/github/webhook/webhook.go
  7. 3
      src/github.com/matrix-org/go-neb/types/types.go

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

@ -44,7 +44,8 @@ func (*configureAuthHandler) OnIncomingRequest(req *http.Request) (interface{},
}
type webhookHandler struct {
db *database.ServiceDB
db *database.ServiceDB
clients *clients.Clients
}
func (wh *webhookHandler) handle(w http.ResponseWriter, req *http.Request) {
@ -57,7 +58,14 @@ func (wh *webhookHandler) handle(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(404)
return
}
service.OnReceiveWebhook(w, req)
cli, err := wh.clients.Client(service.ServiceUserID())
if err != nil {
log.WithError(err).WithField("user_id", service.ServiceUserID()).Print(
"Failed to retrieve matrix client instance")
w.WriteHeader(500)
return
}
service.OnReceiveWebhook(w, req, cli)
}
type configureClientHandler struct {

2
src/github.com/matrix-org/go-neb/clients/clients.go

@ -22,7 +22,7 @@ type Clients struct {
func New(db *database.ServiceDB) *Clients {
clients := &Clients{
db: db,
clients: make(map[string]clientEntry),
clients: make(map[string]clientEntry), // user_id => clientEntry
}
return clients
}

2
src/github.com/matrix-org/go-neb/goneb.go

@ -35,7 +35,7 @@ func main() {
http.Handle("/admin/configureClient", server.MakeJSONAPI(&configureClientHandler{db: db, clients: clients}))
http.Handle("/admin/configureService", server.MakeJSONAPI(&configureServiceHandler{db: db, clients: clients}))
http.Handle("/admin/configureAuth", server.MakeJSONAPI(&configureAuthHandler{db: db}))
wh := &webhookHandler{db: db}
wh := &webhookHandler{db: db, clients: clients}
http.HandleFunc("/services/hooks/", wh.handle)
http.ListenAndServe(bindAddress, nil)

2
src/github.com/matrix-org/go-neb/services/echo/echo.go

@ -30,7 +30,7 @@ func (e *echoService) Plugin(roomID string) plugin.Plugin {
},
}
}
func (e *echoService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request) {
func (e *echoService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *matrix.Client) {
w.WriteHeader(200) // Do nothing
}

43
src/github.com/matrix-org/go-neb/services/github/github.go

@ -21,13 +21,19 @@ var ownerRepoIssueRegex = regexp.MustCompile("([A-z0-9-_]+)/([A-z0-9-_]+)#([0-9]
type githubService struct {
id string
UserID string
Rooms []string
Rooms map[string][]string // room_id => ["push","issue","pull_request"]
}
func (s *githubService) ServiceUserID() string { return s.UserID }
func (s *githubService) ServiceID() string { return s.id }
func (s *githubService) ServiceType() string { return "github" }
func (s *githubService) RoomIDs() []string { return s.Rooms }
func (s *githubService) RoomIDs() []string {
var keys []string
for k := range s.Rooms {
keys = append(keys, k)
}
return keys
}
func (s *githubService) Plugin(roomID string) plugin.Plugin {
return plugin.Plugin{
Commands: []plugin.Command{},
@ -62,9 +68,36 @@ func (s *githubService) Plugin(roomID string) plugin.Plugin {
},
}
}
func (s *githubService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request) {
// defer entirely to the webhook package
webhook.OnReceiveRequest(w, req, "")
func (s *githubService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *matrix.Client) {
evType, repo, msg, err := webhook.OnReceiveRequest(req, "")
if err != nil {
w.WriteHeader(err.Code)
return
}
for roomID, notif := range s.Rooms {
notifyRoom := false
for _, notifyType := range notif {
if evType == notifyType {
notifyRoom = true
break
}
}
if notifyRoom {
log.WithFields(log.Fields{
"type": evType,
"msg": msg,
"repo": repo,
"room_id": roomID,
}).Print("Sending notification to room")
_, e := cli.SendMessageEvent(roomID, "m.room.message", msg)
if e != nil {
log.WithError(e).WithField("room_id", roomID).Print(
"Failed to send notification to room.")
}
}
}
w.WriteHeader(200)
}
// githubClient returns a github Client which can perform Github API operations.

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

@ -8,23 +8,26 @@ import (
"fmt"
log "github.com/Sirupsen/logrus"
"github.com/google/go-github/github"
"github.com/matrix-org/go-neb/errors"
"github.com/matrix-org/go-neb/matrix"
"html"
"io/ioutil"
"net/http"
"strings"
)
// OnReceiveRequest processes incoming github webhook requests. The secretToken
// parameter is optional.
func OnReceiveRequest(w http.ResponseWriter, r *http.Request, secretToken string) {
// OnReceiveRequest processes incoming github webhook requests and returns a
// matrix message to send, along with parsed repo information.
// The secretToken, if supplied, will be used to verify the request is from
// Github. If it isn't, an error is returned.
func OnReceiveRequest(r *http.Request, secretToken string) (string, *github.Repository, *matrix.HTMLMessage, *errors.HTTPError) {
// Verify the HMAC signature if NEB was configured with a secret token
eventType := r.Header.Get("X-GitHub-Event")
signatureSHA1 := r.Header.Get("X-Hub-Signature")
content, err := ioutil.ReadAll(r.Body)
if err != nil {
log.WithError(err).Print("Failed to read Github webhook body")
w.WriteHeader(400)
return
return "", nil, nil, &errors.HTTPError{nil, "Failed to parse body", 400}
}
// Verify request if a secret token has been supplied.
if secretToken != "" {
@ -34,16 +37,14 @@ func OnReceiveRequest(w http.ResponseWriter, r *http.Request, secretToken string
if err != nil {
log.WithError(err).WithField("X-Hub-Signature", sigHex).Print(
"Failed to decode signature as hex.")
w.WriteHeader(400)
return
return "", nil, nil, &errors.HTTPError{nil, "Failed to decode signature", 400}
}
if !checkMAC([]byte(content), sigBytes, []byte(secretToken)) {
log.WithFields(log.Fields{
"X-Hub-Signature": signatureSHA1,
}).Print("Received Github event which failed MAC check.")
w.WriteHeader(403)
return
return "", nil, nil, &errors.HTTPError{nil, "Bad signature", 403}
}
}
@ -55,16 +56,11 @@ func OnReceiveRequest(w http.ResponseWriter, r *http.Request, secretToken string
htmlStr, repo, err := parseGithubEvent(eventType, content)
if err != nil {
log.WithError(err).Print("Failed to parse github event")
w.WriteHeader(500)
return
return "", nil, nil, &errors.HTTPError{nil, "Failed to parse github event", 500}
}
if err := handleWebhookEvent(eventType, htmlStr, repo); err != nil {
log.WithError(err).Print("Failed to handle Github webhook event")
w.WriteHeader(500)
return
}
w.WriteHeader(200)
msg := matrix.GetHTMLMessage("m.notice", htmlStr)
return eventType, repo, &msg, nil
}
// checkMAC reports whether messageMAC is a valid HMAC tag for message.
@ -122,10 +118,6 @@ func parseGithubEvent(eventType string, data []byte) (string, *github.Repository
return "", nil, fmt.Errorf("Unrecognized event type")
}
func handleWebhookEvent(eventType string, htmlStr string, repo *github.Repository) error {
return nil
}
func pullRequestHTMLMessage(p github.PullRequestEvent) string {
var actionTarget string
if p.PullRequest.Assignee != nil && p.PullRequest.Assignee.Login != nil {

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

@ -3,6 +3,7 @@ package types
import (
"encoding/json"
"errors"
"github.com/matrix-org/go-neb/matrix"
"github.com/matrix-org/go-neb/plugin"
"net/http"
"net/url"
@ -33,7 +34,7 @@ type Service interface {
ServiceType() string
RoomIDs() []string
Plugin(roomID string) plugin.Plugin
OnReceiveWebhook(w http.ResponseWriter, req *http.Request)
OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *matrix.Client)
}
var servicesByType = map[string]func(string) Service{}

Loading…
Cancel
Save