Skip to content

Commit 840869e

Browse files
authored
Extract payload manipulation (#33)
1 parent 21ebcfa commit 840869e

File tree

13 files changed

+266
-135
lines changed

13 files changed

+266
-135
lines changed

connectors/github/builder.go

Lines changed: 36 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -9,55 +9,43 @@ import (
99
"io"
1010
"io/ioutil"
1111
"net/http"
12-
"path/filepath"
13-
14-
"strings"
15-
16-
"errors"
1712

1813
"github.com/bivas/rivi/config/client"
1914
"github.com/bivas/rivi/types"
2015
"github.com/bivas/rivi/types/builder"
21-
"github.com/bivas/rivi/util"
2216
"github.com/bivas/rivi/util/log"
2317
)
2418

25-
var (
26-
supportedEventTypes = []string{
27-
"issue_comment",
28-
"pull_request",
29-
"pull_request_review",
30-
"pull_request_review_comment"}
31-
)
32-
3319
type builderContext struct {
3420
secret []byte
3521
client *ghClient
3622
data *data
3723
}
3824

3925
type dataBuilder struct {
40-
logger log.Logger
26+
handlers map[string]eventHandler
27+
defaultHandler eventHandler
28+
logger log.Logger
4129
}
4230

43-
func (builder *dataBuilder) validate(context *builderContext, payload []byte, request *http.Request) bool {
44-
if len(context.secret) == 0 {
31+
func validate(secret, payload []byte, request *http.Request) bool {
32+
if len(secret) == 0 {
4533
return true
4634
}
47-
h := hmac.New(sha1.New, context.secret)
35+
h := hmac.New(sha1.New, secret)
4836
h.Write(payload)
4937
result := fmt.Sprintf("sha1=%s", hex.EncodeToString(h.Sum(nil)))
5038
return request.Header.Get("X-Hub-Signature") == result
5139
}
5240

53-
func (builder *dataBuilder) readPayload(context *builderContext, r *http.Request) (*payload, []byte, error) {
41+
func ReadPayload(secret []byte, r *http.Request) (*payload, []byte, error) {
5442
body := r.Body
5543
defer body.Close()
5644
raw, err := ioutil.ReadAll(io.LimitReader(body, r.ContentLength))
5745
if err != nil {
5846
return nil, raw, err
5947
}
60-
if !builder.validate(context, raw, r) {
48+
if !validate(secret, raw, r) {
6149
return nil, raw, fmt.Errorf("Payload could not be validated")
6250
}
6351
var pr payload
@@ -67,120 +55,41 @@ func (builder *dataBuilder) readPayload(context *builderContext, r *http.Request
6755
return &pr, raw, nil
6856
}
6957

70-
func (builder *dataBuilder) readFromJson(context *builderContext, payload *payload) {
71-
pr := payload.PullRequest
72-
if pr.Number > 0 {
73-
context.data.number = pr.Number
74-
} else {
75-
context.data.number = payload.Number
76-
}
77-
context.data.title = pr.Title
78-
context.data.description = pr.Body
79-
context.data.changedFiles = pr.ChangedFiles
80-
context.data.additions = pr.Additions
81-
context.data.deletions = pr.Deletions
82-
context.data.ref = pr.Base.Ref
83-
head := pr.Head
84-
context.data.origin = types.Origin{
85-
User: strings.ToLower(head.User.Login),
86-
Repo: head.Repo.Name,
87-
Ref: head.Ref,
88-
Head: head.Sha[0:6],
89-
GitURL: head.Repo.GitURL,
90-
}
91-
context.data.state = pr.State
92-
}
93-
94-
func (builder *dataBuilder) readFromClient(context *builderContext) {
95-
id := context.data.number
96-
context.data.assignees = context.client.GetAssignees(id)
97-
context.data.state = context.client.GetState(id)
98-
context.data.labels = context.client.GetLabels(id)
99-
context.data.comments = context.client.GetComments(id)
100-
fileNames := context.client.GetFileNames(id)
101-
context.data.fileNames = fileNames
102-
stringSet := util.StringSet{Transformer: filepath.Ext}
103-
context.data.fileExt = stringSet.AddAll(fileNames).Values()
104-
context.data.reviewers = context.client.GetReviewers(id)
105-
context.data.locked = context.client.Locked(id)
106-
}
107-
108-
func (builder *dataBuilder) checkProcessState(context *builderContext) bool {
109-
builder.logger.DebugWith(log.MetaFields{log.F("issue", context.data.GetShortName())},
110-
"Current state is '%s'", context.data.state)
111-
return context.data.state != "closed"
58+
func (builder *dataBuilder) findEventHandler(githubEvent string) eventHandler {
59+
handler, ok := builder.handlers[githubEvent]
60+
if !ok {
61+
builder.logger.DebugWith(log.MetaFields{
62+
log.F("eventType", githubEvent),
63+
}, "Using default event handler")
64+
handler = builder.defaultHandler
65+
}
66+
return handler
11267
}
11368

11469
func (builder *dataBuilder) BuildFromHook(config client.ClientConfig, r *http.Request) (types.HookData, bool, error) {
11570
githubEvent := r.Header.Get("X-Github-Event")
116-
if githubEvent == "ping" {
117-
builder.logger.Info("Got GitHub 'ping' event")
118-
return nil, false, nil
119-
}
120-
supportedEvent := false
121-
for _, event := range supportedEventTypes {
122-
if event == githubEvent {
123-
supportedEvent = true
124-
}
125-
}
126-
if !supportedEvent {
127-
builder.logger.Debug("Got GitHub '%s' event", githubEvent)
128-
return nil, false, nil
129-
}
130-
context := &builderContext{secret: []byte(config.GetSecret())}
131-
pl, raw, err := builder.readPayload(context, r)
132-
if err != nil {
133-
return nil, false, err
134-
}
135-
if pl.Number == 0 && pl.PullRequest.Number == 0 {
136-
builder.logger.Warning("Payload appear to have issue id 0")
137-
builder.logger.Debug("Faulty payload %+v", pl)
138-
return nil, false, fmt.Errorf("Payload appear to have issue id 0")
139-
}
140-
repo := pl.Repository.Name
141-
owner := pl.Repository.Owner.Login
142-
installation := pl.Installation.ID
143-
if installation > 0 {
144-
context.client = newAppClient(config, owner, repo, installation)
145-
} else {
146-
context.client = newClient(config, owner, repo)
147-
}
148-
if context.client == nil {
149-
return nil, false, errors.New("Unable to initialize github client")
150-
}
151-
context.data = &data{owner: owner, repo: repo, payload: raw, client: context.client}
152-
builder.readFromJson(context, pl)
153-
return context.data, builder.checkProcessState(context), nil
71+
return builder.findEventHandler(githubEvent).FromRequest(config, r)
15472
}
15573

156-
func (builder *dataBuilder) BuildFromPayload(config client.ClientConfig, raw []byte) (types.Data, bool, error) {
157-
var pl payload
158-
if e := json.Unmarshal(raw, &pl); e != nil {
159-
return nil, false, e
160-
}
161-
repo := pl.Repository.Name
162-
owner := pl.Repository.Owner.Login
163-
installation := pl.Installation.ID
164-
context := &builderContext{}
165-
if installation > 0 {
166-
context.client = newAppClient(config, owner, repo, installation)
167-
} else {
168-
context.client = newClient(config, owner, repo)
169-
}
170-
if context.client == nil {
171-
return nil, false, errors.New("Unable to initialize github client")
172-
}
173-
context.data = &data{owner: owner, repo: repo, payload: raw, client: context.client}
174-
builder.readFromJson(context, &pl)
175-
if context.data.GetNumber() == 0 {
176-
builder.logger.Warning("Payload appear to have issue id 0")
177-
builder.logger.Debug("Faulty payload %+v", pl)
178-
return nil, false, fmt.Errorf("Payload appear to have issue id 0")
179-
}
180-
builder.readFromClient(context)
181-
return context.data, builder.checkProcessState(context), nil
74+
func (builder *dataBuilder) BuildFromPayload(config client.ClientConfig, ofType string, raw []byte) (types.Data, bool, error) {
75+
return builder.findEventHandler(ofType).FromPayload(config, raw)
18276
}
18377

78+
var DataBuilder dataBuilder
79+
18480
func init() {
185-
builder.RegisterNewDataBuilder("github", &dataBuilder{logger: log.Get("GitHub.DataBuilder")})
81+
logger := log.Get("GitHub.DataBuilder")
82+
prHandler := &pullRequestEventHandler{
83+
logger: logger.Get("PullRequestHandler"),
84+
}
85+
DataBuilder = dataBuilder{
86+
logger: logger,
87+
handlers: map[string]eventHandler{
88+
"pull_request": prHandler,
89+
"pull_request_review": prHandler,
90+
"pull_request_review_comment": prHandler,
91+
},
92+
defaultHandler: defaultHandler,
93+
}
94+
builder.RegisterNewDataBuilder("github", &DataBuilder)
18695
}

connectors/github/builder_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package github
2+
3+
import (
4+
"net/http"
5+
"testing"
6+
7+
"github.com/bivas/rivi/config/client"
8+
"github.com/bivas/rivi/mocks"
9+
"github.com/bivas/rivi/types"
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
type mockEventHandler struct {
14+
FromRequestCalled bool
15+
FromPayloadCalled bool
16+
}
17+
18+
func (m *mockEventHandler) FromRequest(client.ClientConfig, *http.Request) (types.HookData, bool, error) {
19+
m.FromRequestCalled = true
20+
return nil, false, nil
21+
}
22+
23+
func (m *mockEventHandler) FromPayload(client.ClientConfig, []byte) (types.Data, bool, error) {
24+
m.FromPayloadCalled = true
25+
return nil, false, nil
26+
}
27+
28+
func TestRequestDefault(t *testing.T) {
29+
DataBuilder.handlers["mock"] = &mockEventHandler{}
30+
DataBuilder.defaultHandler = &mockEventHandler{}
31+
32+
request, err := http.NewRequest("GET", "http://example.com", nil)
33+
assert.NoError(t, err, "shouldn't error")
34+
_, cont, _ := DataBuilder.BuildFromHook(&mocks.MockClientConfig{}, request)
35+
assert.False(t, cont, "shouldn't continue")
36+
assert.True(t, DataBuilder.defaultHandler.(*mockEventHandler).FromRequestCalled)
37+
}
38+
39+
func TestRequest(t *testing.T) {
40+
DataBuilder.handlers["mock"] = &mockEventHandler{}
41+
DataBuilder.defaultHandler = &mockEventHandler{}
42+
43+
request, err := http.NewRequest("GET", "http://example.com", nil)
44+
assert.NoError(t, err, "shouldn't error")
45+
request.Header.Set("X-Github-Event", "mock")
46+
_, cont, _ := DataBuilder.BuildFromHook(&mocks.MockClientConfig{}, request)
47+
assert.False(t, cont, "shouldn't continue")
48+
assert.False(t, DataBuilder.defaultHandler.(*mockEventHandler).FromRequestCalled)
49+
assert.True(t, DataBuilder.handlers["mock"].(*mockEventHandler).FromRequestCalled)
50+
}

connectors/github/client.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ func (c *ghClient) Merge(issue int, mergeMethod string) {
266266
}
267267
}
268268

269-
func newClient(config client.ClientConfig, owner, repo string) *ghClient {
269+
func NewClient(config client.ClientConfig, owner, repo string) *ghClient {
270270
logger := log.Get("Github.Client")
271271
if config.GetOAuthToken() == "" {
272272
logger.ErrorWith(
@@ -291,7 +291,7 @@ func newClient(config client.ClientConfig, owner, repo string) *ghClient {
291291
}
292292
}
293293

294-
func newAppClient(config client.ClientConfig, owner, repo string, installation int) *ghClient {
294+
func NewAppClient(config client.ClientConfig, owner, repo string, installation int) *ghClient {
295295
logger := log.Get("Github.Client")
296296
if config.GetApplicationID() == 0 || config.GetPrivateKeyFile() == "" {
297297
logger.ErrorWith(

connectors/github/data.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ type data struct {
2727
assignees []string
2828
comments []types.Comment
2929
payload []byte
30+
eventType string
3031
reviewers map[string]string
3132
collaborators []string
3233
repoLabels []string
@@ -116,6 +117,10 @@ func (d *data) GetRawPayload() []byte {
116117
return d.payload
117118
}
118119

120+
func (d *data) GetRawType() string {
121+
return d.eventType
122+
}
123+
119124
func (d *data) Merge(mergeMethod string) {
120125
d.client.Merge(d.number, mergeMethod)
121126
}

connectors/github/handler.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package github
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/bivas/rivi/config/client"
7+
"github.com/bivas/rivi/types"
8+
"github.com/bivas/rivi/util/log"
9+
)
10+
11+
type eventHandler interface {
12+
FromRequest(client.ClientConfig, *http.Request) (types.HookData, bool, error)
13+
FromPayload(client.ClientConfig, []byte) (types.Data, bool, error)
14+
}
15+
16+
type defaultEventHandler struct {
17+
logger log.Logger
18+
}
19+
20+
func (h *defaultEventHandler) FromRequest(config client.ClientConfig, r *http.Request) (types.HookData, bool, error) {
21+
githubEvent := r.Header.Get("X-Github-Event")
22+
h.logger.Info("Got GitHub '%s' event", githubEvent)
23+
return nil, false, nil
24+
}
25+
26+
func (h *defaultEventHandler) FromPayload(client.ClientConfig, []byte) (types.Data, bool, error) {
27+
h.logger.Warning("Calling 'FromPayload' of default handler")
28+
return nil, false, nil
29+
}
30+
31+
var defaultHandler = &defaultEventHandler{log.Get("GitHub.DataBuilder.DefaultHandler")}

connectors/github/json.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ type repositorySection struct {
3535
Owner struct {
3636
Login string `json:"login"`
3737
} `json:"owner"`
38+
Private bool `json:"private"`
3839
}
3940

4041
type payload struct {

0 commit comments

Comments
 (0)