• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

go-playground / webhooks / 13774114374

07 Jan 2025 09:34PM UTC coverage: 88.933%. Remained the same
13774114374

Pull #207

github

jorng
Add `draft` attribute to bitbucket-server `PullRequest`

As of Bitbucket Data Center 8.17, pull requests can be in a "Draft"
state, similar to other providers like Github/Gitea.

This change updates the `PullRequest` struct for the bitbucket-server
package to include this new attribute, so it can be used in handling
webhook payloads.
Pull Request #207: Add `draft` attribute to bitbucket-server `PullRequest`

900 of 1012 relevant lines covered (88.93%)

8.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.76
/gogs/gogs.go
1
package gogs
2

3
import (
4
        "encoding/json"
5
        "errors"
6
        "fmt"
7
        "io"
8
        "net/http"
9

10
        "crypto/hmac"
11
        "crypto/sha256"
12
        "encoding/hex"
13

14
        client "github.com/gogits/go-gogs-client"
15
)
16

17
// parse errors
18
var (
19
        ErrEventNotSpecifiedToParse   = errors.New("no Event specified to parse")
20
        ErrInvalidHTTPMethod          = errors.New("invalid HTTP Method")
21
        ErrMissingGogsEventHeader     = errors.New("missing X-Gogs-Event Header")
22
        ErrMissingGogsSignatureHeader = errors.New("missing X-Gogs-Signature Header")
23
        ErrEventNotFound              = errors.New("event not defined to be parsed")
24
        ErrParsingPayload             = errors.New("error parsing payload")
25
        ErrHMACVerificationFailed     = errors.New("HMAC verification failed")
26
)
27

28
// Option is a configuration option for the webhook
29
type Option func(*Webhook) error
30

31
// Options is a namespace var for configuration options
32
var Options = WebhookOptions{}
33

34
// WebhookOptions is a namespace for configuration option methods
35
type WebhookOptions struct{}
36

37
// Secret registers the GitLab secret
38
func (WebhookOptions) Secret(secret string) Option {
1✔
39
        return func(hook *Webhook) error {
2✔
40
                hook.secret = secret
1✔
41
                return nil
1✔
42
        }
1✔
43
}
44

45
// Webhook instance contains all methods needed to process events
46
type Webhook struct {
47
        secret string
48
}
49

50
// Event defines a Gogs hook event type
51
type Event string
52

53
// Gogs hook types
54
const (
55
        CreateEvent       Event = "create"
56
        DeleteEvent       Event = "delete"
57
        ForkEvent         Event = "fork"
58
        PushEvent         Event = "push"
59
        IssuesEvent       Event = "issues"
60
        IssueCommentEvent Event = "issue_comment"
61
        PullRequestEvent  Event = "pull_request"
62
        ReleaseEvent      Event = "release"
63
)
64

65
// New creates and returns a WebHook instance denoted by the Provider type
66
func New(options ...Option) (*Webhook, error) {
1✔
67
        hook := new(Webhook)
1✔
68
        for _, opt := range options {
2✔
69
                if err := opt(hook); err != nil {
1✔
70
                        return nil, errors.New("Error applying Option")
×
71
                }
×
72
        }
73
        return hook, nil
1✔
74
}
75

76
// Parse verifies and parses the events specified and returns the payload object or an error
77
func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) {
13✔
78
        defer func() {
26✔
79
                _, _ = io.Copy(io.Discard, r.Body)
13✔
80
                _ = r.Body.Close()
13✔
81
        }()
13✔
82

83
        if len(events) == 0 {
13✔
84
                return nil, ErrEventNotSpecifiedToParse
×
85
        }
×
86
        if r.Method != http.MethodPost {
13✔
87
                return nil, ErrInvalidHTTPMethod
×
88
        }
×
89

90
        event := r.Header.Get("X-Gogs-Event")
13✔
91
        if len(event) == 0 {
15✔
92
                return nil, ErrMissingGogsEventHeader
2✔
93
        }
2✔
94

95
        gogsEvent := Event(event)
11✔
96

11✔
97
        var found bool
11✔
98
        for _, evt := range events {
22✔
99
                if evt == gogsEvent {
21✔
100
                        found = true
10✔
101
                        break
10✔
102
                }
103
        }
104
        // event not defined to be parsed
105
        if !found {
12✔
106
                return nil, ErrEventNotFound
1✔
107
        }
1✔
108

109
        payload, err := io.ReadAll(r.Body)
10✔
110
        if err != nil || len(payload) == 0 {
11✔
111
                return nil, ErrParsingPayload
1✔
112
        }
1✔
113

114
        // If we have a Secret set, we should check the MAC
115
        if len(hook.secret) > 0 {
18✔
116
                signature := r.Header.Get("X-Gogs-Signature")
9✔
117
                if len(signature) == 0 {
10✔
118
                        return nil, ErrMissingGogsSignatureHeader
1✔
119
                }
1✔
120

121
                mac := hmac.New(sha256.New, []byte(hook.secret))
8✔
122
                _, _ = mac.Write(payload)
8✔
123

8✔
124
                expectedMAC := hex.EncodeToString(mac.Sum(nil))
8✔
125

8✔
126
                if !hmac.Equal([]byte(signature), []byte(expectedMAC)) {
8✔
127
                        return nil, ErrHMACVerificationFailed
×
128
                }
×
129
        }
130

131
        switch gogsEvent {
8✔
132
        case CreateEvent:
1✔
133
                var pl client.CreatePayload
1✔
134
                err = json.Unmarshal([]byte(payload), &pl)
1✔
135
                return pl, err
1✔
136

137
        case ReleaseEvent:
1✔
138
                var pl client.ReleasePayload
1✔
139
                err = json.Unmarshal([]byte(payload), &pl)
1✔
140
                return pl, err
1✔
141

142
        case PushEvent:
1✔
143
                var pl client.PushPayload
1✔
144
                err = json.Unmarshal([]byte(payload), &pl)
1✔
145
                return pl, err
1✔
146

147
        case DeleteEvent:
1✔
148
                var pl client.DeletePayload
1✔
149
                err = json.Unmarshal([]byte(payload), &pl)
1✔
150
                return pl, err
1✔
151

152
        case ForkEvent:
1✔
153
                var pl client.ForkPayload
1✔
154
                err = json.Unmarshal([]byte(payload), &pl)
1✔
155
                return pl, err
1✔
156

157
        case IssuesEvent:
1✔
158
                var pl client.IssuesPayload
1✔
159
                err = json.Unmarshal([]byte(payload), &pl)
1✔
160
                return pl, err
1✔
161

162
        case IssueCommentEvent:
1✔
163
                var pl client.IssueCommentPayload
1✔
164
                err = json.Unmarshal([]byte(payload), &pl)
1✔
165
                return pl, err
1✔
166

167
        case PullRequestEvent:
1✔
168
                var pl client.PullRequestPayload
1✔
169
                err = json.Unmarshal([]byte(payload), &pl)
1✔
170
                return pl, err
1✔
171

172
        default:
×
173
                return nil, fmt.Errorf("unknown event %s", gogsEvent)
×
174
        }
175
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc