Skip to content

Commit

Permalink
Add github handling
Browse files Browse the repository at this point in the history
  • Loading branch information
pcarranza committed Sep 23, 2018
1 parent 62351c0 commit 3bb6229
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 52 deletions.
2 changes: 1 addition & 1 deletion core/propaganda.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type Announcement interface {
// Parser provides an interface that allows to identify if a request can be
// parsed, and then will extract the announcement if there is any.
type Parser interface {
Match(map[string][]string) bool
MatchHeaders(map[string][]string) bool
Parse([]byte) (Announcement, error)
}

Expand Down
4 changes: 2 additions & 2 deletions github/fixtures/github-pr-close-no-merge.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"number": 2,
"state": "closed",
"locked": false,
"title": "Second test",
"title": "[announce] Second test",
"user": {
"login": "pcarranza",
"id": 4496338,
Expand All @@ -33,7 +33,7 @@
"type": "User",
"site_admin": false
},
"body": "",
"body": "some other payload message that comes from the description",
"created_at": "2018-09-18T22:02:53Z",
"updated_at": "2018-09-18T22:03:05Z",
"closed_at": "2018-09-18T22:03:05Z",
Expand Down
4 changes: 2 additions & 2 deletions github/fixtures/github-pr-create.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"number": 1,
"state": "open",
"locked": false,
"title": "Just write something",
"title": "[announce] Just write something",
"user": {
"login": "pcarranza",
"id": 4496338,
Expand All @@ -33,7 +33,7 @@
"type": "User",
"site_admin": false
},
"body": "",
"body": "some payload message that comes from the description",
"created_at": "2018-09-18T21:59:43Z",
"updated_at": "2018-09-18T21:59:43Z",
"closed_at": null,
Expand Down
4 changes: 2 additions & 2 deletions github/fixtures/github-pr-merged.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"number": 1,
"state": "closed",
"locked": false,
"title": "Just write something",
"title": "[announce] Just write something",
"user": {
"login": "pcarranza",
"id": 4496338,
Expand All @@ -33,7 +33,7 @@
"type": "User",
"site_admin": false
},
"body": "",
"body": "some payload message that comes from the description",
"created_at": "2018-09-18T21:59:43Z",
"updated_at": "2018-09-18T22:00:59Z",
"closed_at": "2018-09-18T22:00:59Z",
Expand Down
122 changes: 91 additions & 31 deletions github/github.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
package webhook
package github

import (
"encoding/json"
"fmt"
"github.com/sirupsen/logrus"
"gitlab.com/yakshaving.art/propaganda/core"
"regexp"
"strings"
)

// Headers
// "X-Hub-Signature":[]string{"sha1=be490c94029284a1074f6ed7d6f551affcfa6e8b"},
Expand All @@ -10,33 +19,84 @@ package webhook
// "Accept":[]string{"*/*"},
// "Content-Type":[]string{"application/json"}

// // Repository holds the repository information
// type Repository struct {
// URL string `json:"url"`
// FullName string `json:"full_name"`
// }

// // Hook holds the hook events
// type Hook struct {
// Events []string `json:"events"`
// }

// // HookPayload hold the GitHub
// type HookPayload struct {
// Repository Repository `json:"repository"`
// Hook Hook `json:"hook"`
// }

// // GetRepository implements webhook.HookPayload interface
// func (h HookPayload) GetRepository() string {
// return h.Repository.FullName
// }

// // ParseHookPayload parses a payload string and returns the payload as a struct
// func (c Client) ParseHookPayload(payload string) (webhooks.HookPayload, error) {
// var hookPayload HookPayload
// if err := json.Unmarshal([]byte(payload), &hookPayload); err != nil {
// return hookPayload, fmt.Errorf("could not parse hook payload: %s", err)
// }
// return hookPayload, nil
// }
// Parser implements the core.Parser type for GitHub Pull Request
type Parser struct {
matcher *regexp.Regexp
}

// NewParser creates a new parser using the pattern provided
func NewParser(pattern string) Parser {
re, err := regexp.Compile(pattern)
if err != nil {
logrus.Fatalf("could not compile regexp pattern for announcements: %s", err)
}

return Parser{
matcher: re,
}
}

// MatchHeaders indicates that the headers match with the kind of request
func (Parser) MatchHeaders(headers map[string][]string) bool {
if event, ok := headers["X-Github-Event"]; ok {
if len(event) != 1 {
return false
}
return event[0] == "pull_request"
}
return false
}

// Parse parses a payload and returns a a valid one if everything is in place for it to be announced
func (p Parser) Parse(payload []byte) (core.Announcement, error) {
var pl Payload
if err := json.Unmarshal(payload, &pl); err != nil {
return pl, fmt.Errorf("could not parse json payload: %s", err)
}

if !p.matcher.MatchString(pl.PullRequest.Title) {
return pl, fmt.Errorf("MR title '%s' is not annouceable", pl.PullRequest.Title)
}

pl.PullRequest.Title = strings.TrimSpace(p.matcher.ReplaceAllString(pl.PullRequest.Title, ""))

return pl, nil
}

// Payload wraps a Github pull request
type Payload struct {
PullRequest PullRequest `json:"pull_request"`
Repository Repository `json:"repository"`
}

// PullRequest implements a pull request payload object
type PullRequest struct {
URL string `json:"html_url"`
State string `json:"state"`
Title string `json:"title"`
Merged bool `json:"merged"`
Body string `json:"body"`
}

// ProjectName implements Annoucement
func (pl Payload) ProjectName() string {
return pl.Repository.FullName
}

// ShouldAnnounce implements Annoucement
func (pl Payload) ShouldAnnounce() bool {
return pl.PullRequest.Merged && pl.PullRequest.State == "closed"
}

// Text implements Annoucement
func (pl Payload) Text() string {
return fmt.Sprintf("*%s*\n\n%s\n\n*URL:* %s",
pl.PullRequest.Title,
pl.PullRequest.Body,
pl.PullRequest.URL)
}

// Repository holds the repository information
type Repository struct {
FullName string `json:"full_name"`
}
101 changes: 101 additions & 0 deletions github/github_test.go
Original file line number Diff line number Diff line change
@@ -1 +1,102 @@
package github_test

import (
"io/ioutil"
"testing"

"github.com/stretchr/testify/assert"
"gitlab.com/yakshaving.art/propaganda/github"
)

func TestParsingPayloads(t *testing.T) {
parser := github.NewParser("\\[announce\\]")
tt := []struct {
name string
jsonFilename string
expected github.Payload
shouldAnnounce bool
}{
{
"MR Create",
"fixtures/github-pr-create.json",
github.Payload{
PullRequest: github.PullRequest{
State: "open",
Title: "Just write something",
URL: "https://github.com/pcarranza/testing-prs/pull/1",
Body: "some payload message that comes from the description",
Merged: false,
},
Repository: github.Repository{
FullName: "pcarranza/testing-prs",
},
},
false,
},
{
"MR Merged",
"fixtures/github-pr-merged.json",
github.Payload{
PullRequest: github.PullRequest{
State: "closed",
Title: "Just write something",
URL: "https://github.com/pcarranza/testing-prs/pull/1",
Body: "some payload message that comes from the description",
Merged: true,
},
Repository: github.Repository{
FullName: "pcarranza/testing-prs",
},
},
true,
},
{
"MR Closed without a merge",
"fixtures/github-pr-close-no-merge.json",
github.Payload{
PullRequest: github.PullRequest{
State: "closed",
Title: "Second test",
URL: "https://github.com/pcarranza/testing-prs/pull/2",
Body: "some other payload message that comes from the description",
Merged: false,
},
Repository: github.Repository{
FullName: "pcarranza/testing-prs",
},
},
false,
},
}

for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
a := assert.New(t)
b, err := ioutil.ReadFile(tc.jsonFilename)
a.Nilf(err, "could not read fixture file %s", tc.jsonFilename)
a.NotNilf(b, "content should not be nil")

mr, err := parser.Parse(b)
a.NoErrorf(err, "could not unmarshal PR json")

a.EqualValuesf(tc.expected, mr, "parsed merge request is not as expected")

a.Equal(tc.expected.Text(), mr.Text())
a.Equal(tc.expected.ProjectName(), mr.ProjectName())
a.Equal(tc.shouldAnnounce, mr.ShouldAnnounce())
})
}
}

// func TestInvalidPayloadErrs(t *testing.T) {
// a := assert.New(t)

// b, err := ioutil.ReadFile("fixtures/gitlab-push-event.json")
// a.Nil(err, "could not read fixture file")
// a.NotNilf(b, "content should not be nil")

// parser := gitlab.Parser{}
// mr, err := parser.Parse(b)
// a.Errorf(err, "json payload is not a merge request but a push")
// a.Equalf(gitlab.MergeRequest{}, mr, "merge request should be empty")
// }
30 changes: 24 additions & 6 deletions gitlab/gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,35 @@ package gitlab

import (
"fmt"
"regexp"
"strings"

"encoding/json"

"gitlab.com/yakshaving.art/propaganda/core"

"github.com/sirupsen/logrus"
)

// Parser implements the core.Parser type for GitLab merge webhooks
type Parser struct {
MatchString string
matcher *regexp.Regexp
}

// Match indicates that the headers match with the kind of request
func (Parser) Match(headers map[string][]string) bool {
// NewParser creates a new parser using the pattern provided
func NewParser(pattern string) Parser {
re, err := regexp.Compile(pattern)
if err != nil {
logrus.Fatalf("could not compile regexp pattern for announcements: %s", err)
}

return Parser{
matcher: re,
}
}

// MatchHeaders indicates that the headers match with the kind of request
func (Parser) MatchHeaders(headers map[string][]string) bool {
if event, ok := headers["X-Gitlab-Event"]; ok {
if len(event) != 1 {
return false
Expand All @@ -35,7 +50,7 @@ func (Parser) Match(headers map[string][]string) bool {
return false
}

// Parse creates a new merge request object from the passed payload
// Parse parses a payload and returns a a valid one if everything is in place for it to be announced
func (p Parser) Parse(payload []byte) (core.Announcement, error) {
var mr MergeRequest
if err := json.Unmarshal(payload, &mr); err != nil {
Expand All @@ -44,10 +59,13 @@ func (p Parser) Parse(payload []byte) (core.Announcement, error) {
if mr.Kind != "merge_request" {
return MergeRequest{}, fmt.Errorf("json payload is not a merge request but a %s", mr.Kind)
}
if !strings.HasPrefix(mr.Attributes.Title, p.MatchString) {

if !p.matcher.MatchString(mr.Attributes.Title) {
return MergeRequest{}, fmt.Errorf("MR title '%s' is not annouceable", mr.Attributes.Title)
}
mr.Attributes.Title = strings.TrimSpace(mr.Attributes.Title[len(p.MatchString):])

mr.Attributes.Title = strings.TrimSpace(p.matcher.ReplaceAllString(mr.Attributes.Title, ""))

return mr, nil
}

Expand Down
4 changes: 1 addition & 3 deletions gitlab/gitlab_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ import (
)

func TestParsingPayloads(t *testing.T) {
parser := gitlab.Parser{
MatchString: "[announce]",
}
parser := gitlab.NewParser("\\[announce\\]")
tt := []struct {
name string
jsonFilename string
Expand Down
Loading

0 comments on commit 3bb6229

Please sign in to comment.