MartinHeinz/go-github-app

View on GitHub
cmd/app/webhooks/github.go

Summary

Maintainability
A
0 mins
Test Coverage
D
63%
package webhooks

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "github.com/MartinHeinz/go-github-app/cmd/app/config"
    "github.com/gin-gonic/gin"
    "io/ioutil"
    "log"
    "net/http"
)

type Event string

const (
    Install     Event = "installation"
    Ping        Event = "ping"
    Push        Event = "push"
    PullRequest Event = "pull_request"
)

var Events = []Event{
    Install,
    Ping,
    Push,
    PullRequest,
}

var Consumers = map[string]func(EventPayload) error{
    string(Install):     consumeInstallEvent,
    string(Ping):        consumePingEvent,
    string(Push):        consumePushEvent,
    string(PullRequest): consumePullRequestEvent,
}

func VerifySignature(payload []byte, signature string) bool {
    key := hmac.New(sha256.New, []byte(config.Config.GitHubWebhookSecret))
    key.Write([]byte(string(payload)))
    computedSignature := "sha256=" + hex.EncodeToString(key.Sum(nil))
    log.Printf("computed signature: %s", computedSignature)

    return computedSignature == signature
}

func ConsumeEvent(c *gin.Context) {
    payload, _ := ioutil.ReadAll(c.Request.Body)

    if !VerifySignature(payload, c.GetHeader("X-Hub-Signature-256")) {
        c.AbortWithStatus(http.StatusUnauthorized)
        log.Println("signatures don't match")
    }

    event := c.GetHeader("X-GitHub-Event")

    for _, e := range Events {
        if string(e) == event {
            log.Printf("consuming event: %s", e)
            var p EventPayload
            json.Unmarshal(payload, &p)
            if err := Consumers[string(e)](p); err != nil {
                log.Printf("couldn't consume event %s, error: %+v", string(e), err)
                // We're responding to GitHub API, we really just want to say "OK" or "not OK"
                c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"reason": err})
            }
            log.Printf("consumed event: %s", e)
            c.AbortWithStatus(http.StatusNoContent)
            return
        }
    }
    log.Printf("Unsupported event: %s", event)
    c.AbortWithStatusJSON(http.StatusNotImplemented, gin.H{"reason": "Unsupported event: " + event})
}

func consumeInstallEvent(payload EventPayload) error {
    // Process event ...
    // Insert data into database ...
    return nil
}

func consumePingEvent(payload EventPayload) error {
    // Process event ...
    // Insert data into database ...
    return nil
}

func consumePushEvent(payload EventPayload) error {

    // Process event ...
    // Insert data into database ...

    log.Printf("Received push from %s, by user %s, on branch %s",
        payload.Repository.FullName,
        payload.Pusher.Name,
        payload.Ref)

    // Enumerating commits
    var commits []string
    for _, commit := range payload.Commits {
        commits = append(commits, commit.ID)
    }
    log.Printf("Pushed commits: %v", commits)

    return nil
}

func consumePullRequestEvent(payload EventPayload) error {
    // Process event ...
    // Insert data into database ...
    return nil
}