Skip to content

Commit d63b350

Browse files
authored
Merge branch 'master' into impl-gitlab-release
2 parents 220b666 + 2d5aa68 commit d63b350

29 files changed

+1346
-25
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
Library webhooks
22
================
3-
<img align="right" src="https://raw.githubusercontent.com/go-playground/webhooks/v6/logo.png">![Project status](https://img.shields.io/badge/version-6.1.0-green.svg)
3+
<img align="right" src="https://raw.githubusercontent.com/go-playground/webhooks/v6/logo.png">![Project status](https://img.shields.io/badge/version-6.3.0-green.svg)
44
[![Test](https://github.com/go-playground/webhooks/workflows/Test/badge.svg?branch=master)](https://github.com/go-playground/webhooks/actions)
55
[![Coverage Status](https://coveralls.io/repos/go-playground/webhooks/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/webhooks?branch=master)
66
[![Go Report Card](https://goreportcard.com/badge/go-playground/webhooks)](https://goreportcard.com/report/go-playground/webhooks)
77
[![GoDoc](https://godoc.org/github.com/go-playground/webhooks/v6?status.svg)](https://godoc.org/github.com/go-playground/webhooks/v6)
88
![License](https://img.shields.io/dub/l/vibe-d.svg)
99

10-
Library webhooks allows for easy receiving and parsing of GitHub, Bitbucket, GitLab, Docker Hub and Gogs Webhook Events
10+
Library webhooks allows for easy receiving and parsing of GitHub, Bitbucket, GitLab, Docker Hub, Gogs and Azure DevOps Webhook Events
1111

1212
Features:
1313

azuredevops/azuredevops.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package azuredevops
2+
3+
// this package receives Azure DevOps Server webhooks
4+
// https://docs.microsoft.com/en-us/azure/devops/service-hooks/services/webhooks?view=azure-devops-2020
5+
6+
import (
7+
"encoding/json"
8+
"errors"
9+
"fmt"
10+
"io"
11+
"net/http"
12+
)
13+
14+
// parse errors
15+
var (
16+
ErrInvalidHTTPMethod = errors.New("invalid HTTP Method")
17+
ErrParsingPayload = errors.New("error parsing payload")
18+
)
19+
20+
// Event defines an Azure DevOps server hook event type
21+
type Event string
22+
23+
// Azure DevOps Server hook types
24+
const (
25+
BuildCompleteEventType Event = "build.complete"
26+
GitPullRequestCreatedEventType Event = "git.pullrequest.created"
27+
GitPullRequestUpdatedEventType Event = "git.pullrequest.updated"
28+
GitPullRequestMergedEventType Event = "git.pullrequest.merged"
29+
GitPushEventType Event = "git.push"
30+
)
31+
32+
// Webhook instance contains all methods needed to process events
33+
type Webhook struct {
34+
}
35+
36+
// New creates and returns a WebHook instance
37+
func New() (*Webhook, error) {
38+
hook := new(Webhook)
39+
return hook, nil
40+
}
41+
42+
// Parse verifies and parses the events specified and returns the payload object or an error
43+
func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) {
44+
defer func() {
45+
_, _ = io.Copy(io.Discard, r.Body)
46+
_ = r.Body.Close()
47+
}()
48+
49+
if r.Method != http.MethodPost {
50+
return nil, ErrInvalidHTTPMethod
51+
}
52+
53+
payload, err := io.ReadAll(r.Body)
54+
if err != nil || len(payload) == 0 {
55+
return nil, ErrParsingPayload
56+
}
57+
58+
var pl BasicEvent
59+
err = json.Unmarshal([]byte(payload), &pl)
60+
if err != nil {
61+
return nil, ErrParsingPayload
62+
}
63+
64+
switch pl.EventType {
65+
case GitPushEventType:
66+
var fpl GitPushEvent
67+
err = json.Unmarshal([]byte(payload), &fpl)
68+
return fpl, err
69+
case GitPullRequestCreatedEventType, GitPullRequestMergedEventType, GitPullRequestUpdatedEventType:
70+
var fpl GitPullRequestEvent
71+
err = json.Unmarshal([]byte(payload), &fpl)
72+
return fpl, err
73+
case BuildCompleteEventType:
74+
var fpl BuildCompleteEvent
75+
err = json.Unmarshal([]byte(payload), &fpl)
76+
return fpl, err
77+
default:
78+
return nil, fmt.Errorf("unknown event %s", pl.EventType)
79+
}
80+
}

azuredevops/azuredevops_test.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package azuredevops
2+
3+
import (
4+
"log"
5+
"net/http"
6+
"net/http/httptest"
7+
"os"
8+
"testing"
9+
10+
"reflect"
11+
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
// NOTES:
16+
// - Run "go test" to run tests
17+
// - Run "gocov test | gocov report" to report on test converage by file
18+
// - Run "gocov test | gocov annotate -" to report on all code and functions, those ,marked with "MISS" were never called
19+
//
20+
// or
21+
//
22+
// -- may be a good idea to change to output path to somewherelike /tmp
23+
// go test -coverprofile cover.out && go tool cover -html=cover.out -o cover.html
24+
//
25+
26+
const (
27+
virtualDir = "/webhooks"
28+
)
29+
30+
var hook *Webhook
31+
32+
func TestMain(m *testing.M) {
33+
34+
// setup
35+
var err error
36+
hook, err = New()
37+
if err != nil {
38+
log.Fatal(err)
39+
}
40+
os.Exit(m.Run())
41+
// teardown
42+
}
43+
44+
func newServer(handler http.HandlerFunc) *httptest.Server {
45+
mux := http.NewServeMux()
46+
mux.HandleFunc(virtualDir, handler)
47+
return httptest.NewServer(mux)
48+
}
49+
50+
func TestWebhooks(t *testing.T) {
51+
assert := require.New(t)
52+
tests := []struct {
53+
name string
54+
event Event
55+
typ interface{}
56+
filename string
57+
headers http.Header
58+
}{
59+
{
60+
name: "build.complete",
61+
event: BuildCompleteEventType,
62+
typ: BuildCompleteEvent{},
63+
filename: "../testdata/azuredevops/build.complete.json",
64+
},
65+
{
66+
name: "git.pullrequest.created",
67+
event: GitPullRequestCreatedEventType,
68+
typ: GitPullRequestEvent{},
69+
filename: "../testdata/azuredevops/git.pullrequest.created.json",
70+
},
71+
{
72+
name: "git.pullrequest.merged",
73+
event: GitPullRequestMergedEventType,
74+
typ: GitPullRequestEvent{},
75+
filename: "../testdata/azuredevops/git.pullrequest.merged.json",
76+
},
77+
{
78+
name: "git.pullrequest.updated",
79+
event: GitPullRequestUpdatedEventType,
80+
typ: GitPullRequestEvent{},
81+
filename: "../testdata/azuredevops/git.pullrequest.updated.json",
82+
},
83+
{
84+
name: "git.push",
85+
event: GitPushEventType,
86+
typ: GitPushEvent{},
87+
filename: "../testdata/azuredevops/git.push.json",
88+
},
89+
}
90+
91+
for _, tt := range tests {
92+
tc := tt
93+
client := &http.Client{}
94+
t.Run(tt.name, func(t *testing.T) {
95+
t.Parallel()
96+
payload, err := os.Open(tc.filename)
97+
assert.NoError(err)
98+
defer func() {
99+
_ = payload.Close()
100+
}()
101+
102+
var parseError error
103+
var results interface{}
104+
server := newServer(func(w http.ResponseWriter, r *http.Request) {
105+
results, parseError = hook.Parse(r, tc.event)
106+
})
107+
defer server.Close()
108+
req, err := http.NewRequest(http.MethodPost, server.URL+virtualDir, payload)
109+
assert.NoError(err)
110+
req.Header.Set("Content-Type", "application/json")
111+
112+
resp, err := client.Do(req)
113+
assert.NoError(err)
114+
assert.Equal(http.StatusOK, resp.StatusCode)
115+
assert.NoError(parseError)
116+
assert.Equal(reflect.TypeOf(tc.typ), reflect.TypeOf(results))
117+
})
118+
}
119+
}

0 commit comments

Comments
 (0)