1package gitlab
2
3import (
4	"encoding/json"
5	"fmt"
6	"net/http"
7)
8
9// EventType represents a Gitlab event type.
10type EventType string
11
12// List of available event types.
13const (
14	EventTypeBuild        EventType = "Build Hook"
15	EventTypeIssue        EventType = "Issue Hook"
16	EventTypeJob          EventType = "Job Hook"
17	EventTypeMergeRequest EventType = "Merge Request Hook"
18	EventTypeNote         EventType = "Note Hook"
19	EventTypePipeline     EventType = "Pipeline Hook"
20	EventTypePush         EventType = "Push Hook"
21	EventTypeTagPush      EventType = "Tag Push Hook"
22	EventTypeWikiPage     EventType = "Wiki Page Hook"
23)
24
25const (
26	noteableTypeCommit       = "Commit"
27	noteableTypeMergeRequest = "MergeRequest"
28	noteableTypeIssue        = "Issue"
29	noteableTypeSnippet      = "Snippet"
30)
31
32type noteEvent struct {
33	ObjectKind       string `json:"object_kind"`
34	ObjectAttributes struct {
35		NoteableType string `json:"noteable_type"`
36	} `json:"object_attributes"`
37}
38
39const eventTypeHeader = "X-Gitlab-Event"
40
41// WebhookEventType returns the event type for the given request.
42func WebhookEventType(r *http.Request) EventType {
43	return EventType(r.Header.Get(eventTypeHeader))
44}
45
46// ParseWebhook parses the event payload. For recognized event types, a
47// value of the corresponding struct type will be returned. An error will
48// be returned for unrecognized event types.
49//
50// Example usage:
51//
52// func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
53//     payload, err := ioutil.ReadAll(r.Body)
54//     if err != nil { ... }
55//     event, err := gitlab.ParseWebhook(gitlab.WebhookEventType(r), payload)
56//     if err != nil { ... }
57//     switch event := event.(type) {
58//     case *gitlab.PushEvent:
59//         processPushEvent(event)
60//     case *gitlab.MergeEvent:
61//         processMergeEvent(event)
62//     ...
63//     }
64// }
65//
66func ParseWebhook(eventType EventType, payload []byte) (event interface{}, err error) {
67	switch eventType {
68	case EventTypeBuild:
69		event = &BuildEvent{}
70	case EventTypeIssue:
71		event = &IssueEvent{}
72	case EventTypeJob:
73		event = &JobEvent{}
74	case EventTypeMergeRequest:
75		event = &MergeEvent{}
76	case EventTypePipeline:
77		event = &PipelineEvent{}
78	case EventTypePush:
79		event = &PushEvent{}
80	case EventTypeTagPush:
81		event = &TagEvent{}
82	case EventTypeWikiPage:
83		event = &WikiPageEvent{}
84	case EventTypeNote:
85		note := &noteEvent{}
86		err := json.Unmarshal(payload, note)
87		if err != nil {
88			return nil, err
89		}
90
91		if note.ObjectKind != "note" {
92			return nil, fmt.Errorf("unexpected object kind %s", note.ObjectKind)
93		}
94
95		switch note.ObjectAttributes.NoteableType {
96		case noteableTypeCommit:
97			event = &CommitCommentEvent{}
98		case noteableTypeMergeRequest:
99			event = &MergeCommentEvent{}
100		case noteableTypeIssue:
101			event = &IssueCommentEvent{}
102		case noteableTypeSnippet:
103			event = &SnippetCommentEvent{}
104		default:
105			return nil, fmt.Errorf("unexpected noteable type %s", note.ObjectAttributes.NoteableType)
106		}
107
108	default:
109		return nil, fmt.Errorf("unexpected event type: %s", eventType)
110	}
111
112	if err := json.Unmarshal(payload, event); err != nil {
113		return nil, err
114	}
115
116	return event, nil
117}
118