/
logic.go
142 lines (128 loc) · 3.63 KB
/
logic.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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package logic
import (
"context"
"fmt"
"strings"
"log"
"github.com/google/go-github/github"
"github.com/nlopes/slack"
"github.com/svera/snowden/config"
)
// Logic is a struct that contains all the code and properties needed to send a message to subscribers of files and folders
// of a specific repository.
type Logic struct {
sender senderInterface
gh githubInterface
cfg *config.Config
}
// New returns a new instance of Logic.
func New(sender senderInterface, gh githubInterface, cfg *config.Config) *Logic {
return &Logic{
sender: sender,
gh: gh,
cfg: cfg,
}
}
// Process will, given an owner of a repository, its name and a PR number, alert subscriber about a new PR being opened if
// it affects any of their watched files or folders.
func (l *Logic) Process(action string, owner string, repo string, number int, title string, description string) error {
var err error
var author *string
var subs []string
if _, ok := l.cfg.Watched[repo]; !ok {
if l.cfg.Debug == true {
log.Printf("%s repository is not being watched.\n", repo)
}
return nil
}
if action != "opened" && action != "reopened" {
if l.cfg.Debug {
log.Printf("PR %d has not been opened or reopened.\n", number)
}
return nil
}
if author, err = l.getPullRequestAuthor(owner, repo, number); err != nil {
return err
}
if files, _, err := l.gh.ListFiles(context.Background(), owner, repo, number, &github.ListOptions{}); err == nil {
for _, file := range files {
l.appendSubscribers(&subs, file.Filename, l.cfg.Watched[repo], author)
}
if len(subs) == 0 {
if l.cfg.Debug {
log.Printf("No one is subscribed to files in PR %d \n", number)
}
}
if err = l.notify(subs, owner, repo, number, title, description, author); err != nil {
return err
}
} else {
if l.cfg.Debug {
log.Printf("Error when trying to list Github repository files: %s", err.Error())
}
}
return err
}
func (l *Logic) getPullRequestAuthor(owner string, repo string, number int) (*string, error) {
if pr, _, err := l.gh.Get(context.Background(), owner, repo, number); err == nil {
return pr.User.Login, nil
} else {
if l.cfg.Debug {
log.Printf("Error when trying to get PR info: %s", err.Error())
}
return nil, err
}
}
func (l *Logic) appendSubscribers(subs *[]string, fileName *string, rules []config.Rule, author *string) {
for _, rule := range rules {
for _, exception := range rule.Exceptions {
if exception == *author {
return
}
}
for _, name := range rule.Names {
if strings.HasPrefix(*fileName, name) {
for _, new := range rule.Subscribers {
l.appendIfNotIn(subs, new)
}
}
}
}
}
func (l *Logic) appendIfNotIn(subs *[]string, subscriber string) {
for _, v := range *subs {
if v == subscriber {
return
}
}
*subs = append(*subs, subscriber)
}
func (l *Logic) notify(subs []string, owner string, repo string, number int, title string, description string, author *string) error {
url := fmt.Sprintf("https://github.com/%s/%s/pull/%d", owner, repo, number)
for _, subscriber := range subs {
params := slack.PostMessageParameters{
Markdown: true,
}
attachment := slack.Attachment{
Title: title,
TitleLink: url,
Text: description,
}
params.Attachments = []slack.Attachment{attachment}
_, _, err := l.sender.PostMessage(
subscriber,
fmt.Sprintf("Psssst... *%s* has opened a PR that affects files watched by you!", *author),
params,
)
if err != nil {
if l.cfg.Debug {
log.Printf("Error notifying to subscriber %s: %s.\n", err.Error(), subscriber)
}
} else {
if l.cfg.Debug {
log.Printf("Notification sent to %s.\n", subscriber)
}
}
}
return nil
}