Skip to content

Commit 3f1c084

Browse files
jolheiserlafriksguillep2k
authored
Granular webhook events (#9626)
* Initial work Signed-off-by: jolheiser <[email protected]> * Add PR reviews and API coverage Signed-off-by: jolheiser <[email protected]> * Split up events Signed-off-by: jolheiser <[email protected]> * Add migration and locale Signed-off-by: jolheiser <[email protected]> * Format Signed-off-by: jolheiser <[email protected]> * Revert IsPull Signed-off-by: jolheiser <[email protected]> * Fix comments Signed-off-by: jolheiser <[email protected]> * Fix tests Signed-off-by: jolheiser <[email protected]> * Fix PR reviews Signed-off-by: jolheiser <[email protected]> * Fix issue_comment Signed-off-by: jolheiser <[email protected]> * Make fmt Signed-off-by: jolheiser <[email protected]> * Migrations Signed-off-by: jolheiser <[email protected]> * Backwards compatible API Signed-off-by: jolheiser <[email protected]> * Fix feishu Signed-off-by: jolheiser <[email protected]> * Move session commit Signed-off-by: jolheiser <[email protected]> Co-authored-by: Lauris BH <[email protected]> Co-authored-by: guillep2k <[email protected]>
1 parent 80db442 commit 3f1c084

File tree

18 files changed

+602
-151
lines changed

18 files changed

+602
-151
lines changed

models/migrations/migrations.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ var migrations = []Migration{
192192
NewMigration("fix merge base for pull requests", fixMergeBase),
193193
// v129 -> v130
194194
NewMigration("remove dependencies from deleted repositories", purgeUnusedDependencies),
195+
// v130 -> v131
196+
NewMigration("Expand webhooks for more granularity", expandWebhooks),
195197
}
196198

197199
// Migrate database to current version

models/migrations/v130.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright 2020 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package migrations
6+
7+
import (
8+
"encoding/json"
9+
10+
"xorm.io/xorm"
11+
)
12+
13+
func expandWebhooks(x *xorm.Engine) error {
14+
15+
type ChooseEvents struct {
16+
Issues bool `json:"issues"`
17+
IssueAssign bool `json:"issue_assign"`
18+
IssueLabel bool `json:"issue_label"`
19+
IssueMilestone bool `json:"issue_milestone"`
20+
IssueComment bool `json:"issue_comment"`
21+
PullRequest bool `json:"pull_request"`
22+
PullRequestAssign bool `json:"pull_request_assign"`
23+
PullRequestLabel bool `json:"pull_request_label"`
24+
PullRequestMilestone bool `json:"pull_request_milestone"`
25+
PullRequestComment bool `json:"pull_request_comment"`
26+
PullRequestReview bool `json:"pull_request_review"`
27+
PullRequestSync bool `json:"pull_request_sync"`
28+
}
29+
30+
type Events struct {
31+
PushOnly bool `json:"push_only"`
32+
SendEverything bool `json:"send_everything"`
33+
ChooseEvents bool `json:"choose_events"`
34+
BranchFilter string `json:"branch_filter"`
35+
Events ChooseEvents `json:"events"`
36+
}
37+
38+
type Webhook struct {
39+
ID int64
40+
Events string
41+
}
42+
43+
var events Events
44+
var bytes []byte
45+
var last int
46+
const batchSize = 50
47+
sess := x.NewSession()
48+
defer sess.Close()
49+
for {
50+
if err := sess.Begin(); err != nil {
51+
return err
52+
}
53+
var results = make([]Webhook, 0, batchSize)
54+
err := x.OrderBy("id").
55+
Limit(batchSize, last).
56+
Find(&results)
57+
if err != nil {
58+
return err
59+
}
60+
if len(results) == 0 {
61+
break
62+
}
63+
last += len(results)
64+
65+
for _, res := range results {
66+
if err = json.Unmarshal([]byte(res.Events), &events); err != nil {
67+
return err
68+
}
69+
70+
if events.Events.Issues {
71+
events.Events.IssueAssign = true
72+
events.Events.IssueLabel = true
73+
events.Events.IssueMilestone = true
74+
events.Events.IssueComment = true
75+
}
76+
77+
if events.Events.PullRequest {
78+
events.Events.PullRequestAssign = true
79+
events.Events.PullRequestLabel = true
80+
events.Events.PullRequestMilestone = true
81+
events.Events.PullRequestComment = true
82+
events.Events.PullRequestReview = true
83+
events.Events.PullRequestSync = true
84+
}
85+
86+
if bytes, err = json.Marshal(&events); err != nil {
87+
return err
88+
}
89+
90+
_, err = sess.Exec("UPDATE webhook SET events = ? WHERE id = ?", string(bytes), res.ID)
91+
if err != nil {
92+
return err
93+
}
94+
}
95+
96+
if err := sess.Commit(); err != nil {
97+
return err
98+
}
99+
}
100+
return nil
101+
}

models/webhook.go

Lines changed: 147 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,24 @@ func IsValidHookContentType(name string) bool {
5757

5858
// HookEvents is a set of web hook events
5959
type HookEvents struct {
60-
Create bool `json:"create"`
61-
Delete bool `json:"delete"`
62-
Fork bool `json:"fork"`
63-
Issues bool `json:"issues"`
64-
IssueComment bool `json:"issue_comment"`
65-
Push bool `json:"push"`
66-
PullRequest bool `json:"pull_request"`
67-
Repository bool `json:"repository"`
68-
Release bool `json:"release"`
60+
Create bool `json:"create"`
61+
Delete bool `json:"delete"`
62+
Fork bool `json:"fork"`
63+
Issues bool `json:"issues"`
64+
IssueAssign bool `json:"issue_assign"`
65+
IssueLabel bool `json:"issue_label"`
66+
IssueMilestone bool `json:"issue_milestone"`
67+
IssueComment bool `json:"issue_comment"`
68+
Push bool `json:"push"`
69+
PullRequest bool `json:"pull_request"`
70+
PullRequestAssign bool `json:"pull_request_assign"`
71+
PullRequestLabel bool `json:"pull_request_label"`
72+
PullRequestMilestone bool `json:"pull_request_milestone"`
73+
PullRequestComment bool `json:"pull_request_comment"`
74+
PullRequestReview bool `json:"pull_request_review"`
75+
PullRequestSync bool `json:"pull_request_sync"`
76+
Repository bool `json:"repository"`
77+
Release bool `json:"release"`
6978
}
7079

7180
// HookEvent represents events that will delivery hook.
@@ -154,6 +163,24 @@ func (w *Webhook) HasIssuesEvent() bool {
154163
(w.ChooseEvents && w.HookEvents.Issues)
155164
}
156165

166+
// HasIssuesAssignEvent returns true if hook enabled issues assign event.
167+
func (w *Webhook) HasIssuesAssignEvent() bool {
168+
return w.SendEverything ||
169+
(w.ChooseEvents && w.HookEvents.IssueAssign)
170+
}
171+
172+
// HasIssuesLabelEvent returns true if hook enabled issues label event.
173+
func (w *Webhook) HasIssuesLabelEvent() bool {
174+
return w.SendEverything ||
175+
(w.ChooseEvents && w.HookEvents.IssueLabel)
176+
}
177+
178+
// HasIssuesMilestoneEvent returns true if hook enabled issues milestone event.
179+
func (w *Webhook) HasIssuesMilestoneEvent() bool {
180+
return w.SendEverything ||
181+
(w.ChooseEvents && w.HookEvents.IssueMilestone)
182+
}
183+
157184
// HasIssueCommentEvent returns true if hook enabled issue_comment event.
158185
func (w *Webhook) HasIssueCommentEvent() bool {
159186
return w.SendEverything ||
@@ -172,6 +199,54 @@ func (w *Webhook) HasPullRequestEvent() bool {
172199
(w.ChooseEvents && w.HookEvents.PullRequest)
173200
}
174201

202+
// HasPullRequestAssignEvent returns true if hook enabled pull request assign event.
203+
func (w *Webhook) HasPullRequestAssignEvent() bool {
204+
return w.SendEverything ||
205+
(w.ChooseEvents && w.HookEvents.PullRequestAssign)
206+
}
207+
208+
// HasPullRequestLabelEvent returns true if hook enabled pull request label event.
209+
func (w *Webhook) HasPullRequestLabelEvent() bool {
210+
return w.SendEverything ||
211+
(w.ChooseEvents && w.HookEvents.PullRequestLabel)
212+
}
213+
214+
// HasPullRequestMilestoneEvent returns true if hook enabled pull request milestone event.
215+
func (w *Webhook) HasPullRequestMilestoneEvent() bool {
216+
return w.SendEverything ||
217+
(w.ChooseEvents && w.HookEvents.PullRequestMilestone)
218+
}
219+
220+
// HasPullRequestCommentEvent returns true if hook enabled pull_request_comment event.
221+
func (w *Webhook) HasPullRequestCommentEvent() bool {
222+
return w.SendEverything ||
223+
(w.ChooseEvents && w.HookEvents.PullRequestComment)
224+
}
225+
226+
// HasPullRequestApprovedEvent returns true if hook enabled pull request review event.
227+
func (w *Webhook) HasPullRequestApprovedEvent() bool {
228+
return w.SendEverything ||
229+
(w.ChooseEvents && w.HookEvents.PullRequestReview)
230+
}
231+
232+
// HasPullRequestRejectedEvent returns true if hook enabled pull request review event.
233+
func (w *Webhook) HasPullRequestRejectedEvent() bool {
234+
return w.SendEverything ||
235+
(w.ChooseEvents && w.HookEvents.PullRequestReview)
236+
}
237+
238+
// HasPullRequestReviewCommentEvent returns true if hook enabled pull request review event.
239+
func (w *Webhook) HasPullRequestReviewCommentEvent() bool {
240+
return w.SendEverything ||
241+
(w.ChooseEvents && w.HookEvents.PullRequestReview)
242+
}
243+
244+
// HasPullRequestSyncEvent returns true if hook enabled pull request sync event.
245+
func (w *Webhook) HasPullRequestSyncEvent() bool {
246+
return w.SendEverything ||
247+
(w.ChooseEvents && w.HookEvents.PullRequestSync)
248+
}
249+
175250
// HasReleaseEvent returns if hook enabled release event.
176251
func (w *Webhook) HasReleaseEvent() bool {
177252
return w.SendEverything ||
@@ -198,8 +273,19 @@ func (w *Webhook) EventCheckers() []struct {
198273
{w.HasForkEvent, HookEventFork},
199274
{w.HasPushEvent, HookEventPush},
200275
{w.HasIssuesEvent, HookEventIssues},
276+
{w.HasIssuesAssignEvent, HookEventIssueAssign},
277+
{w.HasIssuesLabelEvent, HookEventIssueLabel},
278+
{w.HasIssuesMilestoneEvent, HookEventIssueMilestone},
201279
{w.HasIssueCommentEvent, HookEventIssueComment},
202280
{w.HasPullRequestEvent, HookEventPullRequest},
281+
{w.HasPullRequestAssignEvent, HookEventPullRequestAssign},
282+
{w.HasPullRequestLabelEvent, HookEventPullRequestLabel},
283+
{w.HasPullRequestMilestoneEvent, HookEventPullRequestMilestone},
284+
{w.HasPullRequestCommentEvent, HookEventPullRequestComment},
285+
{w.HasPullRequestApprovedEvent, HookEventPullRequestReviewApproved},
286+
{w.HasPullRequestRejectedEvent, HookEventPullRequestReviewRejected},
287+
{w.HasPullRequestCommentEvent, HookEventPullRequestReviewComment},
288+
{w.HasPullRequestSyncEvent, HookEventPullRequestSync},
203289
{w.HasRepositoryEvent, HookEventRepository},
204290
{w.HasReleaseEvent, HookEventRelease},
205291
}
@@ -498,20 +584,60 @@ type HookEventType string
498584

499585
// Types of hook events
500586
const (
501-
HookEventCreate HookEventType = "create"
502-
HookEventDelete HookEventType = "delete"
503-
HookEventFork HookEventType = "fork"
504-
HookEventPush HookEventType = "push"
505-
HookEventIssues HookEventType = "issues"
506-
HookEventIssueComment HookEventType = "issue_comment"
507-
HookEventPullRequest HookEventType = "pull_request"
508-
HookEventRepository HookEventType = "repository"
509-
HookEventRelease HookEventType = "release"
510-
HookEventPullRequestApproved HookEventType = "pull_request_approved"
511-
HookEventPullRequestRejected HookEventType = "pull_request_rejected"
512-
HookEventPullRequestComment HookEventType = "pull_request_comment"
587+
HookEventCreate HookEventType = "create"
588+
HookEventDelete HookEventType = "delete"
589+
HookEventFork HookEventType = "fork"
590+
HookEventPush HookEventType = "push"
591+
HookEventIssues HookEventType = "issues"
592+
HookEventIssueAssign HookEventType = "issue_assign"
593+
HookEventIssueLabel HookEventType = "issue_label"
594+
HookEventIssueMilestone HookEventType = "issue_milestone"
595+
HookEventIssueComment HookEventType = "issue_comment"
596+
HookEventPullRequest HookEventType = "pull_request"
597+
HookEventPullRequestAssign HookEventType = "pull_request_assign"
598+
HookEventPullRequestLabel HookEventType = "pull_request_label"
599+
HookEventPullRequestMilestone HookEventType = "pull_request_milestone"
600+
HookEventPullRequestComment HookEventType = "pull_request_comment"
601+
HookEventPullRequestReviewApproved HookEventType = "pull_request_review_approved"
602+
HookEventPullRequestReviewRejected HookEventType = "pull_request_review_rejected"
603+
HookEventPullRequestReviewComment HookEventType = "pull_request_review_comment"
604+
HookEventPullRequestSync HookEventType = "pull_request_sync"
605+
HookEventRepository HookEventType = "repository"
606+
HookEventRelease HookEventType = "release"
513607
)
514608

609+
// Event returns the HookEventType as an event string
610+
func (h HookEventType) Event() string {
611+
switch h {
612+
case HookEventCreate:
613+
return "create"
614+
case HookEventDelete:
615+
return "delete"
616+
case HookEventFork:
617+
return "fork"
618+
case HookEventPush:
619+
return "push"
620+
case HookEventIssues, HookEventIssueAssign, HookEventIssueLabel, HookEventIssueMilestone:
621+
return "issues"
622+
case HookEventPullRequest, HookEventPullRequestAssign, HookEventPullRequestLabel, HookEventPullRequestMilestone,
623+
HookEventPullRequestSync:
624+
return "pull_request"
625+
case HookEventIssueComment, HookEventPullRequestComment:
626+
return "issue_comment"
627+
case HookEventPullRequestReviewApproved:
628+
return "pull_request_approved"
629+
case HookEventPullRequestReviewRejected:
630+
return "pull_request_rejected"
631+
case HookEventPullRequestReviewComment:
632+
return "pull_request_comment"
633+
case HookEventRepository:
634+
return "repository"
635+
case HookEventRelease:
636+
return "release"
637+
}
638+
return ""
639+
}
640+
515641
// HookRequest represents hook task request information.
516642
type HookRequest struct {
517643
Headers map[string]string `json:"headers"`

models/webhook_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,11 @@ func TestWebhook_UpdateEvent(t *testing.T) {
6161
}
6262

6363
func TestWebhook_EventsArray(t *testing.T) {
64-
assert.Equal(t, []string{"create", "delete", "fork", "push", "issues", "issue_comment", "pull_request", "repository", "release"},
64+
assert.Equal(t, []string{"create", "delete", "fork", "push",
65+
"issues", "issue_assign", "issue_label", "issue_milestone", "issue_comment",
66+
"pull_request", "pull_request_assign", "pull_request_label", "pull_request_milestone",
67+
"pull_request_comment", "pull_request_review_approved", "pull_request_review_rejected",
68+
"pull_request_review_comment", "pull_request_sync", "repository", "release"},
6569
(&Webhook{
6670
HookEvent: &HookEvent{SendEverything: true},
6771
}).EventsArray(),

modules/auth/repo_form.go

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -190,18 +190,27 @@ func (f *ProtectBranchForm) Validate(ctx *macaron.Context, errs binding.Errors)
190190

191191
// WebhookForm form for changing web hook
192192
type WebhookForm struct {
193-
Events string
194-
Create bool
195-
Delete bool
196-
Fork bool
197-
Issues bool
198-
IssueComment bool
199-
Release bool
200-
Push bool
201-
PullRequest bool
202-
Repository bool
203-
Active bool
204-
BranchFilter string `binding:"GlobPattern"`
193+
Events string
194+
Create bool
195+
Delete bool
196+
Fork bool
197+
Issues bool
198+
IssueAssign bool
199+
IssueLabel bool
200+
IssueMilestone bool
201+
IssueComment bool
202+
Release bool
203+
Push bool
204+
PullRequest bool
205+
PullRequestAssign bool
206+
PullRequestLabel bool
207+
PullRequestMilestone bool
208+
PullRequestComment bool
209+
PullRequestReview bool
210+
PullRequestSync bool
211+
Repository bool
212+
Active bool
213+
BranchFilter string `binding:"GlobPattern"`
205214
}
206215

207216
// PushOnly if the hook will be triggered when push

0 commit comments

Comments
 (0)