Skip to content

Commit c6507a6

Browse files
joeybloggsjoeybloggs
authored andcommitted
Add Processing + registration functionality
* Also updated/added comments
1 parent 28bd494 commit c6507a6

File tree

4 files changed

+222
-59
lines changed

4 files changed

+222
-59
lines changed

github/github.go

Lines changed: 94 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,33 @@
11
package github
22

3-
import "github.com/joeybloggs/webhooks"
3+
import (
4+
"crypto/hmac"
5+
"crypto/sha1"
6+
"encoding/hex"
7+
"encoding/json"
8+
"io/ioutil"
9+
"net/http"
10+
11+
"github.com/joeybloggs/webhooks"
12+
)
413

514
// Webhook instance contains all methods needed to process events
615
type Webhook struct {
7-
provider webhooks.Provider
16+
provider webhooks.Provider
17+
secret string
18+
eventFuncs map[Event]webhooks.ProcessPayloadFunc
819
}
920

1021
// Config defines the configuration to create a new GitHubWebhook instance
1122
type Config struct {
12-
Provider webhooks.Provider
23+
Secret string
1324
}
1425

1526
// Event defines a GitHub hook event type
1627
type Event string
1728

1829
// GitHub hook types
1930
const (
20-
// AnyEvent Event = "*"
2131
CommitCommentEvent Event = "commit_comment"
2232
CreateEvent Event = "create"
2333
DeleteEvent Event = "delete"
@@ -53,19 +63,89 @@ const (
5363
IssueSubtype EventSubtype = "issues"
5464
)
5565

56-
// Provider returns the Webhook's provider
57-
func (w Webhook) Provider() webhooks.Provider {
58-
return w.provider
66+
// New creates and returns a WebHook instance denoted by the Provider type
67+
func New(config *Config) *Webhook {
68+
return &Webhook{
69+
provider: webhooks.GitHub,
70+
secret: config.Secret,
71+
eventFuncs: map[Event]webhooks.ProcessPayloadFunc{},
72+
}
5973
}
6074

61-
// UnderlyingProvider returns the Config's Provider
62-
func (c Config) UnderlyingProvider() webhooks.Provider {
63-
return c.Provider
75+
// Provider returns the current hooks provider ID
76+
func (hook Webhook) Provider() webhooks.Provider {
77+
return hook.provider
6478
}
6579

66-
// New creates and returns a WebHook instance denoted by the Provider type
67-
func New(config *Config) *Webhook {
68-
return &Webhook{
69-
provider: config.Provider,
80+
// RegisterEvents registers the function to call when the specified event(s) are encountered
81+
func (hook Webhook) RegisterEvents(fn webhooks.ProcessPayloadFunc, events ...Event) {
82+
83+
for _, event := range events {
84+
hook.eventFuncs[event] = fn
85+
}
86+
}
87+
88+
// ParsePayload parses and verifies the payload and fires off the mapped function, if it exists.
89+
func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) {
90+
91+
event := r.Header.Get("X-GitHub-Event")
92+
if len(event) == 0 {
93+
http.Error(w, "400 Bad Request - Missing X-GitHub-Event Header", http.StatusBadRequest)
94+
return
95+
}
96+
97+
gitHubEvent := Event(event)
98+
99+
fn, ok := hook.eventFuncs[gitHubEvent]
100+
// if no event registered
101+
if !ok {
102+
return
103+
}
104+
105+
payload, err := ioutil.ReadAll(r.Body)
106+
if err != nil {
107+
http.Error(w, err.Error(), http.StatusInternalServerError)
108+
return
109+
}
110+
111+
// If we have a Secret set, we should check the MAC
112+
if len(hook.secret) > 0 {
113+
114+
signature := r.Header.Get("X-Hub-Signature")
115+
116+
if len(signature) == 0 {
117+
http.Error(w, "403 Forbidden - Missing X-Hub-Signature required for HMAC verification", http.StatusForbidden)
118+
return
119+
}
120+
121+
mac := hmac.New(sha1.New, []byte(hook.secret))
122+
_, err := mac.Write(payload)
123+
if err != nil {
124+
http.Error(w, "400 Bad Request - HMAC verification failed with body parsing", http.StatusBadRequest)
125+
return
126+
}
127+
128+
expectedMAC := hex.EncodeToString(mac.Sum(nil))
129+
130+
if !hmac.Equal([]byte(signature[5:]), []byte(expectedMAC)) {
131+
http.Error(w, "403 Forbidden - HMAC verification failed", http.StatusForbidden)
132+
return
133+
}
134+
}
135+
136+
var results interface{}
137+
138+
switch gitHubEvent {
139+
case ReleaseEvent:
140+
var release ReleasePayload
141+
json.Unmarshal([]byte(payload), &release)
142+
results = release
70143
}
144+
145+
go func(fn webhooks.ProcessPayloadFunc, results interface{}) {
146+
147+
// put in recovery here!
148+
149+
fn(results)
150+
}(fn, results)
71151
}

github/github_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ func TestCommitCommentHook(t *testing.T) {
177177

178178
json.Unmarshal([]byte(body), &cc)
179179

180-
Equal(t, cc.Comment.Line, nil)
180+
Equal(t, cc.Comment.Line, 0)
181181
}
182182

183183
func TestCreateHook(t *testing.T) {

github/payload.go

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ type StatusPayload struct {
2222
ID int `json:"id"`
2323
SHA string `json:"sha"`
2424
Name string `json:"name"`
25-
TragetURL *string `json:"target_url"`
25+
TragetURL string `json:"target_url"`
2626
Context string `json:"context"`
27-
Desctiption *string `json:"description"`
27+
Desctiption string `json:"description"`
2828
State string `json:"state"`
2929
Commit StatusCommit `json:"commit"`
3030
Branches []Branch `json:"branches"`
@@ -58,7 +58,7 @@ type PushPayload struct {
5858
Created bool `json:"created"`
5959
Deleted bool `json:"deleted"`
6060
Forced bool `json:"forced"`
61-
BaseRef *string `json:"base_ref"`
61+
BaseRef string `json:"base_ref"`
6262
Compare string `json:"compare"`
6363
Commits []Commit `json:"commits"`
6464
HeadCommit HeadCommit `json:"head_commit"`
@@ -159,7 +159,7 @@ type DeploymentPayload struct {
159159
Sender Sender `json:"sender"`
160160
}
161161

162-
// CommitComment contains the information for GitHub's commit_comment hook event
162+
// CommitCommentPayload contains the information for GitHub's commit_comment hook event
163163
type CommitCommentPayload struct {
164164
Action string `json:"action"`
165165
RefType string `json:"ref_type"`
@@ -245,7 +245,7 @@ type Repository struct {
245245
Size int `json:"size"`
246246
StargazersCount int `json:"stargazers_count"`
247247
WatchersCount int `json:"watchers_count"`
248-
Language *string `json:"language"`
248+
Language string `json:"language"`
249249
HasIssues bool `json:"has_issues"`
250250
HasDownloads bool `json:"has_downloads"`
251251
HasWiki bool `json:"has_wiki"`
@@ -326,9 +326,9 @@ type Comment struct {
326326
HTMLURL string `json:"html_url"`
327327
ID int `json:"id"`
328328
User User `json:"user"`
329-
Position *int `json:"position"`
330-
Line *int `json:"line"`
331-
Path *string `json:"path"`
329+
Position int `json:"position"`
330+
Line int `json:"line"`
331+
Path string `json:"path"`
332332
CommitID string `json:"commit_id"`
333333
CreatedAt time.Time `json:"created_at"`
334334
UpdatedAt time.Time `json:"updated_at"`
@@ -344,7 +344,7 @@ type Deployment struct {
344344
Task string `json:"task"`
345345
//paylod
346346
Environment string `json:"environment"`
347-
Description *string `json:"description"`
347+
Description string `json:"description"`
348348
Creator Creator `json:"creator"`
349349
CreatedAt time.Time `json:"created_at"`
350350
UpdatedAt time.Time `json:"updated_at"`
@@ -358,7 +358,7 @@ type DeploymentStatus struct {
358358
ID int `json:"id"`
359359
State string `json:"state"`
360360
Creator Creator `json:"creator"`
361-
Description *string `json:"description"`
361+
Description string `json:"description"`
362362
TargetURL string `json:"target_url"`
363363
CreatedAt time.Time `json:"created_at"`
364364
UpdatedAt time.Time `json:"updated_at"`
@@ -374,22 +374,22 @@ type Forkee struct {
374374

375375
// Page contains GitHub's page information
376376
type Page struct {
377-
PageName string `json:"page_name"`
378-
Title string `json:"title"`
379-
Summary *string `json:"summary"`
380-
Action string `json:"action"`
381-
SHA string `json:"sha"`
382-
HTMLURL string `json:"html_url"`
377+
PageName string `json:"page_name"`
378+
Title string `json:"title"`
379+
Summary string `json:"summary"`
380+
Action string `json:"action"`
381+
SHA string `json:"sha"`
382+
HTMLURL string `json:"html_url"`
383383
}
384384

385-
// Page contains GitHub's label information
385+
// Label contains GitHub's label information
386386
type Label struct {
387387
URL string `json:"url"`
388388
Name string `json:"name"`
389389
Color string `json:"color"`
390390
}
391391

392-
// Page contains GitHub's issue information
392+
// Issue contains GitHub's issue information
393393
type Issue struct {
394394
URL string `json:"url"`
395395
LabelsURL string `json:"labels_url"`
@@ -403,8 +403,8 @@ type Issue struct {
403403
Labels []Label `json:"labels"`
404404
State string `json:"state"`
405405
Locked bool `json:"locked"`
406-
Assignee *string `json:"assignee"`
407-
Milestone *string `json:"milestone"`
406+
Assignee string `json:"assignee"`
407+
Milestone string `json:"milestone"`
408408
Comments int `json:"comments"`
409409
CreatedAt time.Time `json:"created_at"`
410410
UpdatedAt time.Time `json:"updated_at"`
@@ -567,9 +567,9 @@ type PullRequest struct {
567567
UpdatedAt time.Time `json:"updated_at"`
568568
ClosedAt time.Time `json:"closed_at"`
569569
MergedAt time.Time `json:"merged_at"`
570-
MergeCommitSHA *string `json:"merge_commit_sha"`
571-
Assignee *string `json:"assignee"`
572-
Milestone *string `json:"milestone"`
570+
MergeCommitSHA string `json:"merge_commit_sha"`
571+
Assignee string `json:"assignee"`
572+
Milestone string `json:"milestone"`
573573
CommitsURL string `json:"commits_url"`
574574
ReviewCommentsURL string `json:"review_comments_url"`
575575
ReviewCommentURL string `json:"review_comment_url"`
@@ -579,9 +579,9 @@ type PullRequest struct {
579579
Base Base `json:"base"`
580580
Links LinksPullRequest `json:"_links"`
581581
Merged bool `json:"merged"`
582-
Mergable *bool `json:"mergeable"`
582+
Mergable bool `json:"mergeable"`
583583
MergableState string `json:"mergeable_state"`
584-
MergedBy *string `json:"merged_by"`
584+
MergedBy string `json:"merged_by"`
585585
Comments int `json:"comments"`
586586
ReviewComments int `json:"review_comments"`
587587
Commits int `json:"commits"`
@@ -633,10 +633,10 @@ type Release struct {
633633
AssetsURL string `json:"assets_url"`
634634
UploadURL string `json:"upload_url"`
635635
HTMLURL string `json:"html_url"`
636-
ID string `json:"id"`
636+
ID int `json:"id"`
637637
TagName string `json:"tag_name"`
638638
TargetCommitish string `json:"target_commitish"`
639-
Name *string `json:"name"`
639+
Name string `json:"name"`
640640
Draft bool `json:"draft"`
641641
Author Author `json:"author"`
642642
Prelelease bool `json:"prerelease"`
@@ -645,7 +645,7 @@ type Release struct {
645645
Assets []string `json:"assets"`
646646
TarballURL string `json:"tarball_url"`
647647
ZipballURL string `json:"zipball_url"`
648-
Body *string `json:"body"`
648+
Body string `json:"body"`
649649
}
650650

651651
// BranchCommit contains GitHub's branch commit information
@@ -695,7 +695,7 @@ type StatusCommit struct {
695695
Commit StatusCommitInner `json:"commit"`
696696
URL string `json:"url"`
697697
HTMLURL string `json:"html_url"`
698-
CommentsURL string `json:"comments_url`
698+
CommentsURL string `json:"comments_url"`
699699
Author Author `json:"author"`
700700
Committer Commiter `json:"committer"`
701701
Parents []string `json:"parents"`

0 commit comments

Comments
 (0)