-
Notifications
You must be signed in to change notification settings - Fork 115
/
commentpruner.go
109 lines (94 loc) · 3.42 KB
/
commentpruner.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package commentpruner facilitates efficiently deleting bot comments as a reaction to webhook events.
package commentpruner
import (
"sync"
"github.com/jenkins-x/go-scm/scm"
"github.com/sirupsen/logrus"
)
type scmProviderClient interface {
BotName() (string, error)
ListIssueComments(org, repo string, number int) ([]*scm.Comment, error)
ListPullRequestComments(org, repo string, number int) ([]*scm.Comment, error)
DeleteComment(org, repo string, number, id int, pr bool) error
}
// EventClient is a struct that provides bot comment deletion for an event related to an issue.
// A single client instance should be created for each event and shared by all consumers of the event.
// The client fetches the comments only once and filters that list repeatedly to find bot comments to
// delete. This avoids using lots of API tokens when fetching comments for each handler that wants
// to delete comments. (An HTTP cache only partially helps with this because deletions modify the
// list of comments so the next call requires GH to send the resource again.)
type EventClient struct {
org string
repo string
number int
spc scmProviderClient
log *logrus.Entry
once sync.Once
lock sync.Mutex
comments []*scm.Comment
}
// NewEventClient creates an EventClient struct. This should be used once per webhook event.
func NewEventClient(spc scmProviderClient, log *logrus.Entry, org, repo string, number int) *EventClient {
return &EventClient{
org: org,
repo: repo,
number: number,
spc: spc,
log: log,
}
}
// PruneComments fetches issue comments if they have not yet been fetched for this webhook event
// and then deletes any bot comments indicated by the func 'shouldPrune'.
func (c *EventClient) PruneComments(pr bool, shouldPrune func(*scm.Comment) bool) {
c.once.Do(func() {
botName, err := c.spc.BotName()
if err != nil {
c.log.WithError(err).Error("failed to get the bot's name. Pruning will consider all comments.")
}
var comments []*scm.Comment
if pr {
comments, err = c.spc.ListPullRequestComments(c.org, c.repo, c.number)
} else {
comments, err = c.spc.ListIssueComments(c.org, c.repo, c.number)
}
if err != nil {
c.log.WithError(err).Errorf("failed to list comments for %s/%s#%d", c.org, c.repo, c.number)
}
if botName != "" {
for _, comment := range comments {
if comment.Author.Login == botName {
c.comments = append(c.comments, comment)
}
}
}
})
c.lock.Lock()
defer c.lock.Unlock()
var remaining []*scm.Comment
for _, comment := range c.comments {
removed := false
if shouldPrune(comment) {
if err := c.spc.DeleteComment(c.org, c.repo, c.number, comment.ID, pr); err != nil {
c.log.WithError(err).Errorf("failed to delete stale comment with ID '%d'", comment.ID)
} else {
removed = true
}
}
if !removed {
remaining = append(remaining, comment)
}
}
c.comments = remaining
}