Browse Source

Parse Webhook payload into actual event

Closes #427.

Change-Id: I67faac8df63e0d55fcce4ca5f9ab50e26c04d6b3
Antoine Pelisse 9 years ago
committed by Glenn Lewis
parent
commit
94a3cd9f53
2 changed files with 178 additions and 0 deletions
  1. +69
    -0
      github/messages.go
  2. +109
    -0
      github/messages_test.go

+ 69
- 0
github/messages.go View File

@ -14,6 +14,7 @@ import (
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"hash"
@ -30,6 +31,35 @@ const (
sha512Prefix = "sha512"
// signatureHeader is the GitHub header key used to pass the HMAC hexdigest.
signatureHeader = "X-Hub-Signature"
// eventTypeHeader is the Github header key used to pass the event type.
eventTypeHeader = "X-Github-Event"
)
var (
// eventTypeMapping maps webhooks types to their corresponding go-github struct types.
eventTypeMapping = map[string]string{
"commit_comment": "CommitCommentEvent",
"create": "CreateEvent",
"delete": "DeleteEvent",
"deployment": "DeploymentEvent",
"deployment_status": "DeploymentStatusEvent",
"fork": "ForkEvent",
"gollum": "GollumEvent",
"issue_comment": "IssueCommentEvent",
"issues": "IssuesEvent",
"member": "MemberEvent",
"membership": "MembershipEvent",
"page_build": "PageBuildEvent",
"public": "PublicEvent",
"pull_request_review_comment": "PullRequestReviewCommentEvent",
"pull_request": "PullRequestEvent",
"push": "PushEvent",
"repository": "RepositoryEvent",
"release": "ReleaseEvent",
"status": "StatusEvent",
"team_add": "TeamAddEvent",
"watch": "WatchEvent",
}
)
// genMAC generates the HMAC signature for a message provided the secret key
@ -117,3 +147,42 @@ func validateSignature(signature string, payload, secretKey []byte) error {
}
return nil
}
// WebHookType returns the event type of webhook request r.
func WebHookType(r *http.Request) string {
return r.Header.Get(eventTypeHeader)
}
// ParseWebHook parses the event payload. For recognized event types, a
// value of the corresponding struct type will be returned (as returned
// by Event.Payload()). An error will be returned for unrecognized event
// types.
//
// Example usage:
//
// func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// payload, err := github.ValidatePayload(r, s.webhookSecretKey)
// if err != nil { ... }
// event, err := github.ParseWebHook(github.WebHookType(r), payload)
// if err != nil { ... }
// switch event := event.(type) {
// case CommitCommentEvent:
// processCommitCommentEvent(event)
// case CreateEvent:
// processCreateEvent(event)
// ...
// }
// }
//
func ParseWebHook(messageType string, payload []byte) (interface{}, error) {
eventType, ok := eventTypeMapping[messageType]
if !ok {
return nil, fmt.Errorf("unknown X-Github-Event in message: %v", messageType)
}
event := Event{
Type: &eventType,
RawPayload: (*json.RawMessage)(&payload),
}
return event.Payload(), nil
}

+ 109
- 0
github/messages_test.go View File

@ -7,7 +7,9 @@ package github
import (
"bytes"
"encoding/json"
"net/http"
"reflect"
"testing"
)
@ -79,3 +81,110 @@ func TestValidatePayload(t *testing.T) {
}
}
}
func TestParseWebHook(t *testing.T) {
tests := []struct {
payload interface{}
messageType string
}{
{
payload: &CommitCommentEvent{},
messageType: "commit_comment",
},
{
payload: &CreateEvent{},
messageType: "create",
},
{
payload: &DeleteEvent{},
messageType: "delete",
},
{
payload: &DeploymentEvent{},
messageType: "deployment",
},
{
payload: &DeploymentStatusEvent{},
messageType: "deployment_status",
},
{
payload: &ForkEvent{},
messageType: "fork",
},
{
payload: &GollumEvent{},
messageType: "gollum",
},
{
payload: &IssueCommentEvent{},
messageType: "issue_comment",
},
{
payload: &IssuesEvent{},
messageType: "issues",
},
{
payload: &MemberEvent{},
messageType: "member",
},
{
payload: &MembershipEvent{},
messageType: "membership",
},
{
payload: &PageBuildEvent{},
messageType: "page_build",
},
{
payload: &PublicEvent{},
messageType: "public",
},
{
payload: &PullRequestEvent{},
messageType: "pull_request",
},
{
payload: &PullRequestReviewCommentEvent{},
messageType: "pull_request_review_comment",
},
{
payload: &PushEvent{},
messageType: "push",
},
{
payload: &ReleaseEvent{},
messageType: "release",
},
{
payload: &RepositoryEvent{},
messageType: "repository",
},
{
payload: &StatusEvent{},
messageType: "status",
},
{
payload: &TeamAddEvent{},
messageType: "team_add",
},
{
payload: &WatchEvent{},
messageType: "watch",
},
}
for _, test := range tests {
p, err := json.Marshal(test.payload)
if err != nil {
t.Fatalf("Marshal(%#v): %v", test.payload, err)
}
got, err := ParseWebHook(test.messageType, p)
if err != nil {
t.Fatalf("ParseWebHook: %v", err)
}
if want := test.payload; !reflect.DeepEqual(got, want) {
t.Errorf("ParseWebHook(%#v, %#v) = %#v, want %#v", test.messageType, p, got, want)
}
}
}

Loading…
Cancel
Save