Compare commits

...

35 Commits

Author SHA1 Message Date
Dean Karn ced2e979bc Merge pull request #19 from kairen/add-author-association
Add parse author association for GitHub comment
2017-10-22 09:17:54 -07:00
kairen d60a03e52a Add parse author association for GitHub comment 2017-10-20 16:14:26 +08:00
Dean Karn f553bfaa59 Update README.md 2017-10-01 19:29:18 -07:00
Dean Karn 903279e458 add test for confidential issues gitlab 2017-10-01 19:24:09 -07:00
Dean Karn 5462959f1e Merge pull request #18 from tulir/patch-2
[GitLab] Add support for confidential issues
2017-10-01 19:17:33 -07:00
Dean Karn 4964805803 Merge pull request #17 from tulir/patch-1
[GitLab] Fix type of SourceProjectID in MergeRequest struct
2017-10-01 19:16:55 -07:00
Tulir Asokan 13e6611c00 Add separate payload type for confidential issues 2017-10-01 23:13:19 +03:00
Tulir Asokan b9424ab72e Add support for confidential GitLab issues 2017-10-01 13:56:19 +03:00
Tulir Asokan 203bf4218b Fix type of SourceProjectID in MergeRequest struct
It should be an int64, but was a string.
https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#comment-on-merge-request

The incorrect type caused the MergeRequest field in comment event payloads to be empty.
2017-09-29 19:54:03 +00:00
Dean Karn 5e4be82c0b Update README.md 2017-08-17 08:39:34 -07:00
Dean Karn cd89a10b64 Merge pull request #16 from samuelkarp/pr-label
payload: add Label to PullRequestPayload
2017-08-17 08:34:59 -07:00
Samuel Karp e120e3b3ba payload: add Label to PullRequestPayload 2017-08-12 17:24:29 -07:00
Dean Karn a5141d656b Add customizable Logger interface + info,error and debugs (#14)
* Add customizable Logger interface + info,error and debugs
* update travis.yml to atest golang versions
2017-07-15 12:12:11 -07:00
Dean Karn 9a8b92d028 Update README 2017-05-04 15:04:26 -04:00
Dean Karn 248dae5b83 Merge branch 'rakeshbala-fix/githubPushPayload' into v3 2017-05-04 15:02:51 -04:00
Rakesh Balasubramanian cc075dfe29 fix(github): adds sha to commits in push payload 2017-05-02 11:25:35 -04:00
Dean Karn c93876b3e9 Merge pull request #10 from go-playground/add-handler-support
Add handler support
2017-04-27 19:24:15 -04:00
Dean Karn 0926003ddf .travis.yml tweak for gopkg.in 2017-04-27 19:19:51 -04:00
Dean Karn 58dd13d367 Add Handler function
Handler(...) return an http.Handler of the webhook for use within your own Mux
2017-04-27 17:57:51 -04:00
Dean Karn 4fa39fdfab Add .travis.yml config 2017-04-27 17:40:55 -04:00
Dean Karn fccbba5986 Update README example typo 2017-04-22 12:47:22 -04:00
Dean Karn 0d7e42cf96 correct some spelling 2017-04-21 22:13:16 -04:00
Dean Karn c9ac93f3b3 Update README 2017-04-21 22:06:42 -04:00
Dean Karn 2d256610b0 Update BitBucket + GitLab Payloads 2017-04-21 22:03:45 -04:00
Dean Karn 05a2f7cd8d refine payload interface{} to types 2017-04-21 20:40:02 -04:00
Dean Karn 5ed22cdd66 Initial GitHub Webhook revamp 2017-04-21 19:45:07 -04:00
Dean Karn 2e471dc89c Update MergerRequestEvents to MergeRequestEvents 2017-04-21 16:11:04 -04:00
Dean Karn 64819086ff Update README.md 2017-04-08 11:48:29 -04:00
Dean Karn fe7552fcf4 Add GitLab support 2017-04-08 11:40:22 -04:00
Dean Karn ca13186bfa Update README.md
Adjustments due to GitHub's new markdown parser
2017-03-27 15:19:31 -04:00
Dean Karn 1e0ece40df cleanup imports + README for v2 2016-10-20 19:51:20 -04:00
Dean Karn 60d6ca11e3 Merge pull request #3 from alrs/headers
HTTP Header Passed to ProcessPayloadFunc
2016-10-20 19:42:36 -04:00
Dean Karn 622d26f48f Merge pull request #4 from go-playground/v1
merge v1 into v2-development
2016-10-20 19:39:08 -04:00
Lars Lehtonen 53781ac0e7 Added header param to README 2016-10-20 16:02:45 -07:00
Lars Lehtonen 16a6ac7a61 Added http headers as another param to ProcessPayloadFunc 2016-10-20 15:53:15 -07:00
17 changed files with 9928 additions and 1799 deletions
+39
View File
@@ -0,0 +1,39 @@
language: go
go:
- 1.7.6
- 1.8.3
- tip
matrix:
allow_failures:
- go: tip
notifications:
email:
recipients: dean.karn@gmail.com
on_success: change
on_failure: always
before_install:
- go get -u github.com/go-playground/overalls
- go get -u github.com/mattn/goveralls
- go get -u golang.org/x/tools/cmd/cover
- go get -u github.com/golang/lint/golint
- go get -u github.com/gordonklaus/ineffassign
- mkdir -p $GOPATH/src/gopkg.in
- ln -s $GOPATH/src/github.com/$TRAVIS_REPO_SLUG $GOPATH/src/gopkg.in/webhooks.v2
- ln -s $GOPATH/src/github.com/$TRAVIS_REPO_SLUG $GOPATH/src/gopkg.in/webhooks.v3
before_script:
- go vet ./...
script:
- gofmt -d -s .
- golint ./...
- ineffassign ./
- go test -v ./...
- go test -race
after_success: |
[ $TRAVIS_GO_VERSION = 1.8.3 ] &&
overalls -project="github.com/go-playground/webhooks" -covermode=count -ignore=.git,examples -debug &&
goveralls -coverprofile=overalls.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN
+25 -31
View File
@@ -1,14 +1,13 @@
Library webhooks Library webhooks
================ ================
<img align="right" src="https://raw.githubusercontent.com/go-playground/webhooks/v1/logo.png"> <img align="right" src="https://raw.githubusercontent.com/go-playground/webhooks/v3/logo.png">![Project status](https://img.shields.io/badge/version-3.3.1-green.svg)
![Project status](https://img.shields.io/badge/version-1.0-green.svg) [![Build Status](https://travis-ci.org/go-playground/webhooks.svg?branch=v3)](https://travis-ci.org/go-playground/webhooks)
[![Build Status](https://semaphoreci.com/api/v1/projects/5b9e2eda-8f8d-40aa-8cb4-e3f6120171fe/587820/badge.svg)](https://semaphoreci.com/joeybloggs/webhooks) [![Coverage Status](https://coveralls.io/repos/go-playground/webhooks/badge.svg?branch=v3&service=github)](https://coveralls.io/github/go-playground/webhooks?branch=v3)
[![Coverage Status](https://coveralls.io/repos/go-playground/webhooks/badge.svg?branch=v1&service=github)](https://coveralls.io/github/go-playground/webhooks?branch=v1)
[![Go Report Card](https://goreportcard.com/badge/go-playground/webhooks)](https://goreportcard.com/report/go-playground/webhooks) [![Go Report Card](https://goreportcard.com/badge/go-playground/webhooks)](https://goreportcard.com/report/go-playground/webhooks)
[![GoDoc](https://godoc.org/gopkg.in/go-playground/webhooks.v1?status.svg)](https://godoc.org/gopkg.in/go-playground/webhooks.v1) [![GoDoc](https://godoc.org/gopkg.in/go-playground/webhooks.v3?status.svg)](https://godoc.org/gopkg.in/go-playground/webhooks.v3)
![License](https://img.shields.io/dub/l/vibe-d.svg) ![License](https://img.shields.io/dub/l/vibe-d.svg)
Library webhooks allows for easy recieving and parsing of GitHub & Bitbucket Webhook Events Library webhooks allows for easy receiving and parsing of GitHub, Bitbucket and GitLab Webhook Events
Features: Features:
@@ -17,27 +16,25 @@ Features:
Notes: Notes:
* Github - Currently only accepting json payloads. * Currently only accepting json payloads.
Installation Installation
------------ ------------
Use go get. Use go get.
go get gopkg.in/go-playground/webhooks.v1 ```shell
go get -u gopkg.in/go-playground/webhooks.v3
```
or to update Then import the package into your own code.
go get -u gopkg.in/go-playground/webhooks.v1 import "gopkg.in/go-playground/webhooks.v3"
Then import the validator package into your own code. Usage and Documentation
import "gopkg.in/go-playground/webhooks.v1"
Usage and documentation
------ ------
Please see http://godoc.org/gopkg.in/go-playground/webhooks.v1 for detailed usage docs. Please see http://godoc.org/gopkg.in/go-playground/webhooks.v3 for detailed usage docs.
##### Examples: ##### Examples:
@@ -49,8 +46,8 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"gopkg.in/go-playground/webhooks.v1" "gopkg.in/go-playground/webhooks.v3"
"gopkg.in/go-playground/webhooks.v1/github" "gopkg.in/go-playground/webhooks.v3/github"
) )
const ( const (
@@ -59,6 +56,7 @@ const (
) )
func main() { func main() {
hook := github.New(&github.Config{Secret: "MyGitHubSuperSecretSecrect...?"}) hook := github.New(&github.Config{Secret: "MyGitHubSuperSecretSecrect...?"})
hook.RegisterEvents(HandleRelease, github.ReleaseEvent) hook.RegisterEvents(HandleRelease, github.ReleaseEvent)
hook.RegisterEvents(HandlePullRequest, github.PullRequestEvent) hook.RegisterEvents(HandlePullRequest, github.PullRequestEvent)
@@ -70,14 +68,14 @@ func main() {
} }
// HandleRelease handles GitHub release events // HandleRelease handles GitHub release events
func HandleRelease(payload interface{}) { func HandleRelease(payload interface{}, header webhooks.Header) {
fmt.Println("Handling Release") fmt.Println("Handling Release")
pl := payload.(github.ReleasePayload) pl := payload.(github.ReleasePayload)
// only want to compile on full releases // only want to compile on full releases
if pl.Release.Draft || pl.Release.Prelelease || pl.Release.TargetCommitish != "master" { if pl.Release.Draft || pl.Release.Prerelease || pl.Release.TargetCommitish != "master" {
return return
} }
@@ -86,7 +84,7 @@ func HandleRelease(payload interface{}) {
} }
// HandlePullRequest handles GitHub pull_request events // HandlePullRequest handles GitHub pull_request events
func HandlePullRequest(payload interface{}) { func HandlePullRequest(payload interface{}, header webhooks.Header) {
fmt.Println("Handling Pull Request") fmt.Println("Handling Pull Request")
@@ -105,8 +103,8 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"gopkg.in/go-playground/webhooks.v1" "gopkg.in/go-playground/webhooks.v3"
"gopkg.in/go-playground/webhooks.v1/github" "gopkg.in/go-playground/webhooks.v3/github"
) )
const ( const (
@@ -115,6 +113,7 @@ const (
) )
func main() { func main() {
hook := github.New(&github.Config{Secret: "MyGitHubSuperSecretSecrect...?"}) hook := github.New(&github.Config{Secret: "MyGitHubSuperSecretSecrect...?"})
hook.RegisterEvents(HandleMultiple, github.ReleaseEvent, github.PullRequestEvent) // Add as many as you want hook.RegisterEvents(HandleMultiple, github.ReleaseEvent, github.PullRequestEvent) // Add as many as you want
@@ -125,7 +124,7 @@ func main() {
} }
// HandleMultiple handles multiple GitHub events // HandleMultiple handles multiple GitHub events
func HandleMultiple(payload interface{}) { func HandleMultiple(payload interface{}, header webhooks.Header) {
fmt.Println("Handling Payload..") fmt.Println("Handling Payload..")
@@ -147,14 +146,9 @@ func HandleMultiple(payload interface{}) {
Contributing Contributing
------ ------
Pull requests for other service like BitBucket are welcome! Pull requests for other services are welcome!
There will always be a development branch for each version i.e. `v1-development`. In order to contribute, If the changes being proposed or requested are breaking changes, please create an issue for discussion.
please make your pull requests against those branches.
If the changes being proposed or requested are breaking changes, please create an issue, for discussion
or create a pull request against the highest development branch for example this package has a
v1 and v1-development branch however, there will also be a v2-development branch even though v2 doesn't exist yet.
License License
------ ------
+43 -27
View File
@@ -2,10 +2,11 @@ package bitbucket
import ( import (
"encoding/json" "encoding/json"
"fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"gopkg.in/go-playground/webhooks.v1" "gopkg.in/go-playground/webhooks.v3"
) )
// Webhook instance contains all methods needed to process events // Webhook instance contains all methods needed to process events
@@ -27,6 +28,7 @@ type Event string
const ( const (
RepoPushEvent Event = "repo:push" RepoPushEvent Event = "repo:push"
RepoForkEvent Event = "repo:fork" RepoForkEvent Event = "repo:fork"
RepoUpdatedEvent Event = "repo:updated"
RepoCommitCommentCreatedEvent Event = "repo:commit_comment_created" RepoCommitCommentCreatedEvent Event = "repo:commit_comment_created"
RepoCommitStatusCreatedEvent Event = "repo:commit_status_created" RepoCommitStatusCreatedEvent Event = "repo:commit_status_created"
RepoCommitStatusUpdatedEvent Event = "repo:commit_status_updated" RepoCommitStatusUpdatedEvent Event = "repo:commit_status_updated"
@@ -36,7 +38,7 @@ const (
PullRequestCreatedEvent Event = "pullrequest:created" PullRequestCreatedEvent Event = "pullrequest:created"
PullRequestUpdatedEvent Event = "pullrequest:updated" PullRequestUpdatedEvent Event = "pullrequest:updated"
PullRequestApprovedEvent Event = "pullrequest:approved" PullRequestApprovedEvent Event = "pullrequest:approved"
PullRequestApprovalRemovedEvent Event = "pullrequest:unapproved" PullRequestUnapprovedEvent Event = "pullrequest:unapproved"
PullRequestMergedEvent Event = "pullrequest:fulfilled" PullRequestMergedEvent Event = "pullrequest:fulfilled"
PullRequestDeclinedEvent Event = "pullrequest:rejected" PullRequestDeclinedEvent Event = "pullrequest:rejected"
PullRequestCommentCreatedEvent Event = "pullrequest:comment_created" PullRequestCommentCreatedEvent Event = "pullrequest:comment_created"
@@ -68,112 +70,126 @@ func (hook Webhook) RegisterEvents(fn webhooks.ProcessPayloadFunc, events ...Eve
// ParsePayload parses and verifies the payload and fires off the mapped function, if it exists. // ParsePayload parses and verifies the payload and fires off the mapped function, if it exists.
func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) { func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) {
webhooks.DefaultLog.Info("Parsing Payload...")
uuid := r.Header.Get("X-Hook-UUID") uuid := r.Header.Get("X-Hook-UUID")
if uuid == "" { if uuid == "" {
webhooks.DefaultLog.Error("Missing X-Hook-UUID Header")
http.Error(w, "400 Bad Request - Missing X-Hook-UUID Header", http.StatusBadRequest) http.Error(w, "400 Bad Request - Missing X-Hook-UUID Header", http.StatusBadRequest)
return return
} }
webhooks.DefaultLog.Debug(fmt.Sprintf("X-Hook-UUID:%s", uuid))
if uuid != hook.uuid { if uuid != hook.uuid {
http.Error(w, "403 Forbidden - Missing X-Hook-UUID does not match", http.StatusForbidden) webhooks.DefaultLog.Error(fmt.Sprintf("X-Hook-UUID does not match configured uuid of %s", hook.uuid))
http.Error(w, "403 Forbidden - X-Hook-UUID does not match", http.StatusForbidden)
return return
} }
event := r.Header.Get("X-Event-Key") event := r.Header.Get("X-Event-Key")
if event == "" { if event == "" {
webhooks.DefaultLog.Error("Missing X-Event-Key Header")
http.Error(w, "400 Bad Request - Missing X-Event-Key Header", http.StatusBadRequest) http.Error(w, "400 Bad Request - Missing X-Event-Key Header", http.StatusBadRequest)
return return
} }
webhooks.DefaultLog.Debug(fmt.Sprintf("X-Event-Key:%s", event))
bitbucketEvent := Event(event) bitbucketEvent := Event(event)
fn, ok := hook.eventFuncs[bitbucketEvent] fn, ok := hook.eventFuncs[bitbucketEvent]
// if no event registered // if no event registered
if !ok { if !ok {
webhooks.DefaultLog.Info(fmt.Sprintf("Webhook Event %s not registered, it is recommended to setup only events in bitbucket that will be registered in the webhook to avoid unnecessary traffic and reduce potential attack vectors.", event))
return return
} }
payload, err := ioutil.ReadAll(r.Body) payload, err := ioutil.ReadAll(r.Body)
if err != nil || len(payload) == 0 { if err != nil || len(payload) == 0 {
http.Error(w, "Error reading Body", http.StatusInternalServerError) webhooks.DefaultLog.Error("Issue reading Payload")
http.Error(w, "Issue reading Payload", http.StatusInternalServerError)
return return
} }
webhooks.DefaultLog.Debug(fmt.Sprintf("Payload:%s", string(payload)))
hd := webhooks.Header(r.Header)
switch bitbucketEvent { switch bitbucketEvent {
case RepoPushEvent: case RepoPushEvent:
var pl RepoPushPayload var pl RepoPushPayload
json.Unmarshal([]byte(payload), &pl) json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, pl) hook.runProcessPayloadFunc(fn, pl, hd)
case RepoForkEvent: case RepoForkEvent:
var pl RepoForkPayload var pl RepoForkPayload
json.Unmarshal([]byte(payload), &pl) json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, pl) hook.runProcessPayloadFunc(fn, pl, hd)
case RepoUpdatedEvent:
var pl RepoUpdatedPayload
json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, pl, hd)
case RepoCommitCommentCreatedEvent: case RepoCommitCommentCreatedEvent:
var pl RepoCommitCommentCreatedPayload var pl RepoCommitCommentCreatedPayload
json.Unmarshal([]byte(payload), &pl) json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, pl) hook.runProcessPayloadFunc(fn, pl, hd)
case RepoCommitStatusCreatedEvent: case RepoCommitStatusCreatedEvent:
var pl RepoCommitStatusCreatedPayload var pl RepoCommitStatusCreatedPayload
json.Unmarshal([]byte(payload), &pl) json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, pl) hook.runProcessPayloadFunc(fn, pl, hd)
case RepoCommitStatusUpdatedEvent: case RepoCommitStatusUpdatedEvent:
var pl RepoCommitStatusUpdatedPayload var pl RepoCommitStatusUpdatedPayload
json.Unmarshal([]byte(payload), &pl) json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, pl) hook.runProcessPayloadFunc(fn, pl, hd)
case IssueCreatedEvent: case IssueCreatedEvent:
var pl IssueCreatedPayload var pl IssueCreatedPayload
json.Unmarshal([]byte(payload), &pl) json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, pl) hook.runProcessPayloadFunc(fn, pl, hd)
case IssueUpdatedEvent: case IssueUpdatedEvent:
var pl IssueUpdatedPayload var pl IssueUpdatedPayload
json.Unmarshal([]byte(payload), &pl) json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, pl) hook.runProcessPayloadFunc(fn, pl, hd)
case IssueCommentCreatedEvent: case IssueCommentCreatedEvent:
var pl IssueCommentCreatedPayload var pl IssueCommentCreatedPayload
json.Unmarshal([]byte(payload), &pl) json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, pl) hook.runProcessPayloadFunc(fn, pl, hd)
case PullRequestCreatedEvent: case PullRequestCreatedEvent:
var pl PullRequestCreatedPayload var pl PullRequestCreatedPayload
json.Unmarshal([]byte(payload), &pl) json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, pl) hook.runProcessPayloadFunc(fn, pl, hd)
case PullRequestUpdatedEvent: case PullRequestUpdatedEvent:
var pl PullRequestUpdatedPayload var pl PullRequestUpdatedPayload
json.Unmarshal([]byte(payload), &pl) json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, pl) hook.runProcessPayloadFunc(fn, pl, hd)
case PullRequestApprovedEvent: case PullRequestApprovedEvent:
var pl PullRequestApprovedPayload var pl PullRequestApprovedPayload
json.Unmarshal([]byte(payload), &pl) json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, pl) hook.runProcessPayloadFunc(fn, pl, hd)
case PullRequestApprovalRemovedEvent: case PullRequestUnapprovedEvent:
var pl PullRequestApprovalRemovedPayload var pl PullRequestUnapprovedPayload
json.Unmarshal([]byte(payload), &pl) json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, pl) hook.runProcessPayloadFunc(fn, pl, hd)
case PullRequestMergedEvent: case PullRequestMergedEvent:
var pl PullRequestMergedPayload var pl PullRequestMergedPayload
json.Unmarshal([]byte(payload), &pl) json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, pl) hook.runProcessPayloadFunc(fn, pl, hd)
case PullRequestDeclinedEvent: case PullRequestDeclinedEvent:
var pl PullRequestDeclinedPayload var pl PullRequestDeclinedPayload
json.Unmarshal([]byte(payload), &pl) json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, pl) hook.runProcessPayloadFunc(fn, pl, hd)
case PullRequestCommentCreatedEvent: case PullRequestCommentCreatedEvent:
var pl PullRequestCommentCreatedPayload var pl PullRequestCommentCreatedPayload
json.Unmarshal([]byte(payload), &pl) json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, pl) hook.runProcessPayloadFunc(fn, pl, hd)
case PullRequestCommentUpdatedEvent: case PullRequestCommentUpdatedEvent:
var pl PullRequestCommentUpdatedPayload var pl PullRequestCommentUpdatedPayload
json.Unmarshal([]byte(payload), &pl) json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, pl) hook.runProcessPayloadFunc(fn, pl, hd)
case PullRequestCommentDeletedEvent: case PullRequestCommentDeletedEvent:
var pl PullRequestCommentDeletedPayload var pl PullRequestCommentDeletedPayload
json.Unmarshal([]byte(payload), &pl) json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, pl) hook.runProcessPayloadFunc(fn, pl, hd)
} }
} }
func (hook Webhook) runProcessPayloadFunc(fn webhooks.ProcessPayloadFunc, results interface{}) { func (hook Webhook) runProcessPayloadFunc(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) {
go func(fn webhooks.ProcessPayloadFunc, results interface{}) { go func(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) {
fn(results) fn(results, header)
}(fn, results) }(fn, results, header)
} }
+183 -27
View File
@@ -9,7 +9,7 @@ import (
"time" "time"
. "gopkg.in/go-playground/assert.v1" . "gopkg.in/go-playground/assert.v1"
"gopkg.in/go-playground/webhooks.v1" "gopkg.in/go-playground/webhooks.v3"
) )
// NOTES: // NOTES:
@@ -24,12 +24,12 @@ import (
// //
// //
const ( const (
port = 3010 port = 3009
path = "/webhooks" path = "/webhooks"
) )
// HandlePayload handles GitHub event(s) // HandlePayload handles GitHub event(s)
func HandlePayload(payload interface{}) { func HandlePayload(payload interface{}, header webhooks.Header) {
} }
@@ -39,10 +39,30 @@ func TestMain(m *testing.M) {
// setup // setup
hook = New(&Config{UUID: "MY_UUID"}) hook = New(&Config{UUID: "MY_UUID"})
hook.RegisterEvents(HandlePayload, RepoPushEvent, RepoForkEvent, RepoCommitCommentCreatedEvent, RepoCommitStatusCreatedEvent, RepoCommitStatusUpdatedEvent, IssueCreatedEvent, IssueUpdatedEvent, IssueCommentCreatedEvent, PullRequestCreatedEvent, PullRequestUpdatedEvent, PullRequestApprovedEvent, PullRequestApprovalRemovedEvent, PullRequestMergedEvent, PullRequestDeclinedEvent, PullRequestCommentCreatedEvent, PullRequestCommentUpdatedEvent, PullRequestCommentDeletedEvent) hook.RegisterEvents(
HandlePayload,
RepoPushEvent,
RepoForkEvent,
RepoUpdatedEvent,
RepoCommitCommentCreatedEvent,
RepoCommitStatusCreatedEvent,
RepoCommitStatusUpdatedEvent,
IssueCreatedEvent,
IssueUpdatedEvent,
IssueCommentCreatedEvent,
PullRequestCreatedEvent,
PullRequestUpdatedEvent,
PullRequestApprovedEvent,
PullRequestUnapprovedEvent,
PullRequestMergedEvent,
PullRequestDeclinedEvent,
PullRequestCommentCreatedEvent,
PullRequestCommentUpdatedEvent,
PullRequestCommentDeletedEvent,
)
go webhooks.Run(hook, "127.0.0.1:"+strconv.Itoa(port), path) go webhooks.Run(hook, "127.0.0.1:"+strconv.Itoa(port), path)
time.Sleep(5000) time.Sleep(time.Millisecond * 500)
os.Exit(m.Run()) os.Exit(m.Run())
@@ -56,7 +76,7 @@ func TestProvider(t *testing.T) {
func TestUUIDMissingEvent(t *testing.T) { func TestUUIDMissingEvent(t *testing.T) {
payload := "{}" payload := "{}"
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Event-Key", "noneexistant_event") req.Header.Set("X-Event-Key", "noneexistant_event")
@@ -74,7 +94,7 @@ func TestUUIDMissingEvent(t *testing.T) {
func TestUUIDDoesNotMatchEvent(t *testing.T) { func TestUUIDDoesNotMatchEvent(t *testing.T) {
payload := "{}" payload := "{}"
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "THIS_DOES_NOT_MATCH") req.Header.Set("X-Hook-UUID", "THIS_DOES_NOT_MATCH")
@@ -92,7 +112,7 @@ func TestUUIDDoesNotMatchEvent(t *testing.T) {
func TestBadNoEventHeader(t *testing.T) { func TestBadNoEventHeader(t *testing.T) {
payload := "{}" payload := "{}"
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
@@ -110,7 +130,7 @@ func TestBadNoEventHeader(t *testing.T) {
func TestUnsubscribedEvent(t *testing.T) { func TestUnsubscribedEvent(t *testing.T) {
payload := "{}" payload := "{}"
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "noneexistant_event") req.Header.Set("X-Event-Key", "noneexistant_event")
@@ -129,7 +149,7 @@ func TestUnsubscribedEvent(t *testing.T) {
func TestBadBody(t *testing.T) { func TestBadBody(t *testing.T) {
payload := "" payload := ""
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "repo:push") req.Header.Set("X-Event-Key", "repo:push")
@@ -355,7 +375,7 @@ func TestRepoPush(t *testing.T) {
} }
` `
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "repo:push") req.Header.Set("X-Event-Key", "repo:push")
@@ -429,7 +449,7 @@ func TestRepoFork(t *testing.T) {
} }
` `
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "repo:fork") req.Header.Set("X-Event-Key", "repo:fork")
@@ -445,6 +465,142 @@ func TestRepoFork(t *testing.T) {
Equal(t, resp.StatusCode, http.StatusOK) Equal(t, resp.StatusCode, http.StatusOK)
} }
func TestRepoUpdated(t *testing.T) {
payload := `{
"actor": {
"type": "user",
"username": "emmap1",
"display_name": "Emma",
"uuid": "{a54f16da-24e9-4d7f-a3a7-b1ba2cd98aa3}",
"links": {
"self": {
"href": "https://api.bitbucket.org/api/2.0/users/emmap1"
},
"html": {
"href": "https://api.bitbucket.org/emmap1"
},
"avatar": {
"href": "https://bitbucket-api-assetroot.s3.amazonaws.com/c/photos/2015/Feb/26/3613917261-0-emmap1-avatar_avatar.png"
}
}
},
"repository": {
"type": "repository",
"links": {
"self": {
"href": "https://api.bitbucket.org/api/2.0/repositories/bitbucket/bitbucket"
},
"html": {
"href": "https://api.bitbucket.org/bitbucket/bitbucket"
},
"avatar": {
"href": "https://api-staging-assetroot.s3.amazonaws.com/c/photos/2014/Aug/01/bitbucket-logo-2629490769-3_avatar.png"
}
},
"uuid": "{673a6070-3421-46c9-9d48-90745f7bfe8e}",
"project": {
"type": "project",
"project": "Untitled project",
"uuid": "{3b7898dc-6891-4225-ae60-24613bb83080}",
"links": {
"html": {
"href": "https://bitbucket.org/account/user/teamawesome/projects/proj"
},
"avatar": {
"href": "https://bitbucket.org/account/user/teamawesome/projects/proj/avatar/32"
}
},
"key": "proj"
},
"full_name": "team_name/repo_name",
"name": "repo_name",
"website": "https://mywebsite.com/",
"owner": {
"type": "user",
"username": "emmap1",
"display_name": "Emma",
"uuid": "{a54f16da-24e9-4d7f-a3a7-b1ba2cd98aa3}",
"links": {
"self": {
"href": "https://api.bitbucket.org/api/2.0/users/emmap1"
},
"html": {
"href": "https://api.bitbucket.org/emmap1"
},
"avatar": {
"href": "https://bitbucket-api-assetroot.s3.amazonaws.com/c/photos/2015/Feb/26/3613917261-0-emmap1-avatar_avatar.png"
}
}
},
"scm": "git",
"is_private": true
},
"changes": {
"name": {
"new": "repository",
"old": "repository_name"
},
"website": {
"new": "http://www.example.com/",
"old": ""
},
"language": {
"new": "java",
"old": ""
},
"links": {
"new": {
"avatar": {
"href": "https://bitbucket.org/teamawesome/repository/avatar/32/"
},
"self": {
"href": "https://api.bitbucket.org/2.0/repositories/teamawesome/repository"
},
"html": {
"href": "https://bitbucket.org/teamawesome/repository"
}
},
"old": {
"avatar": {
"href": "https://bitbucket.org/teamawesome/repository_name/avatar/32/"
},
"self": {
"href": "https://api.bitbucket.org/2.0/repositories/teamawesome/repository_name"
},
"html": {
"href": "https://bitbucket.org/teamawesome/repository_name"
}
}
},
"description": {
"new": "This is a better description.",
"old": "This is a description."
},
"full_name": {
"new": "teamawesome/repository",
"old": "teamawesome/repository_name"
}
}
}
`
req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "repo:updated")
Equal(t, err, nil)
client := &http.Client{}
resp, err := client.Do(req)
Equal(t, err, nil)
defer resp.Body.Close()
Equal(t, resp.StatusCode, http.StatusOK)
}
func TestRepoCommitCommentCreated(t *testing.T) { func TestRepoCommitCommentCreated(t *testing.T) {
payload := `{ payload := `{
@@ -514,7 +670,7 @@ func TestRepoCommitCommentCreated(t *testing.T) {
} }
` `
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "repo:commit_comment_created") req.Header.Set("X-Event-Key", "repo:commit_comment_created")
@@ -588,7 +744,7 @@ func TestRepoCommitStatusCreated(t *testing.T) {
} }
` `
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "repo:commit_status_created") req.Header.Set("X-Event-Key", "repo:commit_status_created")
@@ -662,7 +818,7 @@ func TestRepoCommitStatusUpdated(t *testing.T) {
} }
` `
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "repo:commit_status_updated") req.Header.Set("X-Event-Key", "repo:commit_status_updated")
@@ -747,7 +903,7 @@ func TestIssueCreated(t *testing.T) {
} }
` `
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "issue:created") req.Header.Set("X-Event-Key", "issue:created")
@@ -864,7 +1020,7 @@ func TestIssueUpdated(t *testing.T) {
} }
` `
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "issue:updated") req.Header.Set("X-Event-Key", "issue:updated")
@@ -975,7 +1131,7 @@ func TestIssueCommentCreated(t *testing.T) {
} }
` `
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "issue:comment_created") req.Header.Set("X-Event-Key", "issue:comment_created")
@@ -1172,7 +1328,7 @@ func TestPullRequestCreated(t *testing.T) {
} }
` `
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "pullrequest:created") req.Header.Set("X-Event-Key", "pullrequest:created")
@@ -1369,7 +1525,7 @@ func TestPullRequestUpdated(t *testing.T) {
} }
` `
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "pullrequest:updated") req.Header.Set("X-Event-Key", "pullrequest:updated")
@@ -1585,7 +1741,7 @@ func TestPullRequestApproved(t *testing.T) {
} }
` `
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "pullrequest:approved") req.Header.Set("X-Event-Key", "pullrequest:approved")
@@ -1801,7 +1957,7 @@ func TestPullRequestApprovalRemoved(t *testing.T) {
} }
` `
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "pullrequest:unapproved") req.Header.Set("X-Event-Key", "pullrequest:unapproved")
@@ -1998,7 +2154,7 @@ func TestPullRequestMerged(t *testing.T) {
} }
` `
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "pullrequest:fulfilled") req.Header.Set("X-Event-Key", "pullrequest:fulfilled")
@@ -2195,7 +2351,7 @@ func TestPullRequestDeclined(t *testing.T) {
} }
` `
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "pullrequest:rejected") req.Header.Set("X-Event-Key", "pullrequest:rejected")
@@ -2418,7 +2574,7 @@ func TestPullRequestCommentCreated(t *testing.T) {
} }
` `
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "pullrequest:comment_created") req.Header.Set("X-Event-Key", "pullrequest:comment_created")
@@ -2641,7 +2797,7 @@ func TestPullRequestCommentUpdated(t *testing.T) {
} }
` `
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "pullrequest:comment_updated") req.Header.Set("X-Event-Key", "pullrequest:comment_updated")
@@ -2864,7 +3020,7 @@ func TestPullRequestCommentDeleted(t *testing.T) {
} }
` `
req, err := http.NewRequest("POST", "http://127.0.0.1:3010/webhooks", bytes.NewBuffer([]byte(payload))) req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hook-UUID", "MY_UUID") req.Header.Set("X-Hook-UUID", "MY_UUID")
req.Header.Set("X-Event-Key", "pull_request:comment_deleted") req.Header.Set("X-Event-Key", "pull_request:comment_deleted")
+435 -315
View File
@@ -2,153 +2,203 @@ package bitbucket
import "time" import "time"
// PullRequestCommentDeletedPayload is the Bitbucket pull_request:comment_deleted payload // RepoPushPayload is the Bitbucket repo:push payload
type PullRequestCommentDeletedPayload struct { type RepoPushPayload struct {
Actor User `json:"actor"` Actor Owner `json:"actor"`
Repository Repository `json:"repository"` Repository Repository `json:"repository"`
PullRequest PullRequest `json:"pullrequest"` Push struct {
Comment Comment `json:"comment"` Changes []struct {
} New struct {
Type string `json:"type"`
// PullRequestCommentUpdatedPayload is the Bitbucket pullrequest:comment_updated payload Name string `json:"name"`
type PullRequestCommentUpdatedPayload struct { Target struct {
Actor User `json:"actor"` Type string `json:"type"`
Repository Repository `json:"repository"` Hash string `json:"hash"`
PullRequest PullRequest `json:"pullrequest"` Author Owner `json:"author"`
Comment Comment `json:"comment"` Message string `json:"message"`
} Date time.Time `json:"date"`
Parents []struct {
// PullRequestCommentCreatedPayload is the Bitbucket pullrequest:comment_created payload Type string `json:"type"`
type PullRequestCommentCreatedPayload struct { Hash string `json:"hash"`
Actor User `json:"actor"` Links struct {
Repository Repository `json:"repository"` Self struct {
PullRequest PullRequest `json:"pullrequest"` Href string `json:"href"`
Comment Comment `json:"comment"` } `json:"self"`
} HTML struct {
Href string `json:"href"`
// PullRequestDeclinedPayload is the Bitbucket pullrequest:rejected payload } `json:"html"`
type PullRequestDeclinedPayload struct { } `json:"links"`
Actor User `json:"actor"` } `json:"parents"`
PullRequest PullRequest `json:"pullrequest"` Links struct {
Repository Repository `json:"repository"` Self struct {
} Href string `json:"href"`
} `json:"self"`
// PullRequestMergedPayload is the Bitbucket pullrequest:fulfilled payload HTML struct {
type PullRequestMergedPayload struct { Href string `json:"href"`
Actor User `json:"actor"` } `json:"html"`
PullRequest PullRequest `json:"pullrequest"` } `json:"links"`
Repository Repository `json:"repository"` } `json:"target"`
} Links struct {
Self struct {
// PullRequestApprovalRemovedPayload is the Bitbucket pullrequest:unapproved payload Href string `json:"href"`
type PullRequestApprovalRemovedPayload struct { } `json:"self"`
Actor User `json:"actor"` Commits struct {
PullRequest PullRequest `json:"pullrequest"` Href string `json:"href"`
Repository Repository `json:"repository"` } `json:"commits"`
Approval Approval `json:"approval"` HTML struct {
} Href string `json:"href"`
} `json:"html"`
// PullRequestApprovedPayload is the Bitbucket pullrequest:approved payload } `json:"links"`
type PullRequestApprovedPayload struct { } `json:"new"`
Actor User `json:"actor"` Old struct {
PullRequest PullRequest `json:"pullrequest"` Type string `json:"type"`
Repository Repository `json:"repository"` Name string `json:"name"`
Approval Approval `json:"approval"` Target struct {
} Type string `json:"type"`
Hash string `json:"hash"`
// PullRequestUpdatedPayload is the Bitbucket pullrequest:updated payload Author Owner `json:"author"`
type PullRequestUpdatedPayload struct { Message string `json:"message"`
Actor User `json:"actor"` Date time.Time `json:"date"`
PullRequest PullRequest `json:"pullrequest"` Parents []struct {
Repository Repository `json:"repository"` Type string `json:"type"`
} Hash string `json:"hash"`
Links struct {
// PullRequestCreatedPayload is the Bitbucket pullrequest:created payload Self struct {
type PullRequestCreatedPayload struct { Href string `json:"href"`
Actor User `json:"actor"` } `json:"self"`
PullRequest PullRequest `json:"pullrequest"` HTML struct {
Repository Repository `json:"repository"` Href string `json:"href"`
} } `json:"html"`
} `json:"links"`
// IssueCommentCreatedPayload is the Bitbucket issue:comment_created payload } `json:"parents"`
type IssueCommentCreatedPayload struct { Links struct {
Actor User `json:"actor"` Self struct {
Repository Repository `json:"repository"` Href string `json:"href"`
Issue Issue `json:"issue"` } `json:"self"`
Comment Comment `json:"comment"` HTML struct {
} Href string `json:"href"`
} `json:"html"`
// IssueUpdatedPayload is the Bitbucket issue:updated payload } `json:"links"`
type IssueUpdatedPayload struct { } `json:"target"`
Actor User `json:"actor"` Links struct {
Issue Issue `json:"issue"` Self struct {
Repository Repository `json:"repository"` Href string `json:"href"`
Comment Comment `json:"comment"` } `json:"self"`
Changes IssueChanges `json:"changes"` Commits struct {
} Href string `json:"href"`
} `json:"commits"`
// IssueCreatedPayload is the Bitbucket issue:created payload HTML struct {
type IssueCreatedPayload struct { Href string `json:"href"`
Actor User `json:"actor"` } `json:"html"`
Issue Issue `json:"issue"` } `json:"links"`
Repository Repository `json:"repository"` } `json:"old"`
} Links struct {
HTML struct {
// RepoCommitStatusUpdatedPayload is the Bitbucket repo:commit_status_updated payload Href string `json:"href"`
type RepoCommitStatusUpdatedPayload struct { } `json:"html"`
Actor User `json:"actor"` Diff struct {
Repository Repository `json:"repository"` Href string `json:"href"`
CommitStatus CommitStatus `json:"commit_status"` } `json:"diff"`
} Commits struct {
Href string `json:"href"`
// RepoCommitStatusCreatedPayload is the Bitbucket repo:commit_status_created payload } `json:"commits"`
type RepoCommitStatusCreatedPayload struct { } `json:"links"`
Actor User `json:"actor"` Created bool `json:"created"`
Repository Repository `json:"repository"` Forced bool `json:"forced"`
CommitStatus CommitStatus `json:"commit_status"` Closed bool `json:"closed"`
} Commits []struct {
Hash string `json:"hash"`
// RepoCommitCommentCreatedPayload is the Bitbucket repo:commit_comment_created payload Type string `json:"type"`
type RepoCommitCommentCreatedPayload struct { Message string `json:"message"`
Actor User `json:"actor"` Author Owner `json:"author"`
Comment Comment `json:"comment"` Links struct {
Repository Repository `json:"repository"` Self struct {
Commit CommitHash `json:"commit"` Href string `json:"href"`
} `json:"self"`
HTML struct {
Href string `json:"href"`
} `json:"html"`
} `json:"links"`
} `json:"commits"`
Truncated bool `json:"truncated"`
} `json:"changes"`
} `json:"push"`
} }
// RepoForkPayload is the Bitbucket repo:fork payload // RepoForkPayload is the Bitbucket repo:fork payload
type RepoForkPayload struct { type RepoForkPayload struct {
Actor User `json:"actor"` Actor Owner `json:"actor"`
Repository Repository `json:"repository"` Repository Repository `json:"repository"`
Fork Repository `json:"fork"` Fork Repository `json:"fork"`
} }
// RepoPushPayload is the Bitbucket repo:push payload // RepoUpdatedPayload is the Bitbucket repo:updated payload
type RepoPushPayload struct { type RepoUpdatedPayload struct {
Actor User `json:"actor"` Actor Owner `json:"actor"`
Repository Repository `json:"repository"` Repository Repository `json:"repository"`
Push Push `json:"push"` Changes struct {
} Name struct {
// Approval is the common Bitbucket Issue Approval Sub Entity
type Approval struct {
Date time.Time `json:"date"`
User User `json:"user"`
}
// IssueChanges is the common Bitbucket Issue Changes Sub Entity
type IssueChanges struct {
Status IssueChangeStatus `json:"status"`
}
// IssueChangeStatus is the common Bitbucket Issue Change Status Sub Entity
type IssueChangeStatus struct {
Old string `json:"old"`
New string `json:"new"` New string `json:"new"`
Old string `json:"old"`
} `json:"name"`
Website struct {
New string `json:"new"`
Old string `json:"old"`
} `json:"website"`
Language struct {
New string `json:"new"`
Old string `json:"old"`
} `json:"language"`
Links struct {
New struct {
Avatar struct {
Href string `json:"href"`
} `json:"avatar"`
Self struct {
Href string `json:"href"`
} `json:"self"`
HTML struct {
Href string `json:"href"`
} `json:"html"`
} `json:"new"`
Old struct {
Avatar struct {
Href string `json:"href"`
} `json:"avatar"`
Self struct {
Href string `json:"href"`
} `json:"self"`
HTML struct {
Href string `json:"href"`
} `json:"html"`
} `json:"old"`
} `json:"links"`
Description struct {
New string `json:"new"`
Old string `json:"old"`
} `json:"description"`
FullName struct {
New string `json:"new"`
Old string `json:"old"`
} `json:"full_name"`
} `json:"changes"`
} }
// CommitStatus is the common Bitbucket CommitStatus Sub Entity // RepoCommitCommentCreatedPayload is the Bitbucket repo:commit_comment_created payload
type CommitStatus struct { type RepoCommitCommentCreatedPayload struct {
Actor Owner `json:"actor"`
Comment Comment `json:"comment"`
Repository Repository `json:"repository"`
Commit struct {
Hash string `json:"hash"`
} `json:"commit"`
}
// RepoCommitStatusCreatedPayload is the Bitbucket repo:commit_status_created payload
type RepoCommitStatusCreatedPayload struct {
Actor Owner `json:"actor"`
Repository Repository `json:"repository"`
CommitStatus struct {
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"` Description string `json:"description"`
State string `json:"state"` State string `json:"state"`
@@ -157,233 +207,303 @@ type CommitStatus struct {
Type string `json:"type"` Type string `json:"type"`
CreatedOn time.Time `json:"created_on"` CreatedOn time.Time `json:"created_on"`
UpdatedOn time.Time `json:"updated_on"` UpdatedOn time.Time `json:"updated_on"`
Links LinksSelfCommit `json:"links"` Links struct {
Commit struct {
Href string `json:"href"`
} `json:"commit"`
Self struct {
Href string `json:"href"`
} `json:"self"`
} `json:"links"`
} `json:"commit_status"`
} }
// Push is the common Bitbucket Push Sub Entity // RepoCommitStatusUpdatedPayload is the Bitbucket repo:commit_status_updated payload
type Push struct { type RepoCommitStatusUpdatedPayload struct {
Changes []Change `json:"changes"` Actor Owner `json:"actor"`
} Repository Repository `json:"repository"`
CommitStatus struct {
// Change is the common Bitbucket Change Sub Entity
type Change struct {
New ChangeData `json:"new"`
Old ChangeData `json:"old"`
Links LinksHTMLDiffCommits `json:"links"`
Created bool `json:"created"`
Forced bool `json:"forced"`
Closed bool `json:"closed"`
Commits []Commit `json:"commits"`
Truncated bool `json:"truncated"`
}
// ChangeData is the common Bitbucket ChangeData Sub Entity
type ChangeData struct {
Type string `json:"type"`
Name string `json:"name"` Name string `json:"name"`
Target Target `json:"target"` Description string `json:"description"`
Links LinksHTMLSelfCommits `json:"links"` State string `json:"state"`
Key string `json:"key"`
URL string `json:"url"`
Type string `json:"type"`
CreatedOn time.Time `json:"created_on"`
UpdatedOn time.Time `json:"updated_on"`
Links struct {
Commit struct {
Href string `json:"href"`
} `json:"commit"`
Self struct {
Href string `json:"href"`
} `json:"self"`
} `json:"links"`
} `json:"commit_status"`
} }
// Target is the common Bitbucket Target Sub Entity // IssueCreatedPayload is the Bitbucket issue:created payload
type Target struct { type IssueCreatedPayload struct {
Type string `json:"type"` Actor Owner `json:"actor"`
Hash string `json:"hash"` Issue Issue `json:"issue"`
Author User `json:"author"` Repository Repository `json:"repository"`
Message string `json:"message"` }
// IssueUpdatedPayload is the Bitbucket issue:updated payload
type IssueUpdatedPayload struct {
Actor Owner `json:"actor"`
Issue Issue `json:"issue"`
Repository Repository `json:"repository"`
Comment Comment `json:"comment"`
Changes struct {
Status struct {
Old string `json:"old"`
New string `json:"new"`
} `json:"status"`
} `json:"changes"`
}
// IssueCommentCreatedPayload is the Bitbucket pullrequest:created payload
type IssueCommentCreatedPayload struct {
Actor Owner `json:"actor"`
Repository Repository `json:"repository"`
Issue Issue `json:"issue"`
Comment Comment `json:"comment"`
}
// PullRequestCreatedPayload is the Bitbucket pullrequest:created payload
type PullRequestCreatedPayload struct {
Actor Owner `json:"actor"`
PullRequest PullRequest `json:"pullrequest"`
Repository Repository `json:"repository"`
}
// PullRequestUpdatedPayload is the Bitbucket pullrequest:updated payload
type PullRequestUpdatedPayload struct {
Actor Owner `json:"actor"`
PullRequest PullRequest `json:"pullrequest"`
Repository Repository `json:"repository"`
}
// PullRequestApprovedPayload is the Bitbucket pullrequest:approved payload
type PullRequestApprovedPayload struct {
Actor Owner `json:"actor"`
PullRequest PullRequest `json:"pullrequest"`
Repository Repository `json:"repository"`
Approval struct {
Date time.Time `json:"date"` Date time.Time `json:"date"`
Parents []Parent `json:"parents"` User Owner `json:"user"`
Links LinksHTMLSelf `json:"links"` } `json:"approval"`
} }
// Parent is the common Bitbucket Parent Sub Entity // PullRequestUnapprovedPayload is the Bitbucket pullrequest:unapproved payload
type Parent struct { type PullRequestUnapprovedPayload struct {
Actor Owner `json:"actor"`
PullRequest PullRequest `json:"pullrequest"`
Repository Repository `json:"repository"`
Approval struct {
Date time.Time `json:"date"`
User Owner `json:"user"`
} `json:"approval"`
}
// PullRequestMergedPayload is the Bitbucket pullrequest:fulfilled payload
type PullRequestMergedPayload struct {
Actor Owner `json:"actor"`
PullRequest PullRequest `json:"pullrequest"`
Repository Repository `json:"repository"`
}
// PullRequestDeclinedPayload is the Bitbucket pullrequest:rejected payload
type PullRequestDeclinedPayload struct {
Actor Owner `json:"actor"`
PullRequest PullRequest `json:"pullrequest"`
Repository Repository `json:"repository"`
}
// PullRequestCommentCreatedPayload is the Bitbucket pullrequest:comment_updated payload
type PullRequestCommentCreatedPayload struct {
Actor Owner `json:"actor"`
Repository Repository `json:"repository"`
PullRequest PullRequest `json:"pullrequest"`
Comment Comment `json:"comment"`
}
// PullRequestCommentUpdatedPayload is the Bitbucket pullrequest:comment_created payload
type PullRequestCommentUpdatedPayload struct {
Actor Owner `json:"actor"`
Repository Repository `json:"repository"`
PullRequest PullRequest `json:"pullrequest"`
Comment Comment `json:"comment"`
}
// PullRequestCommentDeletedPayload is the Bitbucket pullrequest:comment_deleted payload
type PullRequestCommentDeletedPayload struct {
Actor Owner `json:"actor"`
Repository Repository `json:"repository"`
PullRequest PullRequest `json:"pullrequest"`
Comment Comment `json:"comment"`
}
// Owner is the common Bitbucket Owner Sub Entity
type Owner struct {
Type string `json:"type"` Type string `json:"type"`
Hash string `json:"hash"`
Links LinksHTMLSelf `json:"links"`
}
// Commit is the common Bitbucket Commit Sub Entity
type Commit struct {
Hash string `json:"hash"`
Type string `json:"type"`
Message string `json:"message"`
Author User `json:"author"`
Links LinksHTMLSelf `json:"links"`
}
// User is the common Bitbucket User Entity
type User struct {
Username string `json:"username"` Username string `json:"username"`
DisplayName string `json:"display_name"` DisplayName string `json:"display_name"`
UUID string `json:"uuid"` UUID string `json:"uuid"`
Links Links `json:"links"` Links struct {
Self struct {
Href string `json:"href"`
} `json:"self"`
HTML struct {
Href string `json:"href"`
} `json:"html"`
Avatar struct {
Href string `json:"href"`
} `json:"avatar"`
} `json:"links"`
} }
// Repository is the common Bitbucket Repository Entity // Repository is the common Bitbucket Repository Sub Entity
type Repository struct { type Repository struct {
Links Links `json:"links"` Type string `json:"type"`
Links struct {
Self struct {
Href string `json:"href"`
} `json:"self"`
HTML struct {
Href string `json:"href"`
} `json:"html"`
Avatar struct {
Href string `json:"href"`
} `json:"avatar"`
} `json:"links"`
UUID string `json:"uuid"` UUID string `json:"uuid"`
Project Project `json:"project"`
FullName string `json:"full_name"` FullName string `json:"full_name"`
Name string `json:"name"` Name string `json:"name"`
Website string `json:"website"`
Owner Owner `json:"owner"`
Scm string `json:"scm"` Scm string `json:"scm"`
IsPrivate bool `json:"is_private"` IsPrivate bool `json:"is_private"`
} }
// Issue is the common Bitbucket Issue Entity // Project is the common Bitbucket Project Sub Entity
type Project struct {
Type string `json:"type"`
Project string `json:"project"`
UUID string `json:"uuid"`
Links struct {
HTML struct {
Href string `json:"href"`
} `json:"html"`
Avatar struct {
Href string `json:"href"`
} `json:"avatar"`
} `json:"links"`
Key string `json:"key"`
}
// Issue is the common Bitbucket Issue Sub Entity
type Issue struct { type Issue struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Component string `json:"component"` Component string `json:"component"`
Title string `json:"title"` Title string `json:"title"`
Content Content `json:"content"` Content struct {
Raw string `json:"raw"`
HTML string `json:"html"`
Markup string `json:"markup"`
} `json:"content"`
Priority string `json:"priority"` Priority string `json:"priority"`
State string `json:"state"` State string `json:"state"`
Type string `json:"type"` Type string `json:"type"`
Milestone Milestone `json:"milestone"` Milestone struct {
Version Version `json:"version"` Name string `json:"name"`
} `json:"milestone"`
Version struct {
Name string `json:"name"`
} `json:"version"`
CreatedOn time.Time `json:"created_on"` CreatedOn time.Time `json:"created_on"`
UpdatedOn time.Time `json:"updated_on"` UpdatedOn time.Time `json:"updated_on"`
Links LinksHTMLSelf `json:"links"` Links struct {
Self struct {
Href string `json:"href"`
} `json:"self"`
HTML struct {
Href string `json:"href"`
} `json:"html"`
} `json:"links"`
} }
// Comment is the common Bitbucket Comment Entity // Comment is the common Bitbucket Comment Sub Entity
type Comment struct { type Comment struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Parent ParentID `json:"parent"` Parent struct {
Content Content `json:"content"` ID int64 `json:"id"`
Inline Inline `json:"inline"` } `json:"parent"`
Content struct {
Raw string `json:"raw"`
HTML string `json:"html"`
Markup string `json:"markup"`
} `json:"content"`
Inline struct {
Path string `json:"path"`
From *int64 `json:"from"`
To int64 `json:"to"`
} `json:"inline"`
CreatedOn time.Time `json:"created_on"` CreatedOn time.Time `json:"created_on"`
UpdatedOn time.Time `json:"updated_on"` UpdatedOn time.Time `json:"updated_on"`
Links LinksHTMLSelf `json:"links"` Links struct {
Self struct {
Href string `json:"href"`
} `json:"self"`
HTML struct {
Href string `json:"href"`
} `json:"html"`
} `json:"links"`
} }
// PullRequest is the common Bitbucket PullRequest Entity // PullRequest is the common Bitbucket Pull Request Sub Entity
type PullRequest struct { type PullRequest struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
State string `json:"state"` State string `json:"state"`
Author User `json:"author"` Author Owner `json:"author"`
Source Source `json:"source"` Source struct {
Destination Destination `json:"destination"` Branch struct {
MergeCommit CommitHash `json:"merge_commit"` Name string `json:"name"`
Participants []User `json:"participants"` } `json:"branch"`
Reviewers []User `json:"reviewers"` Commit struct {
Hash string `json:"hash"`
} `json:"commit"`
Repository Repository `json:"repository"`
} `json:"source"`
Destination struct {
Branch struct {
Name string `json:"name"`
} `json:"branch"`
Commit struct {
Hash string `json:"hash"`
} `json:"commit"`
Repository Repository `json:"repository"`
} `json:"destination"`
MergeCommit struct {
Hash string `json:"hash"`
} `json:"merge_commit"`
Participants []Owner `json:"participants"`
Reviewers []Owner `json:"reviewers"`
CloseSourceBranch bool `json:"close_source_branch"` CloseSourceBranch bool `json:"close_source_branch"`
ClosedBy User `json:"closed_by"` ClosedBy Owner `json:"closed_by"`
Reason string `json:"reason"` Reason string `json:"reason"`
CreatedOn time.Time `json:"created_on"` CreatedOn time.Time `json:"created_on"`
UpdatedOn time.Time `json:"updated_on"` UpdatedOn time.Time `json:"updated_on"`
Links LinksHTMLSelf `json:"links"` Links struct {
} Self struct {
Href string `json:"href"`
// Destination is the common Bitbucket Destination Sub Entity } `json:"self"`
type Destination struct { HTML struct {
Branch Branch `json:"branch"` Href string `json:"href"`
Commit CommitHash `json:"commit"` } `json:"html"`
Repository Repository `json:"repository"` } `json:"links"`
}
// Source is the common Bitbucket Source Sub Entity
type Source struct {
Branch Branch `json:"branch"`
Commit CommitHash `json:"commit"`
Repository Repository `json:"repository"`
}
// Branch is the common Bitbucket Branch Sub Entity
type Branch struct {
Name string `json:"name"`
}
// CommitHash is the common Bitbucket CommitHash Sub Entity
type CommitHash struct {
Hash string `json:"hash"`
}
// Inline is the common Bitbucket Inline Sub Entity
type Inline struct {
Path string `json:"path"`
From *int64 `json:"from"`
To int64 `json:"to"`
}
// ParentID is the common Bitbucket ParentID Sub Entity
type ParentID struct {
ID int64 `json:"id"`
}
// Avatar is the common Bitbucket Avatar Sub Entity
type Avatar struct {
HREF string `json:"href"`
}
// HTML is the common Bitbucket HTML Sub Entity
type HTML struct {
HREF string `json:"href"`
}
// Self is the common Bitbucket Self Sub Entity
type Self struct {
HREF string `json:"href"`
}
// Diff is the common Bitbucket Diff Sub Entity
type Diff struct {
HREF string `json:"href"`
}
// Commits is the common Bitbucket Commits Sub Entity
type Commits struct {
HREF string `json:"href"`
}
// LinksSelfCommit is the common Bitbucket LinksSelfCommit Sub Entity
type LinksSelfCommit struct {
Self Self `json:"self"`
Commit Commits `json:"commit"`
}
// LinksHTMLSelfCommits is the common Bitbucket LinksHTMLSelfCommits Sub Entity
type LinksHTMLSelfCommits struct {
Self Self `json:"self"`
Commits Commits `json:"commits"`
HTML HTML `json:"html"`
}
// LinksHTMLDiffCommits is the common Bitbucket LinksHTMLDiffCommits Sub Entity
type LinksHTMLDiffCommits struct {
HTML HTML `json:"html"`
Diff Diff `json:"diff"`
Commits Commits `json:"commits"`
}
// Links is the common Bitbucket Links Sub Entity
type Links struct {
Avatar Avatar `json:"avatar"`
HTML HTML `json:"html"`
Self Self `json:"self"`
}
// LinksHTMLSelf is the common Bitbucket LinksHTMLSelf Sub Entity
type LinksHTMLSelf struct {
HTML HTML `json:"html"`
Self Self `json:"self"`
}
// Content is the common Bitbucket Content Sub Entity
type Content struct {
HTML string `json:"html"`
Markup string `json:"markup"`
Raw string `json:"raw"`
}
// Milestone is the common Bitbucket Milestone Sub Entity
type Milestone struct {
Name string `json:"name"`
}
// Version is the common Bitbucket Version Sub Entity
type Version struct {
Name string `json:"name"`
} }
+67
View File
@@ -0,0 +1,67 @@
package main
import (
"fmt"
"log"
"strconv"
"gopkg.in/go-playground/webhooks.v3"
"gopkg.in/go-playground/webhooks.v3/github"
)
const (
path = "/webhooks"
port = 3016
)
type myLogger struct {
PrintDebugs bool
}
func (l *myLogger) Info(msg string) {
log.Println(msg)
}
func (l *myLogger) Error(msg string) {
log.Println(msg)
}
func (l *myLogger) Debug(msg string) {
if !l.PrintDebugs {
return
}
log.Println(msg)
}
func main() {
// webhooks.DefaultLog=webhooks.NewLogger(true)
//
// or override with your own
webhooks.DefaultLog = &myLogger{PrintDebugs: true}
hook := github.New(&github.Config{Secret: "MyGitHubSuperSecretSecrect...?"})
hook.RegisterEvents(HandleMultiple, github.ReleaseEvent, github.PullRequestEvent) // Add as many as you want
err := webhooks.Run(hook, ":"+strconv.Itoa(port), path)
if err != nil {
fmt.Println(err)
}
}
// HandleMultiple handles multiple GitHub events
func HandleMultiple(payload interface{}, header webhooks.Header) {
fmt.Println("Handling Payload..")
switch payload.(type) {
case github.ReleasePayload:
release := payload.(github.ReleasePayload)
// Do whatever you want from here...
fmt.Printf("%+v", release)
case github.PullRequestPayload:
pullRequest := payload.(github.PullRequestPayload)
// Do whatever you want from here...
fmt.Printf("%+v", pullRequest)
}
}
+5 -6
View File
@@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"gopkg.in/go-playground/webhooks.v1" "gopkg.in/go-playground/webhooks.v3"
"gopkg.in/go-playground/webhooks.v1/github" "gopkg.in/go-playground/webhooks.v3/github"
) )
const ( const (
@@ -25,14 +25,13 @@ func main() {
} }
// HandleRelease handles GitHub release events // HandleRelease handles GitHub release events
func HandleRelease(payload interface{}) { func HandleRelease(payload interface{}, header webhooks.Header) {
fmt.Println("Handling Release") fmt.Println("Handling Release")
pl := payload.(github.ReleasePayload) pl := payload.(github.ReleasePayload)
// only want to compile on full releases // only want to compile on full releases
if pl.Release.Draft || pl.Release.Prelelease || pl.Release.TargetCommitish != "master" { if pl.Release.Draft || pl.Release.Prerelease || pl.Release.TargetCommitish != "master" {
return return
} }
@@ -41,7 +40,7 @@ func HandleRelease(payload interface{}) {
} }
// HandlePullRequest handles GitHub pull_request events // HandlePullRequest handles GitHub pull_request events
func HandlePullRequest(payload interface{}) { func HandlePullRequest(payload interface{}, header webhooks.Header) {
fmt.Println("Handling Pull Request") fmt.Println("Handling Pull Request")
+3 -4
View File
@@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"gopkg.in/go-playground/webhooks.v1" "gopkg.in/go-playground/webhooks.v3"
"gopkg.in/go-playground/webhooks.v1/github" "gopkg.in/go-playground/webhooks.v3/github"
) )
const ( const (
@@ -24,8 +24,7 @@ func main() {
} }
// HandleMultiple handles multiple GitHub events // HandleMultiple handles multiple GitHub events
func HandleMultiple(payload interface{}) { func HandleMultiple(payload interface{}, header webhooks.Header) {
fmt.Println("Handling Payload..") fmt.Println("Handling Payload..")
switch payload.(type) { switch payload.(type) {
+94 -37
View File
@@ -5,10 +5,11 @@ import (
"crypto/sha1" "crypto/sha1"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"gopkg.in/go-playground/webhooks.v1" "gopkg.in/go-playground/webhooks.v3"
) )
// Webhook instance contains all methods needed to process events // Webhook instance contains all methods needed to process events
@@ -37,16 +38,25 @@ const (
GollumEvent Event = "gollum" GollumEvent Event = "gollum"
IssueCommentEvent Event = "issue_comment" IssueCommentEvent Event = "issue_comment"
IssuesEvent Event = "issues" IssuesEvent Event = "issues"
LabelEvent Event = "label"
MemberEvent Event = "member" MemberEvent Event = "member"
MembershipEvent Event = "membership" MembershipEvent Event = "membership"
MilestoneEvent Event = "milestone"
OrganizationEvent Event = "organization"
OrgBlockEvent Event = "org_block"
PageBuildEvent Event = "page_build" PageBuildEvent Event = "page_build"
ProjectCardEvent Event = "project_card"
ProjectColumnEvent Event = "project_column"
ProjectEvent Event = "project"
PublicEvent Event = "public" PublicEvent Event = "public"
PullRequestReviewCommentEvent Event = "pull_request_review_comment"
PullRequestEvent Event = "pull_request" PullRequestEvent Event = "pull_request"
PullRequestReviewEvent Event = "pull_request_review"
PullRequestReviewCommentEvent Event = "pull_request_review_comment"
PushEvent Event = "push" PushEvent Event = "push"
RepositoryEvent Event = "repository"
ReleaseEvent Event = "release" ReleaseEvent Event = "release"
RepositoryEvent Event = "repository"
StatusEvent Event = "status" StatusEvent Event = "status"
TeamEvent Event = "team"
TeamAddEvent Event = "team_add" TeamAddEvent Event = "team_add"
WatchEvent Event = "watch" WatchEvent Event = "watch"
) )
@@ -87,36 +97,43 @@ func (hook Webhook) RegisterEvents(fn webhooks.ProcessPayloadFunc, events ...Eve
// ParsePayload parses and verifies the payload and fires off the mapped function, if it exists. // ParsePayload parses and verifies the payload and fires off the mapped function, if it exists.
func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) { func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) {
webhooks.DefaultLog.Info("Parsing Payload...")
event := r.Header.Get("X-GitHub-Event") event := r.Header.Get("X-GitHub-Event")
if len(event) == 0 { if len(event) == 0 {
webhooks.DefaultLog.Error("Missing X-GitHub-Event Header")
http.Error(w, "400 Bad Request - Missing X-GitHub-Event Header", http.StatusBadRequest) http.Error(w, "400 Bad Request - Missing X-GitHub-Event Header", http.StatusBadRequest)
return return
} }
webhooks.DefaultLog.Debug(fmt.Sprintf("X-GitHub-Event:%s", event))
gitHubEvent := Event(event) gitHubEvent := Event(event)
fn, ok := hook.eventFuncs[gitHubEvent] fn, ok := hook.eventFuncs[gitHubEvent]
// if no event registered // if no event registered
if !ok { if !ok {
webhooks.DefaultLog.Info(fmt.Sprintf("Webhook Event %s not registered, it is recommended to setup only events in github that will be registered in the webhook to avoid unnecessary traffic and reduce potential attack vectors.", event))
return return
} }
payload, err := ioutil.ReadAll(r.Body) payload, err := ioutil.ReadAll(r.Body)
if err != nil || len(payload) == 0 { if err != nil || len(payload) == 0 {
http.Error(w, "Error reading Body", http.StatusInternalServerError) webhooks.DefaultLog.Error("Issue reading Payload")
http.Error(w, "Issue reading Payload", http.StatusInternalServerError)
return return
} }
webhooks.DefaultLog.Debug(fmt.Sprintf("Payload:%s", string(payload)))
// If we have a Secret set, we should check the MAC // If we have a Secret set, we should check the MAC
if len(hook.secret) > 0 { if len(hook.secret) > 0 {
webhooks.DefaultLog.Info("Checking secret")
signature := r.Header.Get("X-Hub-Signature") signature := r.Header.Get("X-Hub-Signature")
if len(signature) == 0 { if len(signature) == 0 {
webhooks.DefaultLog.Error("Missing X-Hub-Signature required for HMAC verification")
http.Error(w, "403 Forbidden - Missing X-Hub-Signature required for HMAC verification", http.StatusForbidden) http.Error(w, "403 Forbidden - Missing X-Hub-Signature required for HMAC verification", http.StatusForbidden)
return return
} }
webhooks.DefaultLog.Debug(fmt.Sprintf("X-Hub-Signature:%s", signature))
mac := hmac.New(sha1.New, []byte(hook.secret)) mac := hmac.New(sha1.New, []byte(hook.secret))
mac.Write(payload) mac.Write(payload)
@@ -124,101 +141,141 @@ func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) {
expectedMAC := hex.EncodeToString(mac.Sum(nil)) expectedMAC := hex.EncodeToString(mac.Sum(nil))
if !hmac.Equal([]byte(signature[5:]), []byte(expectedMAC)) { if !hmac.Equal([]byte(signature[5:]), []byte(expectedMAC)) {
webhooks.DefaultLog.Error("HMAC verification failed")
http.Error(w, "403 Forbidden - HMAC verification failed", http.StatusForbidden) http.Error(w, "403 Forbidden - HMAC verification failed", http.StatusForbidden)
return return
} }
} }
// Make headers available to ProcessPayloadFunc as a webhooks type
hd := webhooks.Header(r.Header)
switch gitHubEvent { switch gitHubEvent {
case CommitCommentEvent: case CommitCommentEvent:
var cc CommitCommentPayload var cc CommitCommentPayload
json.Unmarshal([]byte(payload), &cc) json.Unmarshal([]byte(payload), &cc)
hook.runProcessPayloadFunc(fn, cc) hook.runProcessPayloadFunc(fn, cc, hd)
case CreateEvent: case CreateEvent:
var c CreatePayload var c CreatePayload
json.Unmarshal([]byte(payload), &c) json.Unmarshal([]byte(payload), &c)
hook.runProcessPayloadFunc(fn, c) hook.runProcessPayloadFunc(fn, c, hd)
case DeleteEvent: case DeleteEvent:
var d DeletePayload var d DeletePayload
json.Unmarshal([]byte(payload), &d) json.Unmarshal([]byte(payload), &d)
hook.runProcessPayloadFunc(fn, d) hook.runProcessPayloadFunc(fn, d, hd)
case DeploymentEvent: case DeploymentEvent:
var d DeploymentPayload var d DeploymentPayload
json.Unmarshal([]byte(payload), &d) json.Unmarshal([]byte(payload), &d)
hook.runProcessPayloadFunc(fn, d) hook.runProcessPayloadFunc(fn, d, hd)
case DeploymentStatusEvent: case DeploymentStatusEvent:
var d DeploymentStatusPayload var d DeploymentStatusPayload
json.Unmarshal([]byte(payload), &d) json.Unmarshal([]byte(payload), &d)
hook.runProcessPayloadFunc(fn, d) hook.runProcessPayloadFunc(fn, d, hd)
case ForkEvent: case ForkEvent:
var f ForkPayload var f ForkPayload
json.Unmarshal([]byte(payload), &f) json.Unmarshal([]byte(payload), &f)
hook.runProcessPayloadFunc(fn, f) hook.runProcessPayloadFunc(fn, f, hd)
case GollumEvent: case GollumEvent:
var g GollumPayload var g GollumPayload
json.Unmarshal([]byte(payload), &g) json.Unmarshal([]byte(payload), &g)
hook.runProcessPayloadFunc(fn, g) hook.runProcessPayloadFunc(fn, g, hd)
case IssueCommentEvent: case IssueCommentEvent:
var i IssueCommentPayload var i IssueCommentPayload
json.Unmarshal([]byte(payload), &i) json.Unmarshal([]byte(payload), &i)
hook.runProcessPayloadFunc(fn, i) hook.runProcessPayloadFunc(fn, i, hd)
case IssuesEvent: case IssuesEvent:
var i IssuesPayload var i IssuesPayload
json.Unmarshal([]byte(payload), &i) json.Unmarshal([]byte(payload), &i)
hook.runProcessPayloadFunc(fn, i) hook.runProcessPayloadFunc(fn, i, hd)
case LabelEvent:
var l LabelPayload
json.Unmarshal([]byte(payload), &l)
hook.runProcessPayloadFunc(fn, l, hd)
case MemberEvent: case MemberEvent:
var m MemberPayload var m MemberPayload
json.Unmarshal([]byte(payload), &m) json.Unmarshal([]byte(payload), &m)
hook.runProcessPayloadFunc(fn, m) hook.runProcessPayloadFunc(fn, m, hd)
case MembershipEvent: case MembershipEvent:
var m MembershipPayload var m MembershipPayload
json.Unmarshal([]byte(payload), &m) json.Unmarshal([]byte(payload), &m)
hook.runProcessPayloadFunc(fn, m) hook.runProcessPayloadFunc(fn, m, hd)
case MilestoneEvent:
var m MilestonePayload
json.Unmarshal([]byte(payload), &m)
hook.runProcessPayloadFunc(fn, m, hd)
case OrganizationEvent:
var o OrganizationPayload
json.Unmarshal([]byte(payload), &o)
hook.runProcessPayloadFunc(fn, o, hd)
case OrgBlockEvent:
var o OrgBlockPayload
json.Unmarshal([]byte(payload), &o)
hook.runProcessPayloadFunc(fn, o, hd)
case PageBuildEvent: case PageBuildEvent:
var p PageBuildPayload var p PageBuildPayload
json.Unmarshal([]byte(payload), &p) json.Unmarshal([]byte(payload), &p)
hook.runProcessPayloadFunc(fn, p) hook.runProcessPayloadFunc(fn, p, hd)
case ProjectCardEvent:
var p ProjectCardPayload
json.Unmarshal([]byte(payload), &p)
hook.runProcessPayloadFunc(fn, p, hd)
case ProjectColumnEvent:
var p ProjectColumnPayload
json.Unmarshal([]byte(payload), &p)
hook.runProcessPayloadFunc(fn, p, hd)
case ProjectEvent:
var p ProjectPayload
json.Unmarshal([]byte(payload), &p)
hook.runProcessPayloadFunc(fn, p, hd)
case PublicEvent: case PublicEvent:
var p PublicPayload var p PublicPayload
json.Unmarshal([]byte(payload), &p) json.Unmarshal([]byte(payload), &p)
hook.runProcessPayloadFunc(fn, p) hook.runProcessPayloadFunc(fn, p, hd)
case PullRequestReviewCommentEvent:
var p PullRequestReviewCommentPayload
json.Unmarshal([]byte(payload), &p)
hook.runProcessPayloadFunc(fn, p)
case PullRequestEvent: case PullRequestEvent:
var p PullRequestPayload var p PullRequestPayload
json.Unmarshal([]byte(payload), &p) json.Unmarshal([]byte(payload), &p)
hook.runProcessPayloadFunc(fn, p) hook.runProcessPayloadFunc(fn, p, hd)
case PullRequestReviewEvent:
var p PullRequestReviewPayload
json.Unmarshal([]byte(payload), &p)
hook.runProcessPayloadFunc(fn, p, hd)
case PullRequestReviewCommentEvent:
var p PullRequestReviewCommentPayload
json.Unmarshal([]byte(payload), &p)
hook.runProcessPayloadFunc(fn, p, hd)
case PushEvent: case PushEvent:
var p PushPayload var p PushPayload
json.Unmarshal([]byte(payload), &p) json.Unmarshal([]byte(payload), &p)
hook.runProcessPayloadFunc(fn, p) hook.runProcessPayloadFunc(fn, p, hd)
case RepositoryEvent:
var r RepositoryPayload
json.Unmarshal([]byte(payload), &r)
hook.runProcessPayloadFunc(fn, r)
case ReleaseEvent: case ReleaseEvent:
var r ReleasePayload var r ReleasePayload
json.Unmarshal([]byte(payload), &r) json.Unmarshal([]byte(payload), &r)
hook.runProcessPayloadFunc(fn, r) hook.runProcessPayloadFunc(fn, r, hd)
case RepositoryEvent:
var r RepositoryPayload
json.Unmarshal([]byte(payload), &r)
hook.runProcessPayloadFunc(fn, r, hd)
case StatusEvent: case StatusEvent:
var s StatusPayload var s StatusPayload
json.Unmarshal([]byte(payload), &s) json.Unmarshal([]byte(payload), &s)
hook.runProcessPayloadFunc(fn, s) hook.runProcessPayloadFunc(fn, s, hd)
case TeamEvent:
var t TeamPayload
json.Unmarshal([]byte(payload), &t)
hook.runProcessPayloadFunc(fn, t, hd)
case TeamAddEvent: case TeamAddEvent:
var t TeamAddPayload var t TeamAddPayload
json.Unmarshal([]byte(payload), &t) json.Unmarshal([]byte(payload), &t)
hook.runProcessPayloadFunc(fn, t) hook.runProcessPayloadFunc(fn, t, hd)
case WatchEvent: case WatchEvent:
var w WatchPayload var w WatchPayload
json.Unmarshal([]byte(payload), &w) json.Unmarshal([]byte(payload), &w)
hook.runProcessPayloadFunc(fn, w) hook.runProcessPayloadFunc(fn, w, hd)
} }
} }
func (hook Webhook) runProcessPayloadFunc(fn webhooks.ProcessPayloadFunc, results interface{}) { func (hook Webhook) runProcessPayloadFunc(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) {
go func(fn webhooks.ProcessPayloadFunc, results interface{}) { go func(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) {
fn(results) fn(results, header)
}(fn, results) }(fn, results, header)
} }
+2170 -634
View File
File diff suppressed because it is too large Load Diff
+4872 -602
View File
File diff suppressed because it is too large Load Diff
+157
View File
@@ -0,0 +1,157 @@
package gitlab
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"gopkg.in/go-playground/webhooks.v3"
)
// Webhook instance contains all methods needed to process events
type Webhook struct {
provider webhooks.Provider
secret string
eventFuncs map[Event]webhooks.ProcessPayloadFunc
}
// Config defines the configuration to create a new GitHub Webhook instance
type Config struct {
Secret string
}
// Event defines a GitHub hook event type
type Event string
// GitLab hook types
const (
PushEvents Event = "Push Hook"
TagEvents Event = "Tag Push Hook"
IssuesEvents Event = "Issue Hook"
ConfidentialIssuesEvents Event = "Confidential Issue Hook"
CommentEvents Event = "Note Hook"
MergeRequestEvents Event = "Merge Request Hook"
WikiPageEvents Event = "Wiki Page Hook"
PipelineEvents Event = "Pipeline Hook"
BuildEvents Event = "Build Hook"
)
// New creates and returns a WebHook instance denoted by the Provider type
func New(config *Config) *Webhook {
return &Webhook{
provider: webhooks.GitLab,
secret: config.Secret,
eventFuncs: map[Event]webhooks.ProcessPayloadFunc{},
}
}
// Provider returns the current hooks provider ID
func (hook Webhook) Provider() webhooks.Provider {
return hook.provider
}
// RegisterEvents registers the function to call when the specified event(s) are encountered
func (hook Webhook) RegisterEvents(fn webhooks.ProcessPayloadFunc, events ...Event) {
for _, event := range events {
hook.eventFuncs[event] = fn
}
}
// ParsePayload parses and verifies the payload and fires off the mapped function, if it exists.
func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) {
webhooks.DefaultLog.Info("Parsing Payload...")
event := r.Header.Get("X-Gitlab-Event")
if len(event) == 0 {
webhooks.DefaultLog.Error("Missing X-Gitlab-Event Header")
http.Error(w, "400 Bad Request - Missing X-Gitlab-Event Header", http.StatusBadRequest)
return
}
webhooks.DefaultLog.Debug(fmt.Sprintf("X-Gitlab-Event:%s", event))
gitLabEvent := Event(event)
fn, ok := hook.eventFuncs[gitLabEvent]
// if no event registered
if !ok {
webhooks.DefaultLog.Info(fmt.Sprintf("Webhook Event %s not registered, it is recommended to setup only events in gitlab that will be registered in the webhook to avoid unnecessary traffic and reduce potential attack vectors.", event))
return
}
payload, err := ioutil.ReadAll(r.Body)
if err != nil || len(payload) == 0 {
webhooks.DefaultLog.Error("Issue reading Payload")
http.Error(w, "Error reading Payload", http.StatusInternalServerError)
return
}
webhooks.DefaultLog.Debug(fmt.Sprintf("Payload:%s", string(payload)))
// If we have a Secret set, we should check the MAC
if len(hook.secret) > 0 {
webhooks.DefaultLog.Info("Checking secret")
signature := r.Header.Get("X-Gitlab-Token")
if signature != hook.secret {
webhooks.DefaultLog.Error(fmt.Sprintf("Invalid X-Gitlab-Token of '%s'", signature))
http.Error(w, "403 Forbidden - Token missmatch", http.StatusForbidden)
return
}
}
// Make headers available to ProcessPayloadFunc as a webhooks type
hd := webhooks.Header(r.Header)
switch gitLabEvent {
case PushEvents:
var pe PushEventPayload
json.Unmarshal([]byte(payload), &pe)
hook.runProcessPayloadFunc(fn, pe, hd)
case TagEvents:
var te TagEventPayload
json.Unmarshal([]byte(payload), &te)
hook.runProcessPayloadFunc(fn, te, hd)
case ConfidentialIssuesEvents:
var cie ConfidentialIssueEventPayload
json.Unmarshal([]byte(payload), &cie)
hook.runProcessPayloadFunc(fn, cie, hd)
case IssuesEvents:
var ie IssueEventPayload
json.Unmarshal([]byte(payload), &ie)
hook.runProcessPayloadFunc(fn, ie, hd)
case CommentEvents:
var ce CommentEventPayload
json.Unmarshal([]byte(payload), &ce)
hook.runProcessPayloadFunc(fn, ce, hd)
case MergeRequestEvents:
var mre MergeRequestEventPayload
json.Unmarshal([]byte(payload), &mre)
hook.runProcessPayloadFunc(fn, mre, hd)
case WikiPageEvents:
var wpe WikiPageEventPayload
json.Unmarshal([]byte(payload), &wpe)
hook.runProcessPayloadFunc(fn, wpe, hd)
case PipelineEvents:
var pe PipelineEventPayload
json.Unmarshal([]byte(payload), &pe)
hook.runProcessPayloadFunc(fn, pe, hd)
case BuildEvents:
var be BuildEventPayload
json.Unmarshal([]byte(payload), &be)
hook.runProcessPayloadFunc(fn, be, hd)
}
}
func (hook Webhook) runProcessPayloadFunc(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) {
go func(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) {
fn(results, header)
}(fn, results, header)
}
File diff suppressed because it is too large Load Diff
+411
View File
@@ -0,0 +1,411 @@
package gitlab
import (
"strings"
"time"
)
type customTime struct {
time.Time
}
func (t *customTime) UnmarshalJSON(b []byte) (err error) {
layout := []string{
"2006-01-02 15:04:05 MST",
"2006-01-02 15:04:05 Z07:00",
"2006-01-02 15:04:05 Z0700",
time.RFC3339,
}
s := strings.Trim(string(b), "\"")
if s == "null" {
t.Time = time.Time{}
return
}
for _, l := range layout {
t.Time, err = time.Parse(l, s)
if err == nil {
break
}
}
return
}
// IssueEventPayload contains the information for GitLab's issue event
type IssueEventPayload struct {
ObjectKind string `json:"object_kind"`
User User `json:"user"`
Project Project `json:"project"`
Repository Repository `json:"repository"`
ObjectAttributes ObjectAttributes `json:"object_attributes"`
Assignee Assignee `json:"assignee"`
}
// ConfidentialIssueEventPayload contains the information for GitLab's confidential issue event
type ConfidentialIssueEventPayload struct {
// The data for confidential issues is currently the same as normal issues,
// so we can just embed the normal issue payload type here.
IssueEventPayload
}
// MergeRequestEventPayload contains the information for GitLab's merge request event
type MergeRequestEventPayload struct {
ObjectKind string `json:"object_kind"`
User User `json:"user"`
ObjectAttributes ObjectAttributes `json:"object_attributes"`
}
// PushEventPayload contains the information for GitLab's push event
type PushEventPayload struct {
ObjectKind string `json:"object_kind"`
Before string `json:"before"`
After string `json:"after"`
Ref string `json:"ref"`
CheckoutSHA string `json:"checkout_sha"`
UserID int64 `json:"user_id"`
UserName string `json:"user_name"`
UserEmail string `json:"user_email"`
UserAvatar string `json:"user_avatar"`
ProjectID int64 `json:"project_id"`
Project Project `json:"Project"`
Repository Repository `json:"repository"`
Commits []Commit `json:"commits"`
TotalCommitsCount int64 `json:"total_commits_count"`
}
// TagEventPayload contains the information for GitLab's tag push event
type TagEventPayload struct {
ObjectKind string `json:"object_kind"`
Before string `json:"before"`
After string `json:"after"`
Ref string `json:"ref"`
CheckoutSHA string `json:"checkout_sha"`
UserID int64 `json:"user_id"`
UserName string `json:"user_name"`
UserAvatar string `json:"user_avatar"`
ProjectID int64 `json:"project_id"`
Project Project `json:"Project"`
Repository Repository `json:"repository"`
Commits []Commit `json:"commits"`
TotalCommitsCount int64 `json:"total_commits_count"`
}
// WikiPageEventPayload contains the information for GitLab's wiki created/updated event
type WikiPageEventPayload struct {
ObjectKind string `json:"object_kind"`
User User `json:"user"`
Project Project `json:"project"`
Wiki Wiki `json:"wiki"`
ObjectAttributes ObjectAttributes `json:"object_attributes"`
}
// PipelineEventPayload contains the information for GitLab's pipeline status change event
type PipelineEventPayload struct {
ObjectKind string `json:"object_kind"`
User User `json:"user"`
Project Project `json:"project"`
Commit Commit `json:"commit"`
ObjectAttributes ObjectAttributes `json:"object_attributes"`
Builds []Build `json:"builds"`
}
// CommentEventPayload contains the information for GitLab's comment event
type CommentEventPayload struct {
ObjectKind string `json:"object_kind"`
User User `json:"user"`
ProjectID int64 `json:"project_id"`
Project Project `json:"project"`
Repository Repository `json:"repository"`
ObjectAttributes ObjectAttributes `json:"object_attributes"`
MergeRequest MergeRequest `json:"merge_request"`
Commit Commit `json:"commit"`
Issue Issue `json:"issue"`
Snippet Snippet `json:"snippet"`
}
// BuildEventPayload contains the information for GitLab's build status change event
type BuildEventPayload struct {
ObjectKind string `json:"object_kind"`
Ref string `json:"ref"`
Tag bool `json:"tag"`
BeforeSHA string `json:"before_sha"`
SHA string `json:"sha"`
BuildID int64 `json:"build_id"`
BuildName string `json:"build_name"`
BuildStage string `json:"build_stage"`
BuildStatus string `json:"build_status"`
BuildStartedAt customTime `json:"build_started_at"`
BuildFinishedAt customTime `json:"build_finished_at"`
BuildDuration int64 `json:"build_duration"`
BuildAllowFailure bool `json:"build_allow_failure"`
ProjectID int64 `json:"project_id"`
ProjectName string `json:"project_name"`
User User `json:"user"`
Commit BuildCommit `json:"commit"`
Repository Repository `json:"repository"`
}
// Issue contains all of the GitLab issue information
type Issue struct {
ID int64 `json:"id"`
Title string `json:"title"`
AssigneeID int64 `json:"assignee_id"`
AuthorID int64 `json:"author_id"`
ProjectID int64 `json:"project_id"`
CreatedAt customTime `json:"created_at"`
UpdatedAt customTime `json:"updated_at"`
Position int64 `json:"position"`
BranchName string `json:"branch_name"`
Description string `json:"description"`
MilestoneID int64 `json:"milestone_id"`
State string `json:"state"`
IID int64 `json:"iid"`
}
// Build contains all of the GitLab build information
type Build struct {
ID int64 `json:"id"`
Stage string `json:"stage"`
Name string `json:"name"`
Status string `json:"status"`
CreatedAt customTime `json:"created_at"`
StartedAt customTime `json:"started_at"`
FinishedAt customTime `json:"finished_at"`
When string `json:"when"`
Manual bool `json:"manual"`
User User `json:"user"`
Runner string `json:"runner"`
ArtifactsFile ArtifactsFile `json:"artifactsfile"`
}
// ArtifactsFile contains all of the GitLab artifact information
type ArtifactsFile struct {
Filename string `json:"filename"`
Size string `json:"size"`
}
// Wiki contains all of the GitLab wiki information
type Wiki struct {
WebURL string `json:"web_url"`
GitSSHURL string `json:"git_ssh_url"`
GitHTTPURL string `json:"git_http_url"`
PathWithNamespace string `json:"path_with_namespace"`
DefaultBranch string `json:"default_branch"`
}
// Commit contains all of the GitLab commit information
type Commit struct {
ID string `json:"id"`
Message string `json:"message"`
Timestamp customTime `json:"timestamp"`
URL string `json:"url"`
Author Author `json:"author"`
Added []string `json:"added"`
Modified []string `json:"modified"`
Removed []string `json:"removed"`
}
// BuildCommit contains all of the GitLab build commit information
type BuildCommit struct {
ID int64 `json:"id"`
SHA string `json:"sha"`
Message string `json:"message"`
AuthorName string `json:"auuthor_name"`
AuthorEmail string `json:"author_email"`
Status string `json:"status"`
Duration int64 `json:"duration"`
StartedAt customTime `json:"started_at"`
FinishedAt customTime `json:"finished_at"`
}
// Snippet contains all of the GitLab snippet information
type Snippet struct {
ID int64 `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
AuthorID int64 `json:"author_id"`
ProjectID int64 `json:"project_id"`
CreatedAt customTime `json:"created_at"`
UpdatedAt customTime `json:"updated_at"`
FileName string `json:"file_name"`
ExpiresAt customTime `json:"expires_at"`
Type string `json:"type"`
VisibilityLevel int64 `json:"visibility_level"`
}
// User contains all of the GitLab user information
type User struct {
Name string `json:"name"`
UserName string `json:"username"`
AvatarURL string `json:"avatar_url"`
}
// Project contains all of the GitLab project information
type Project struct {
Name string `json:"name"`
Description string `json:"description"`
WebURL string `json:"web_url"`
AvatarURL string `json:"avatar_url"`
GitSSSHURL string `json:"git_ssh_url"`
GitHTTPURL string `json:"git_http_url"`
Namespace string `json:"namespace"`
VisibilityLevel int64 `json:"visibility_level"`
PathWithNamespace string `json:"path_with_namespace"`
DefaultBranch string `json:"default_branch"`
Homepage string `json:"homepage"`
URL string `json:"url"`
SSHURL string `json:"ssh_url"`
HTTPURL string `json:"http_url"`
}
// Repository contains all of the GitLab repository information
type Repository struct {
Name string `json:"name"`
URL string `json:"url"`
Description string `json:"description"`
Homepage string `json:"homepage"`
}
// ObjectAttributes contains all of the GitLab object attributes information
type ObjectAttributes struct {
ID int64 `json:"id"`
Title string `json:"title"`
AssigneeID int64 `json:"assignee_id"`
AuthorID int64 `json:"author_id"`
ProjectID int64 `json:"project_id"`
CreatedAt customTime `json:"created_at"`
UpdatedAt customTime `json:"updated_at"`
Position int64 `json:"position"`
BranchName string `json:"branch_name"`
Description string `json:"description"`
MilestoneID int64 `json:"milestone_id"`
State string `json:"state"`
IID int64 `json:"iid"`
URL string `json:"url"`
Action string `json:"action"`
TargetBranch string `json:"target_branch"`
SourceBranch string `json:"source_branch"`
SourceProjectID int64 `json:"source_project_id"`
TargetProjectID int64 `json:"target_project_id"`
StCommits string `json:"st_commits"`
MergeStatus string `json:"merge_status"`
Content string `json:"content"`
Format string `json:"format"`
Message string `json:"message"`
Slug string `json:"slug"`
Ref string `json:"ref"`
Tag bool `json:"tag"`
SHA string `json:"sha"`
BeforeSHA string `json:"before_sha"`
Status string `json:"status"`
Stages []string `json:"stages"`
Duration int64 `json:"duration"`
Note string `json:"note"`
NotebookType string `json:"noteable_type"`
At customTime `json:"attachment"`
LineCode string `json:"line_code"`
CommitID string `json:"commit_id"`
NoteableID int64 `json:"noteable_id"`
System bool `json:"system"`
WorkInProgress bool `json:"work_in_progress"`
StDiffs []StDiff `json:"st_diffs"`
Source Source `json:"source"`
Target Target `json:"target"`
LastCommit LastCommit `json:"last_commit"`
Assignee Assignee `json:"assignee"`
}
// MergeRequest contains all of the GitLab merge request information
type MergeRequest struct {
ID int64 `json:"id"`
TargetBranch string `json:"target_branch"`
SourceBranch string `json:"source_branch"`
SourceProjectID int64 `json:"source_project_id"`
AssigneeID int64 `json:"assignee_id"`
AuthorID int64 `json:"author_id"`
Title string `json:"title"`
CreatedAt customTime `json:"created_at"`
UpdatedAt customTime `json:"updated_at"`
MilestoneID int64 `json:"milestone_id"`
State string `json:"state"`
MergeStatus string `json:"merge_status"`
TargetProjectID int64 `json:"target_project_id"`
IID int64 `json:"iid"`
Description string `json:"description"`
Position int64 `json:"position"`
LockedAt customTime `json:"locked_at"`
Source Source `json:"source"`
Target Target `json:"target"`
LastCommit LastCommit `json:"last_commit"`
WorkInProgress bool `json:"work_in_progress"`
Assignee Assignee `json:"assignee"`
}
// Assignee contains all of the GitLab assignee information
type Assignee struct {
Name string `json:"name"`
Username string `json:"username"`
AvatarURL string `json:"avatar_url"`
}
// StDiff contains all of the GitLab diff information
type StDiff struct {
Diff string `json:"diff"`
NewPath string `json:"new_path"`
OldPath string `json:"old_path"`
AMode string `json:"a_mode"`
BMode string `json:"b_mode"`
NewFile bool `json:"new_file"`
RenamedFile bool `json:"renamed_file"`
DeletedFile bool `json:"deleted_file"`
}
// Source contains all of the GitLab source information
type Source struct {
Name string `json:"name"`
Description string `json:"description"`
WebURL string `json:"web_url"`
AvatarURL string `json:"avatar_url"`
GitSSHURL string `json:"git_ssh_url"`
GitHTTPURL string `json:"git_http_url"`
Namespace string `json:"namespace"`
VisibilityLevel int64 `json:"visibility_level"`
PathWithNamespace string `json:"path_with_namespace"`
DefaultBranch string `json:"default_branch"`
Homepage string `json:"homepage"`
URL string `json:"url"`
SSHURL string `json:"ssh_url"`
HTTPURL string `json:"http_url"`
}
// Target contains all of the GitLab target information
type Target struct {
Name string `json:"name"`
Description string `json:"description"`
WebURL string `json:"web_url"`
AvatarURL string `json:"avatar_url"`
GitSSHURL string `json:"git_ssh_url"`
GitHTTPURL string `json:"git_http_url"`
Namespace string `json:"namespace"`
VisibilityLevel int64 `json:"visibility_level"`
PathWithNamespace string `json:"path_with_namespace"`
DefaultBranch string `json:"default_branch"`
Homepage string `json:"homepage"`
URL string `json:"url"`
SSHURL string `json:"ssh_url"`
HTTPURL string `json:"http_url"`
}
// LastCommit contains all of the GitLab last commit information
type LastCommit struct {
ID string `json:"id"`
Message string `json:"message"`
Timestamp customTime `json:"timestamp"`
URL string `json:"url"`
Author Author `json:"author"`
}
// Author contains all of the GitLab author information
type Author struct {
Name string `json:"name"`
Email string `json:"email"`
}
+44
View File
@@ -0,0 +1,44 @@
package webhooks
import "log"
// DefaultLog contains the default logger for webhooks, and prints only info and error messages by default
// for debugs override DefaultLog or see NewLogger for creating one without debugs.
var DefaultLog Logger = new(logger)
// Logger allows for customizable logging
type Logger interface {
// Info prints basic information.
Info(string)
// Error prints error information.
Error(string)
// Debug prints information usefull for debugging.
Debug(string)
}
// NewLogger returns a new logger for use.
func NewLogger(debug bool) Logger {
return &logger{PrintDebugs: debug}
}
type logger struct {
PrintDebugs bool
}
// Info prints basic information.
func (l *logger) Info(msg string) {
log.Println("INFO:", msg)
}
// v prints error information.
func (l *logger) Error(msg string) {
log.Println("ERROR:", msg)
}
// Debug prints information usefull for debugging.
func (l *logger) Debug(msg string) {
if !l.PrintDebugs {
return
}
log.Println("DEBUG:", msg)
}
+34 -7
View File
@@ -1,6 +1,12 @@
package webhooks package webhooks
import "net/http" import (
"fmt"
"net/http"
)
// Header provides http.Header to minimize imports
type Header http.Header
// Provider defines the type of webhook // Provider defines the type of webhook
type Provider int type Provider int
@@ -11,6 +17,8 @@ func (p Provider) String() string {
return "GitHub" return "GitHub"
case Bitbucket: case Bitbucket:
return "Bitbucket" return "Bitbucket"
case GitLab:
return "GitLab"
default: default:
return "Unknown" return "Unknown"
} }
@@ -20,9 +28,10 @@ func (p Provider) String() string {
const ( const (
GitHub Provider = iota GitHub Provider = iota
Bitbucket Bitbucket
GitLab
) )
// Webhook interface defines a webhook to recieve events // Webhook interface defines a webhook to receive events
type Webhook interface { type Webhook interface {
Provider() Provider Provider() Provider
ParsePayload(w http.ResponseWriter, r *http.Request) ParsePayload(w http.ResponseWriter, r *http.Request)
@@ -31,20 +40,29 @@ type Webhook interface {
type server struct { type server struct {
hook Webhook hook Webhook
path string path string
includePathCheck bool
} }
// ProcessPayloadFunc is a common function for payload return values // ProcessPayloadFunc is a common function for payload return values
type ProcessPayloadFunc func(payload interface{}) type ProcessPayloadFunc func(payload interface{}, header Header)
// Handler returns the webhook http.Handler for use in your own Mux implementation
func Handler(hook Webhook) http.Handler {
return &server{
hook: hook,
}
}
// Run runs a server // Run runs a server
func Run(hook Webhook, addr string, path string) error { func Run(hook Webhook, addr string, path string) error {
srv := &server{ srv := &server{
hook: hook, hook: hook,
path: path, path: path,
includePathCheck: true,
} }
s := &http.Server{Addr: addr, Handler: srv} s := &http.Server{Addr: addr, Handler: srv}
DefaultLog.Info(fmt.Sprintf("Listening on addr: %s path: %s", addr, path))
return s.ListenAndServe() return s.ListenAndServe()
} }
@@ -54,41 +72,50 @@ func RunServer(s *http.Server, hook Webhook, path string) error {
srv := &server{ srv := &server{
hook: hook, hook: hook,
path: path, path: path,
includePathCheck: true,
} }
s.Handler = srv s.Handler = srv
DefaultLog.Info(fmt.Sprintf("Listening on addr: %s path: %s", s.Addr, path))
return s.ListenAndServe() return s.ListenAndServe()
} }
// RunTLSServer runs a custom server with TLS configuration. // RunTLSServer runs a custom server with TLS configuration.
// NOTE: http.Server Handler will be overridden by this library, just set it to nil. // NOTE: http.Server Handler will be overridden by this library, just set it to nil.
// Setting the Certificates can be done in the http.Server.TLSConfig.Certificates // Setting the Certificates can be done in the http.Server.TLSConfig.Certificates
// see example here: https://gopkg.in/go-playground/webhooks.v1/blob/master/webhooks_test.go#L178 // see example here: https://github.com/go-playground/webhooks/blob/v2/webhooks_test.go#L178
func RunTLSServer(s *http.Server, hook Webhook, path string) error { func RunTLSServer(s *http.Server, hook Webhook, path string) error {
srv := &server{ srv := &server{
hook: hook, hook: hook,
path: path, path: path,
includePathCheck: true,
} }
s.Handler = srv s.Handler = srv
DefaultLog.Info(fmt.Sprintf("Listening on addr: %s path: %s", s.Addr, path))
return s.ListenAndServeTLS("", "") return s.ListenAndServeTLS("", "")
} }
// ServeHTTP is the Handler for every posted WebHook Event // ServeHTTP is the Handler for every posted WebHook Event
func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close() defer r.Body.Close()
DefaultLog.Info("Webhook received")
if r.Method != "POST" { if r.Method != "POST" {
DefaultLog.Error(fmt.Sprintf("405 Method not allowed, attempt made using Method: %s", r.Method))
http.Error(w, "405 Method not allowed", http.StatusMethodNotAllowed) http.Error(w, "405 Method not allowed", http.StatusMethodNotAllowed)
return return
} }
DefaultLog.Debug(fmt.Sprintf("Include path check: %t", s.includePathCheck))
if s.includePathCheck {
if r.URL.Path != s.path { if r.URL.Path != s.path {
DefaultLog.Error(fmt.Sprintf("404 Not found, POST made using path: %s, but expected %s", r.URL.Path, s.path))
http.Error(w, "404 Not found", http.StatusNotFound) http.Error(w, "404 Not found", http.StatusNotFound)
return return
} }
}
s.hook.ParsePayload(w, r) s.hook.ParsePayload(w, r)
} }
+41 -1
View File
@@ -8,6 +8,8 @@ import (
"testing" "testing"
"time" "time"
"net/http/httptest"
. "gopkg.in/go-playground/assert.v1" . "gopkg.in/go-playground/assert.v1"
) )
@@ -47,6 +49,43 @@ func TestMain(m *testing.M) {
// teardown // teardown
} }
func TestHandler(t *testing.T) {
mux := http.NewServeMux()
mux.Handle("/webhooks", Handler(fakeHook))
s := httptest.NewServer(Handler(fakeHook))
defer s.Close()
payload := "{}"
req, err := http.NewRequest("POST", s.URL+"/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json")
Equal(t, err, nil)
client := &http.Client{}
resp, err := client.Do(req)
Equal(t, err, nil)
defer resp.Body.Close()
Equal(t, resp.StatusCode, http.StatusOK)
// Test BAD METHOD
req, err = http.NewRequest("GET", s.URL+"/webhooks", bytes.NewBuffer([]byte(payload)))
req.Header.Set("Content-Type", "application/json")
Equal(t, err, nil)
resp, err = client.Do(req)
Equal(t, err, nil)
defer resp.Body.Close()
Equal(t, resp.StatusCode, http.StatusMethodNotAllowed)
}
func TestRun(t *testing.T) { func TestRun(t *testing.T) {
go Run(fakeHook, "127.0.0.1:3006", "/webhooks") go Run(fakeHook, "127.0.0.1:3006", "/webhooks")
@@ -97,7 +136,7 @@ func TestRun(t *testing.T) {
} }
func TestRunServer(t *testing.T) { func TestRunServer(t *testing.T) {
DefaultLog = NewLogger(true)
server := &http.Server{Addr: "127.0.0.1:3007", Handler: nil} server := &http.Server{Addr: "127.0.0.1:3007", Handler: nil}
go RunServer(server, fakeHook, "/webhooks") go RunServer(server, fakeHook, "/webhooks")
time.Sleep(5000) time.Sleep(5000)
@@ -236,5 +275,6 @@ func TestProviderString(t *testing.T) {
Equal(t, GitHub.String(), "GitHub") Equal(t, GitHub.String(), "GitHub")
Equal(t, Bitbucket.String(), "Bitbucket") Equal(t, Bitbucket.String(), "Bitbucket")
Equal(t, GitLab.String(), "GitLab")
Equal(t, Provider(999999).String(), "Unknown") Equal(t, Provider(999999).String(), "Unknown")
} }