Converted GitHub

This commit is contained in:
Dean Karn
2018-07-25 22:05:43 -07:00
parent adb918738a
commit 077706a514
13 changed files with 746 additions and 516 deletions
+8 -8
View File
@@ -4,7 +4,7 @@ Library webhooks
[![Build Status](https://travis-ci.org/go-playground/webhooks.svg?branch=v4)](https://travis-ci.org/go-playground/webhooks) [![Build Status](https://travis-ci.org/go-playground/webhooks.svg?branch=v4)](https://travis-ci.org/go-playground/webhooks)
[![Coverage Status](https://coveralls.io/repos/go-playground/webhooks/badge.svg?branch=v4&service=github)](https://coveralls.io/github/go-playground/webhooks?branch=v3) [![Coverage Status](https://coveralls.io/repos/go-playground/webhooks/badge.svg?branch=v4&service=github)](https://coveralls.io/github/go-playground/webhooks?branch=v3)
[![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.v4?status.svg)](https://godoc.org/gopkg.in/go-playground/webhooks.v4) [![GoDoc](https://godoc.org/gopkg.in/go-playground/webhooks.v5?status.svg)](https://godoc.org/gopkg.in/go-playground/webhooks.v5)
![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 receiving and parsing of GitHub, Bitbucket and GitLab Webhook Events Library webhooks allows for easy receiving and parsing of GitHub, Bitbucket and GitLab Webhook Events
@@ -24,17 +24,17 @@ Installation
Use go get. Use go get.
```shell ```shell
go get -u gopkg.in/go-playground/webhooks.v4 go get -u gopkg.in/go-playground/webhooks.v5
``` ```
Then import the package into your own code. Then import the package into your own code.
import "gopkg.in/go-playground/webhooks.v4" import "gopkg.in/go-playground/webhooks.v5"
Usage and Documentation Usage and Documentation
------ ------
Please see http://godoc.org/gopkg.in/go-playground/webhooks.v4 for detailed usage docs. Please see http://godoc.org/gopkg.in/go-playground/webhooks.v5 for detailed usage docs.
##### Examples: ##### Examples:
@@ -46,8 +46,8 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"gopkg.in/go-playground/webhooks.v4" "gopkg.in/go-playground/webhooks.v5"
"gopkg.in/go-playground/webhooks.v4/github" "gopkg.in/go-playground/webhooks.v5/github"
) )
const ( const (
@@ -103,8 +103,8 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"gopkg.in/go-playground/webhooks.v4" "gopkg.in/go-playground/webhooks.v5"
"gopkg.in/go-playground/webhooks.v4/github" "gopkg.in/go-playground/webhooks.v5/github"
) )
const ( const (
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"gopkg.in/go-playground/webhooks.v4" "gopkg.in/go-playground/webhooks.v5"
) )
// Webhook instance contains all methods needed to process events // Webhook instance contains all methods needed to process events
+1 -1
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.v4" "gopkg.in/go-playground/webhooks.v5"
) )
// NOTES: // NOTES:
+2 -2
View File
@@ -5,8 +5,8 @@ import (
"log" "log"
"strconv" "strconv"
"gopkg.in/go-playground/webhooks.v4" "gopkg.in/go-playground/webhooks.v5"
"gopkg.in/go-playground/webhooks.v4/github" "gopkg.in/go-playground/webhooks.v5/github"
) )
const ( const (
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"gopkg.in/go-playground/webhooks.v4" "gopkg.in/go-playground/webhooks.v5"
"gopkg.in/go-playground/webhooks.v4/github" "gopkg.in/go-playground/webhooks.v5/github"
) )
const ( const (
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"gopkg.in/go-playground/webhooks.v4" "gopkg.in/go-playground/webhooks.v5"
"gopkg.in/go-playground/webhooks.v4/github" "gopkg.in/go-playground/webhooks.v5/github"
) )
const ( const (
+164 -161
View File
@@ -5,24 +5,23 @@ import (
"crypto/sha1" "crypto/sha1"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"gopkg.in/go-playground/webhooks.v4"
) )
// Webhook instance contains all methods needed to process events // parse errros
type Webhook struct { var (
provider webhooks.Provider ErrEventNotSpecifiedToParse = errors.New("No Event specified to parse")
secret string ErrInvalidHTTPMethod = errors.New("Invalid HTTP Method")
eventFuncs map[Event]webhooks.ProcessPayloadFunc ErrMissingGithubEventHeader = errors.New("Missing X-GitHub-Event Header")
} ErrMissingHubSignatureHeader = errors.New("Missing X-Hub-Signature")
ErrEventNotFound = errors.New("Event not defined to be parsed")
// Config defines the configuration to create a new GitHub Webhook instance ErrParsingPayload = errors.New("Error Reading Payload")
type Config struct { ErrHMACVerificationFailed = errors.New("HMAC verification failed")
Secret string )
}
// Event defines a GitHub hook event type // Event defines a GitHub hook event type
type Event string type Event string
@@ -76,217 +75,221 @@ const (
IssueSubtype EventSubtype = "issues" IssueSubtype EventSubtype = "issues"
) )
// Option is a configuration option for the webhook
type Option func(*Webhook) error
// Options is a namespace var for configuration options
var Options = WebhookOptions{}
// WebhookOptions is a namespace for configuration option methods
type WebhookOptions struct{}
// Secret registers the GitHub secret
func (WebhookOptions) Secret(secret string) Option {
return func(hook *Webhook) error {
hook.secret = secret
return nil
}
}
// Webhook instance contains all methods needed to process events
type Webhook struct {
secret string
}
// New creates and returns a WebHook instance denoted by the Provider type // New creates and returns a WebHook instance denoted by the Provider type
func New(config *Config) *Webhook { func New(options ...Option) (*Webhook, error) {
return &Webhook{ hook := new(Webhook)
provider: webhooks.GitHub, for _, opt := range options {
secret: config.Secret, if err := opt(hook); err != nil {
eventFuncs: map[Event]webhooks.ProcessPayloadFunc{}, return nil, errors.New("Error applying Option")
}
} }
return hook, nil
} }
// Provider returns the current hooks provider ID // Parse verifies and parses the events specified and returns the payload object or an error
func (hook Webhook) Provider() webhooks.Provider { func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) {
return hook.provider defer func() {
} _, _ = io.Copy(ioutil.Discard, r.Body)
_ = r.Body.Close()
}()
// RegisterEvents registers the function to call when the specified event(s) are encountered if len(events) == 0 {
func (hook Webhook) RegisterEvents(fn webhooks.ProcessPayloadFunc, events ...Event) { return nil, ErrEventNotSpecifiedToParse
}
for _, event := range events { if r.Method != http.MethodPost {
hook.eventFuncs[event] = fn return nil, ErrInvalidHTTPMethod
} }
}
// 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-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") return nil, ErrMissingGithubEventHeader
http.Error(w, "400 Bad Request - Missing X-GitHub-Event Header", http.StatusBadRequest)
return
} }
webhooks.DefaultLog.Debug(fmt.Sprintf("X-GitHub-Event:%s", event))
gitHubEvent := Event(event) gitHubEvent := Event(event)
fn, ok := hook.eventFuncs[gitHubEvent] var found bool
// if no event registered for _, evt := range events {
if !ok { if evt == gitHubEvent {
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)) found = true
return break
}
}
// event not defined to be parsed
if !found {
return nil, ErrEventNotFound
} }
payload, err := ioutil.ReadAll(r.Body) payload, err := ioutil.ReadAll(r.Body)
if err != nil || len(payload) == 0 { if err != nil || len(payload) == 0 {
webhooks.DefaultLog.Error("Issue reading Payload") return nil, ErrParsingPayload
http.Error(w, "Issue 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 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") return nil, ErrMissingHubSignatureHeader
http.Error(w, "403 Forbidden - Missing X-Hub-Signature required for HMAC verification", http.StatusForbidden)
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)
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") return nil, ErrHMACVerificationFailed
http.Error(w, "403 Forbidden - HMAC verification failed", http.StatusForbidden)
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 pl CommitCommentPayload
json.Unmarshal([]byte(payload), &cc) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, cc, hd) return pl, err
case CreateEvent: case CreateEvent:
var c CreatePayload var pl CreatePayload
json.Unmarshal([]byte(payload), &c) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, c, hd) return pl, err
case DeleteEvent: case DeleteEvent:
var d DeletePayload var pl DeletePayload
json.Unmarshal([]byte(payload), &d) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, d, hd) return pl, err
case DeploymentEvent: case DeploymentEvent:
var d DeploymentPayload var pl DeploymentPayload
json.Unmarshal([]byte(payload), &d) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, d, hd) return pl, err
case DeploymentStatusEvent: case DeploymentStatusEvent:
var d DeploymentStatusPayload var pl DeploymentStatusPayload
json.Unmarshal([]byte(payload), &d) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, d, hd) return pl, err
case ForkEvent: case ForkEvent:
var f ForkPayload var pl ForkPayload
json.Unmarshal([]byte(payload), &f) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, f, hd) return pl, err
case GollumEvent: case GollumEvent:
var g GollumPayload var pl GollumPayload
json.Unmarshal([]byte(payload), &g) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, g, hd) return pl, err
case InstallationEvent, IntegrationInstallationEvent: case InstallationEvent, IntegrationInstallationEvent:
var i InstallationPayload var pl InstallationPayload
json.Unmarshal([]byte(payload), &i) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, i, hd) return pl, err
case IssueCommentEvent: case IssueCommentEvent:
var i IssueCommentPayload var pl IssueCommentPayload
json.Unmarshal([]byte(payload), &i) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, i, hd) return pl, err
case IssuesEvent: case IssuesEvent:
var i IssuesPayload var pl IssuesPayload
json.Unmarshal([]byte(payload), &i) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, i, hd) return pl, err
case LabelEvent: case LabelEvent:
var l LabelPayload var pl LabelPayload
json.Unmarshal([]byte(payload), &l) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, l, hd) return pl, err
case MemberEvent: case MemberEvent:
var m MemberPayload var pl MemberPayload
json.Unmarshal([]byte(payload), &m) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, m, hd) return pl, err
case MembershipEvent: case MembershipEvent:
var m MembershipPayload var pl MembershipPayload
json.Unmarshal([]byte(payload), &m) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, m, hd) return pl, err
case MilestoneEvent: case MilestoneEvent:
var m MilestonePayload var pl MilestonePayload
json.Unmarshal([]byte(payload), &m) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, m, hd) return pl, err
case OrganizationEvent: case OrganizationEvent:
var o OrganizationPayload var pl OrganizationPayload
json.Unmarshal([]byte(payload), &o) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, o, hd) return pl, err
case OrgBlockEvent: case OrgBlockEvent:
var o OrgBlockPayload var pl OrgBlockPayload
json.Unmarshal([]byte(payload), &o) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, o, hd) return pl, err
case PageBuildEvent: case PageBuildEvent:
var p PageBuildPayload var pl PageBuildPayload
json.Unmarshal([]byte(payload), &p) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, p, hd) return pl, err
case PingEvent: case PingEvent:
var p PingPayload var pl PingPayload
json.Unmarshal([]byte(payload), &p) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, p, hd) return pl, err
case ProjectCardEvent: case ProjectCardEvent:
var p ProjectCardPayload var pl ProjectCardPayload
json.Unmarshal([]byte(payload), &p) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, p, hd) return pl, err
case ProjectColumnEvent: case ProjectColumnEvent:
var p ProjectColumnPayload var pl ProjectColumnPayload
json.Unmarshal([]byte(payload), &p) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, p, hd) return pl, err
case ProjectEvent: case ProjectEvent:
var p ProjectPayload var pl ProjectPayload
json.Unmarshal([]byte(payload), &p) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, p, hd) return pl, err
case PublicEvent: case PublicEvent:
var p PublicPayload var pl PublicPayload
json.Unmarshal([]byte(payload), &p) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, p, hd) return pl, err
case PullRequestEvent: case PullRequestEvent:
var p PullRequestPayload var pl PullRequestPayload
json.Unmarshal([]byte(payload), &p) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, p, hd) return pl, err
case PullRequestReviewEvent: case PullRequestReviewEvent:
var p PullRequestReviewPayload var pl PullRequestReviewPayload
json.Unmarshal([]byte(payload), &p) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, p, hd) return pl, err
case PullRequestReviewCommentEvent: case PullRequestReviewCommentEvent:
var p PullRequestReviewCommentPayload var pl PullRequestReviewCommentPayload
json.Unmarshal([]byte(payload), &p) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, p, hd) return pl, err
case PushEvent: case PushEvent:
var p PushPayload var pl PushPayload
json.Unmarshal([]byte(payload), &p) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, p, hd) return pl, err
case ReleaseEvent: case ReleaseEvent:
var r ReleasePayload var pl ReleasePayload
json.Unmarshal([]byte(payload), &r) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, r, hd) return pl, err
case RepositoryEvent: case RepositoryEvent:
var r RepositoryPayload var pl RepositoryPayload
json.Unmarshal([]byte(payload), &r) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, r, hd) return pl, err
case StatusEvent: case StatusEvent:
var s StatusPayload var pl StatusPayload
json.Unmarshal([]byte(payload), &s) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, s, hd) return pl, err
case TeamEvent: case TeamEvent:
var t TeamPayload var pl TeamPayload
json.Unmarshal([]byte(payload), &t) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, t, hd) return pl, err
case TeamAddEvent: case TeamAddEvent:
var t TeamAddPayload var pl TeamAddPayload
json.Unmarshal([]byte(payload), &t) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, t, hd) return pl, err
case WatchEvent: case WatchEvent:
var w WatchPayload var pl WatchPayload
json.Unmarshal([]byte(payload), &w) err = json.Unmarshal([]byte(payload), &pl)
hook.runProcessPayloadFunc(fn, w, hd) return pl, err
default:
return nil, fmt.Errorf("unknown event %s", gitHubEvent)
} }
} }
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)
}
+449 -222
View File
File diff suppressed because it is too large Load Diff
+18 -18
View File
@@ -376,14 +376,14 @@ type DeletePayload struct {
// DeploymentPayload contains the information for GitHub's deployment hook // DeploymentPayload contains the information for GitHub's deployment hook
type DeploymentPayload struct { type DeploymentPayload struct {
Deployment struct { Deployment struct {
URL string `json:"url"` URL string `json:"url"`
ID int64 `json:"id"` ID int64 `json:"id"`
Sha string `json:"sha"` Sha string `json:"sha"`
Ref string `json:"ref"` Ref string `json:"ref"`
Task string `json:"task"` Task string `json:"task"`
Payload string `json:"payload"` Payload struct{} `json:"payload"`
Environment string `json:"environment"` Environment string `json:"environment"`
Description *string `json:"description"` Description *string `json:"description"`
Creator struct { Creator struct {
Login string `json:"login"` Login string `json:"login"`
ID int64 `json:"id"` ID int64 `json:"id"`
@@ -549,14 +549,14 @@ type DeploymentStatusPayload struct {
RepositoryURL string `json:"repository_url"` RepositoryURL string `json:"repository_url"`
} `json:"deployment_status"` } `json:"deployment_status"`
Deployment struct { Deployment struct {
URL string `json:"url"` URL string `json:"url"`
ID int64 `json:"id"` ID int64 `json:"id"`
Sha string `json:"sha"` Sha string `json:"sha"`
Ref string `json:"ref"` Ref string `json:"ref"`
Task string `json:"task"` Task string `json:"task"`
Payload string `json:"payload"` Payload struct{} `json:"payload"`
Environment string `json:"environment"` Environment string `json:"environment"`
Description *string `json:"description"` Description *string `json:"description"`
Creator struct { Creator struct {
Login string `json:"login"` Login string `json:"login"`
ID int64 `json:"id"` ID int64 `json:"id"`
@@ -2172,7 +2172,7 @@ type PingPayload struct {
AppID int `json:"app_id"` AppID int `json:"app_id"`
Config struct { Config struct {
ContentType string `json:"content_type"` ContentType string `json:"content_type"`
InsecureSSL int `json:"insecure_ssl"` InsecureSSL string `json:"insecure_ssl"`
Secret string `json:"secret"` Secret string `json:"secret"`
URL string `json:"url"` URL string `json:"url"`
} `json:"config"` } `json:"config"`
@@ -4230,7 +4230,7 @@ type PushPayload struct {
SiteAdmin bool `json:"site_admin"` SiteAdmin bool `json:"site_admin"`
} `json:"sender"` } `json:"sender"`
Installation struct { Installation struct {
Id int `json:"id"` ID int `json:"id"`
} `json:"installation"` } `json:"installation"`
} }
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"gopkg.in/go-playground/webhooks.v4" "gopkg.in/go-playground/webhooks.v5"
) )
// Webhook instance contains all methods needed to process events // Webhook instance contains all methods needed to process events
+1 -1
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.v4" "gopkg.in/go-playground/webhooks.v5"
) )
// NOTES: // NOTES:
+2 -1
View File
@@ -9,8 +9,9 @@ import (
"crypto/hmac" "crypto/hmac"
"crypto/sha256" "crypto/sha256"
"encoding/hex" "encoding/hex"
client "github.com/gogits/go-gogs-client" client "github.com/gogits/go-gogs-client"
"gopkg.in/go-playground/webhooks.v4" "gopkg.in/go-playground/webhooks.v5"
) )
// Webhook instance contains all methods needed to process events // Webhook instance contains all methods needed to process events
+95 -96
View File
@@ -1,124 +1,123 @@
package webhooks package webhooks
import ( import (
"fmt"
"net/http" "net/http"
) )
// Header provides http.Header to minimize imports // Header provides http.Header to minimize imports
type Header http.Header type Header http.Header
// Provider defines the type of webhook // // Provider defines the type of webhook
type Provider int // type Provider int
func (p Provider) String() string { // func (p Provider) String() string {
switch p { // switch p {
case GitHub: // case GitHub:
return "GitHub" // return "GitHub"
case Bitbucket: // case Bitbucket:
return "Bitbucket" // return "Bitbucket"
case GitLab: // case GitLab:
return "GitLab" // return "GitLab"
case Gogs: // case Gogs:
return "Gogs" // return "Gogs"
default: // default:
return "Unknown" // return "Unknown"
} // }
} // }
// webhooks available providers // // webhooks available providers
const ( // const (
GitHub Provider = iota // GitHub Provider = iota
Bitbucket // Bitbucket
GitLab // GitLab
Gogs // Gogs
) // )
// Webhook interface defines a webhook to receive 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)
} // }
type server struct { // type server struct {
hook Webhook // hook Webhook
path string // path string
includePathCheck bool // 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{}, header Header) type ProcessPayloadFunc func(payload interface{}, header Header) error
// Handler returns the webhook http.Handler for use in your own Mux implementation // // Handler returns the webhook http.Handler for use in your own Mux implementation
func Handler(hook Webhook) http.Handler { // func Handler(hook Webhook) http.Handler {
return &server{ // return &server{
hook: hook, // 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, // 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)) // DefaultLog.Info(fmt.Sprintf("Listening on addr: %s path: %s", addr, path))
return s.ListenAndServe() // return s.ListenAndServe()
} // }
// RunServer runs a custom server. // // RunServer runs a custom server.
func RunServer(s *http.Server, hook Webhook, path string) error { // func RunServer(s *http.Server, hook Webhook, path string) error {
srv := &server{ // srv := &server{
hook: hook, // hook: hook,
path: path, // path: path,
includePathCheck: true, // includePathCheck: true,
} // }
s.Handler = srv // s.Handler = srv
DefaultLog.Info(fmt.Sprintf("Listening on addr: %s path: %s", s.Addr, path)) // 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://github.com/go-playground/webhooks/blob/v2/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, // includePathCheck: true,
} // }
s.Handler = srv // s.Handler = srv
DefaultLog.Info(fmt.Sprintf("Listening on addr: %s path: %s", s.Addr, path)) // 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") // 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)) // 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)) // DefaultLog.Debug(fmt.Sprintf("Include path check: %t", s.includePathCheck))
if 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)) // 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)
} // }