Skip to content

Commit 739eddd

Browse files
author
Dean Karn
authored
Merge pull request #54 from Munsio/feature/gitlab-system-hooks
* Add gitlab system hook parsing If you use gitlab system hooks you can now send push/tag_push/merge_request events * Declare err and payload to avoid scope issues * Refactor the whole event switch into own function * Remove unnecessary comment * Give the user the choice to parse SystemHooks
2 parents acf48b9 + bad16bf commit 739eddd

File tree

3 files changed

+117
-27
lines changed

3 files changed

+117
-27
lines changed

gitlab/gitlab.go

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ var (
1717
ErrGitLabTokenVerificationFailed = errors.New("X-Gitlab-Token validation failed")
1818
ErrEventNotFound = errors.New("event not defined to be parsed")
1919
ErrParsingPayload = errors.New("error parsing payload")
20+
ErrParsingSystemPayload = errors.New("error parsing system payload")
2021
// ErrHMACVerificationFailed = errors.New("HMAC verification failed")
2122
)
2223

@@ -31,6 +32,11 @@ const (
3132
WikiPageEvents Event = "Wiki Page Hook"
3233
PipelineEvents Event = "Pipeline Hook"
3334
BuildEvents Event = "Build Hook"
35+
SystemHookEvents Event = "System Hook"
36+
37+
objectPush string = "push"
38+
objectTag string = "tag_push"
39+
objectMergeRequest string = "merge_request"
3440
)
3541

3642
// Option is a configuration option for the webhook
@@ -55,7 +61,7 @@ type Webhook struct {
5561
secret string
5662
}
5763

58-
// Event defines a GitHub hook event type
64+
// Event defines a GitLab hook event type by the X-Gitlab-Event Header
5965
type Event string
6066

6167
// New creates and returns a WebHook instance denoted by the Provider type
@@ -83,13 +89,31 @@ func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error)
8389
return nil, ErrInvalidHTTPMethod
8490
}
8591

92+
// If we have a Secret set, we should check the MAC
93+
if len(hook.secret) > 0 {
94+
signature := r.Header.Get("X-Gitlab-Token")
95+
if signature != hook.secret {
96+
return nil, ErrGitLabTokenVerificationFailed
97+
}
98+
}
99+
86100
event := r.Header.Get("X-Gitlab-Event")
87101
if len(event) == 0 {
88102
return nil, ErrMissingGitLabEventHeader
89103
}
90104

91105
gitLabEvent := Event(event)
92106

107+
payload, err := ioutil.ReadAll(r.Body)
108+
if err != nil || len(payload) == 0 {
109+
return nil, ErrParsingPayload
110+
}
111+
112+
return eventParsing(gitLabEvent, events, payload)
113+
}
114+
115+
func eventParsing(gitLabEvent Event, events []Event, payload []byte) (interface{}, error) {
116+
93117
var found bool
94118
for _, evt := range events {
95119
if evt == gitLabEvent {
@@ -102,64 +126,68 @@ func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error)
102126
return nil, ErrEventNotFound
103127
}
104128

105-
payload, err := ioutil.ReadAll(r.Body)
106-
if err != nil || len(payload) == 0 {
107-
return nil, ErrParsingPayload
108-
}
109-
110-
// If we have a Secret set, we should check the MAC
111-
if len(hook.secret) > 0 {
112-
signature := r.Header.Get("X-Gitlab-Token")
113-
if signature != hook.secret {
114-
return nil, ErrGitLabTokenVerificationFailed
115-
}
116-
}
117-
118129
switch gitLabEvent {
119130
case PushEvents:
120131
var pl PushEventPayload
121-
err = json.Unmarshal([]byte(payload), &pl)
132+
err := json.Unmarshal([]byte(payload), &pl)
122133
return pl, err
123134

124135
case TagEvents:
125136
var pl TagEventPayload
126-
err = json.Unmarshal([]byte(payload), &pl)
137+
err := json.Unmarshal([]byte(payload), &pl)
127138
return pl, err
128139

129140
case ConfidentialIssuesEvents:
130141
var pl ConfidentialIssueEventPayload
131-
err = json.Unmarshal([]byte(payload), &pl)
142+
err := json.Unmarshal([]byte(payload), &pl)
132143
return pl, err
133144

134145
case IssuesEvents:
135146
var pl IssueEventPayload
136-
err = json.Unmarshal([]byte(payload), &pl)
147+
err := json.Unmarshal([]byte(payload), &pl)
137148
return pl, err
138149

139150
case CommentEvents:
140151
var pl CommentEventPayload
141-
err = json.Unmarshal([]byte(payload), &pl)
152+
err := json.Unmarshal([]byte(payload), &pl)
142153
return pl, err
143154

144155
case MergeRequestEvents:
145156
var pl MergeRequestEventPayload
146-
err = json.Unmarshal([]byte(payload), &pl)
157+
err := json.Unmarshal([]byte(payload), &pl)
147158
return pl, err
148159

149160
case WikiPageEvents:
150161
var pl WikiPageEventPayload
151-
err = json.Unmarshal([]byte(payload), &pl)
162+
err := json.Unmarshal([]byte(payload), &pl)
152163
return pl, err
153164

154165
case PipelineEvents:
155166
var pl PipelineEventPayload
156-
err = json.Unmarshal([]byte(payload), &pl)
167+
err := json.Unmarshal([]byte(payload), &pl)
157168
return pl, err
158169

159170
case BuildEvents:
160171
var pl BuildEventPayload
161-
err = json.Unmarshal([]byte(payload), &pl)
172+
err := json.Unmarshal([]byte(payload), &pl)
162173
return pl, err
174+
175+
case SystemHookEvents:
176+
var pl SystemHookPayload
177+
err := json.Unmarshal([]byte(payload), &pl)
178+
if err != nil {
179+
return nil, err
180+
}
181+
switch pl.ObjectKind {
182+
case objectPush:
183+
return eventParsing(PushEvents, events, payload)
184+
case objectTag:
185+
return eventParsing(TagEvents, events, payload)
186+
case objectMergeRequest:
187+
return eventParsing(MergeRequestEvents, events, payload)
188+
default:
189+
return nil, fmt.Errorf("unknown system hook event %s", gitLabEvent)
190+
}
163191
default:
164192
return nil, fmt.Errorf("unknown event %s", gitLabEvent)
165193
}

gitlab/gitlab_test.go

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@ package gitlab
22

33
import (
44
"bytes"
5+
"io"
56
"log"
67
"net/http"
78
"net/http/httptest"
89
"os"
9-
"testing"
10-
11-
"io"
12-
1310
"reflect"
11+
"testing"
1412

1513
"github.com/stretchr/testify/require"
1614
)
@@ -266,3 +264,62 @@ func TestWebhooks(t *testing.T) {
266264
})
267265
}
268266
}
267+
268+
func TestSystemHooks(t *testing.T) {
269+
assert := require.New(t)
270+
tests := []struct {
271+
name string
272+
event Event
273+
typ interface{}
274+
filename string
275+
}{
276+
{
277+
name: "PushEvent",
278+
event: PushEvents,
279+
typ: PushEventPayload{},
280+
filename: "../testdata/gitlab/push-event.json",
281+
},
282+
{
283+
name: "TagEvent",
284+
event: TagEvents,
285+
typ: TagEventPayload{},
286+
filename: "../testdata/gitlab/tag-event.json",
287+
},
288+
{
289+
name: "MergeRequestEvent",
290+
event: MergeRequestEvents,
291+
typ: MergeRequestEventPayload{},
292+
filename: "../testdata/gitlab/merge-request-event.json",
293+
},
294+
}
295+
for _, tt := range tests {
296+
tc := tt
297+
client := &http.Client{}
298+
t.Run(tt.name, func(t *testing.T) {
299+
t.Parallel()
300+
payload, err := os.Open(tc.filename)
301+
assert.NoError(err)
302+
defer func() {
303+
_ = payload.Close()
304+
}()
305+
306+
var parseError error
307+
var results interface{}
308+
server := newServer(func(w http.ResponseWriter, r *http.Request) {
309+
results, parseError = hook.Parse(r, SystemHookEvents, tc.event)
310+
})
311+
defer server.Close()
312+
req, err := http.NewRequest(http.MethodPost, server.URL+path, payload)
313+
assert.NoError(err)
314+
req.Header.Set("Content-Type", "application/json")
315+
req.Header.Set("X-Gitlab-Token", "sampleToken!")
316+
req.Header.Set("X-Gitlab-Event", "System Hook")
317+
318+
resp, err := client.Do(req)
319+
assert.NoError(err)
320+
assert.Equal(http.StatusOK, resp.StatusCode)
321+
assert.NoError(parseError)
322+
assert.Equal(reflect.TypeOf(tc.typ), reflect.TypeOf(results))
323+
})
324+
}
325+
}

gitlab/payload.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,11 @@ type BuildEventPayload struct {
148148
Repository Repository `json:"repository"`
149149
}
150150

151+
// SystemHookPayload contains the ObjectKind to match with real hook events
152+
type SystemHookPayload struct {
153+
ObjectKind string `json:"object_kind"`
154+
}
155+
151156
// Issue contains all of the GitLab issue information
152157
type Issue struct {
153158
ID int64 `json:"id"`

0 commit comments

Comments
 (0)