Browse Source

Further github listener work

tags/v0.3.0
Russ Garrett 5 years ago
parent
commit
fb90d9e4b3
No account linked to committer's email address

+ 41
- 83
httplistener/github.go View File

@@ -2,91 +2,18 @@ package httplistener
2 2
 
3 3
 import (
4 4
 	"fmt"
5
-	"github.com/irccloud/irccat/util"
6 5
 	"github.com/spf13/viper"
7 6
 	"gopkg.in/go-playground/webhooks.v5/github"
8 7
 	"net/http"
9 8
 	"strings"
10 9
 )
11 10
 
12
-func formatRef(ref string) string {
13
-	parts := strings.Split(ref, "/")
14
-	res := ""
15
-	if parts[1] == "heads" {
16
-		res = "branch "
17
-	} else if parts[1] == "tags" {
18
-		res = "tag "
11
+func interestingIssueAction(action string) bool {
12
+	switch action {
13
+	case "opened", "closed", "reopened":
14
+		return true
19 15
 	}
20
-	return res + parts[2]
21
-}
22
-
23
-func handleRelease(payload github.ReleasePayload) ([]string, string, bool) {
24
-	if payload.Action != "published" {
25
-		return []string{""}, "", false
26
-	}
27
-
28
-	var release string
29
-	if payload.Release.Name != nil {
30
-		release = *payload.Release.Name
31
-	}
32
-	if release == "" {
33
-		release = payload.Release.TagName
34
-	}
35
-
36
-	msg := fmt.Sprintf("[\x02%s\x0f] release \x02%s\x0f has been published by %s: %s",
37
-		payload.Repository.Name, release, payload.Release.Author.Login, payload.Release.HTMLURL)
38
-	return []string{msg}, payload.Repository.Name, true
39
-}
40
-
41
-func handlePush(payload github.PushPayload) ([]string, string, bool) {
42
-	var msgs []string
43
-
44
-	msgs = append(msgs, fmt.Sprintf("[\x02%s\x0f] %s pushed %d new commits to %s: %s",
45
-		payload.Repository.Name, payload.Sender.Login, len(payload.Commits),
46
-		formatRef(payload.Ref), payload.Compare))
47
-
48
-	commits_shown := 0
49
-	for _, commit := range payload.Commits {
50
-		if commits_shown == 3 {
51
-			break
52
-		}
53
-		if !commit.Distinct {
54
-			continue
55
-		}
56
-		msgs = append(msgs, fmt.Sprintf("\t%s: %s", commit.Author.Username, commit.Message))
57
-		commits_shown++
58
-	}
59
-	return msgs, payload.Repository.Name, true
60
-}
61
-
62
-func handleIssue(payload github.IssuesPayload) ([]string, string, bool) {
63
-	msg := fmt.Sprintf("[\x02%s\x0f] %s ", payload.Repository.Name, payload.Sender.Login)
64
-	issue_id := fmt.Sprintf("issue #%d", payload.Issue.Number)
65
-
66
-	show := true
67
-	switch payload.Action {
68
-	case "opened":
69
-		msg = msg + "opened " + issue_id
70
-	case "closed":
71
-		msg = msg + "closed " + issue_id
72
-	default:
73
-		// Don't know what to do with this, so don't show it
74
-		show = false
75
-	}
76
-
77
-	msg = msg + fmt.Sprintf(": %s %s", payload.Issue.Title, payload.Issue.HTMLURL)
78
-	return []string{msg}, payload.Repository.Name, show
79
-}
80
-
81
-func handleIssueComment(payload github.IssueCommentPayload) ([]string, string, bool) {
82
-	if payload.Action != "created" {
83
-		return []string{}, payload.Repository.Name, false
84
-	}
85
-
86
-	msg := fmt.Sprintf("[\x02%s\x0f] %s commented on issue %d: %s",
87
-		payload.Repository.Name, payload.Comment.User.Login,
88
-		payload.Issue.Number, util.Truncate(payload.Comment.Body, 150))
89
-	return []string{msg}, payload.Repository.Name, true
16
+	return false
90 17
 }
91 18
 
92 19
 func (hl *HTTPListener) githubHandler(w http.ResponseWriter, request *http.Request) {
@@ -101,7 +28,8 @@ func (hl *HTTPListener) githubHandler(w http.ResponseWriter, request *http.Reque
101 28
 
102 29
 	// All valid events we want to receive need to be listed here.
103 30
 	payload, err := hook.Parse(request,
104
-		github.ReleaseEvent, github.PushEvent, github.IssuesEvent, github.IssueCommentEvent)
31
+		github.ReleaseEvent, github.PushEvent, github.IssuesEvent, github.IssueCommentEvent,
32
+		github.PullRequestEvent)
105 33
 
106 34
 	if err != nil {
107 35
 		log.Errorf("Error parsing github webhook: %s", err)
@@ -114,13 +42,43 @@ func (hl *HTTPListener) githubHandler(w http.ResponseWriter, request *http.Reque
114 42
 
115 43
 	switch payload.(type) {
116 44
 	case github.ReleasePayload:
117
-		msgs, repo, send = handleRelease(payload.(github.ReleasePayload))
45
+		pl := payload.(github.ReleasePayload)
46
+		if pl.Action == "published" {
47
+			send = true
48
+			msgs, err = hl.renderTemplate("github.release", payload)
49
+			repo = pl.Repository.Name
50
+		}
118 51
 	case github.PushPayload:
119
-		msgs, repo, send = handlePush(payload.(github.PushPayload))
52
+		pl := payload.(github.PushPayload)
53
+		send = true
54
+		msgs, err = hl.renderTemplate("github.push", payload)
55
+		repo = pl.Repository.Name
120 56
 	case github.IssuesPayload:
121
-		msgs, repo, send = handleIssue(payload.(github.IssuesPayload))
57
+		pl := payload.(github.IssuesPayload)
58
+		if interestingIssueAction(pl.Action) {
59
+			send = true
60
+			msgs, err = hl.renderTemplate("github.issue", payload)
61
+			repo = pl.Repository.Name
62
+		}
122 63
 	case github.IssueCommentPayload:
123
-		msgs, repo, send = handleIssueComment(payload.(github.IssueCommentPayload))
64
+		pl := payload.(github.IssueCommentPayload)
65
+		if pl.Action == "created" {
66
+			send = true
67
+			msgs, err = hl.renderTemplate("github.issuecomment", payload)
68
+			repo = pl.Repository.Name
69
+		}
70
+	case github.PullRequestPayload:
71
+		pl := payload.(github.PullRequestPayload)
72
+		if interestingIssueAction(pl.Action) {
73
+			send = true
74
+			msgs, err = hl.renderTemplate("github.pullrequest", payload)
75
+			repo = pl.Repository.Name
76
+		}
77
+	}
78
+
79
+	if err != nil {
80
+		log.Errorf("Error rendering GitHub event template: %s", err)
81
+		return
124 82
 	}
125 83
 
126 84
 	if send {

+ 3
- 0
httplistener/httplistener.go View File

@@ -5,6 +5,7 @@ import (
5 5
 	"github.com/juju/loggo"
6 6
 	"github.com/spf13/viper"
7 7
 	"net/http"
8
+	"text/template"
8 9
 )
9 10
 
10 11
 var log = loggo.GetLogger("HTTPListener")
@@ -12,12 +13,14 @@ var log = loggo.GetLogger("HTTPListener")
12 13
 type HTTPListener struct {
13 14
 	http http.Server
14 15
 	irc  *irc.Connection
16
+	tpls *template.Template
15 17
 }
16 18
 
17 19
 func New(irc *irc.Connection) (*HTTPListener, error) {
18 20
 	hl := HTTPListener{}
19 21
 	hl.irc = irc
20 22
 	hl.http = http.Server{Addr: viper.GetString("http.listen")}
23
+	hl.tpls = parseTemplates()
21 24
 
22 25
 	mux := http.NewServeMux()
23 26
 

+ 109
- 0
httplistener/templates.go View File

@@ -0,0 +1,109 @@
1
+package httplistener
2
+
3
+import (
4
+	"bytes"
5
+	"errors"
6
+	"github.com/irccloud/irccat/util"
7
+	"gopkg.in/go-playground/webhooks.v5/github"
8
+	"strings"
9
+	"text/template"
10
+)
11
+
12
+var defaultTemplates = map[string]string{
13
+	"github.release": "[{{b .Repository.Name}}] release {{h .Release.TagName}} has been published by {{g .Release.Author.Login}}: {{.Release.HTMLURL}}",
14
+	"github.push": `[{{b .Repository.Name}}] {{g .Sender.Login}} pushed {{.Commits|len}} commits to {{.Ref|refType}} {{.Ref|refName|h}}: {{.Compare}}
15
+{{range commitLimit . 3}}
16
+	{{g .Username}} ({{.Sha|truncateSha|h}}): {{.Message}}
17
+{{end}}`,
18
+	"github.issue":        "[{{b .Repository.Name}}] {{g .Sender.Login}} {{.Action}} issue #{{.Issue.Number}}: {{.Issue.Title}} {{.Issue.HTMLURL}}",
19
+	"github.issuecomment": "[{{b .Repository.Name}}] {{g .Comment.User.Login}} commented on issue #{{.Issue.Number}}: {{trunc .Comment.Body 150}} {{.Issue.HTMLURL}}",
20
+	"github.pullrequest":  "[{{b .Repository.Name}}] {{g .Sender.Login}} {{.Action}} pull request #{{.PullRequest.Number}} (\x0303{{.PullRequest.Base.Ref}}…{{.PullRequest.Head.Ref}}\x0f): {{.PullRequest.Title}} {{.PullRequest.HTMLURL}}",
21
+}
22
+
23
+func refName(ref string) string {
24
+	parts := strings.Split(ref, "/")
25
+	return parts[2]
26
+}
27
+
28
+func refType(ref string) string {
29
+	parts := strings.Split(ref, "/")
30
+	if parts[1] == "heads" {
31
+		return "branch"
32
+	} else if parts[1] == "tags" {
33
+		return "tag"
34
+	}
35
+	return ""
36
+}
37
+
38
+func truncateSha(sha string) string {
39
+	return sha[len(sha)-7:]
40
+}
41
+
42
+// Colour helper functions to try and declutter
43
+func boldFormat(text string) string {
44
+	return "\x02" + text + "\x0f"
45
+}
46
+
47
+func greyFormat(text string) string {
48
+	return "\x0314" + text + "\x0f"
49
+}
50
+
51
+func highlightFormat(text string) string {
52
+	return "\x0303" + text + "\x0f"
53
+}
54
+
55
+func parseTemplates() *template.Template {
56
+
57
+	funcMap := template.FuncMap{
58
+		"trunc":       util.Truncate,
59
+		"truncateSha": truncateSha,
60
+		"refType":     refType,
61
+		"refName":     refName,
62
+		"commitLimit": commitLimit,
63
+		"b":           boldFormat,
64
+		"g":           greyFormat,
65
+		"h":           highlightFormat,
66
+	}
67
+
68
+	t := template.New("irccat")
69
+
70
+	for k, v := range defaultTemplates {
71
+		template.Must(t.New(k).Funcs(funcMap).Parse(v))
72
+	}
73
+
74
+	return t
75
+}
76
+
77
+func (hl *HTTPListener) renderTemplate(tpl_name string, data interface{}) ([]string, error) {
78
+	var out bytes.Buffer
79
+	t := hl.tpls.Lookup(tpl_name)
80
+	if t == nil {
81
+		return []string{}, errors.New("Nonexistent template")
82
+	}
83
+	t.Execute(&out, data)
84
+	return strings.Split(out.String(), "\n"), nil
85
+}
86
+
87
+// We need this additional struct and function because the GitHub webhook package represents
88
+// commits as anonymous inner structs and Go's type system is bad. Unless I'm missing something very obvious.
89
+type Commit struct {
90
+	Message  string
91
+	Username string
92
+	Sha      string
93
+}
94
+
95
+func commitLimit(pl github.PushPayload, length int) []Commit {
96
+	res := make([]Commit, 0)
97
+	i := 0
98
+	for _, c := range pl.Commits {
99
+		if !c.Distinct {
100
+			continue
101
+		}
102
+		res = append(res, Commit{Message: c.Message, Username: c.Author.Username, Sha: c.Sha})
103
+		i += 1
104
+		if i == length {
105
+			break
106
+		}
107
+	}
108
+	return res
109
+}

+ 450
- 0
webhook_test_data/github/pull_request.json View File

@@ -0,0 +1,450 @@
1
+{
2
+  "action": "closed",
3
+  "number": 1,
4
+  "pull_request": {
5
+    "url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/1",
6
+    "id": 191568743,
7
+    "node_id": "MDExOlB1bGxSZXF1ZXN0MTkxNTY4NzQz",
8
+    "html_url": "https://github.com/Codertocat/Hello-World/pull/1",
9
+    "diff_url": "https://github.com/Codertocat/Hello-World/pull/1.diff",
10
+    "patch_url": "https://github.com/Codertocat/Hello-World/pull/1.patch",
11
+    "issue_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/1",
12
+    "number": 1,
13
+    "state": "closed",
14
+    "locked": false,
15
+    "title": "Update the README with new information",
16
+    "user": {
17
+      "login": "Codertocat",
18
+      "id": 21031067,
19
+      "node_id": "MDQ6VXNlcjIxMDMxMDY3",
20
+      "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
21
+      "gravatar_id": "",
22
+      "url": "https://api.github.com/users/Codertocat",
23
+      "html_url": "https://github.com/Codertocat",
24
+      "followers_url": "https://api.github.com/users/Codertocat/followers",
25
+      "following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
26
+      "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
27
+      "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
28
+      "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
29
+      "organizations_url": "https://api.github.com/users/Codertocat/orgs",
30
+      "repos_url": "https://api.github.com/users/Codertocat/repos",
31
+      "events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
32
+      "received_events_url": "https://api.github.com/users/Codertocat/received_events",
33
+      "type": "User",
34
+      "site_admin": false
35
+    },
36
+    "body": "This is a pretty simple change that we need to pull into master.",
37
+    "created_at": "2018-05-30T20:18:30Z",
38
+    "updated_at": "2018-05-30T20:18:50Z",
39
+    "closed_at": "2018-05-30T20:18:50Z",
40
+    "merged_at": null,
41
+    "merge_commit_sha": "414cb0069601a32b00bd122a2380cd283626a8e5",
42
+    "assignee": null,
43
+    "assignees": [
44
+
45
+    ],
46
+    "requested_reviewers": [
47
+
48
+    ],
49
+    "requested_teams": [
50
+
51
+    ],
52
+    "labels": [
53
+
54
+    ],
55
+    "milestone": null,
56
+    "commits_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/1/commits",
57
+    "review_comments_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/1/comments",
58
+    "review_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/comments{/number}",
59
+    "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/1/comments",
60
+    "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/34c5c7793cb3b279e22454cb6750c80560547b3a",
61
+    "head": {
62
+      "label": "Codertocat:changes",
63
+      "ref": "changes",
64
+      "sha": "34c5c7793cb3b279e22454cb6750c80560547b3a",
65
+      "user": {
66
+        "login": "Codertocat",
67
+        "id": 21031067,
68
+        "node_id": "MDQ6VXNlcjIxMDMxMDY3",
69
+        "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
70
+        "gravatar_id": "",
71
+        "url": "https://api.github.com/users/Codertocat",
72
+        "html_url": "https://github.com/Codertocat",
73
+        "followers_url": "https://api.github.com/users/Codertocat/followers",
74
+        "following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
75
+        "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
76
+        "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
77
+        "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
78
+        "organizations_url": "https://api.github.com/users/Codertocat/orgs",
79
+        "repos_url": "https://api.github.com/users/Codertocat/repos",
80
+        "events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
81
+        "received_events_url": "https://api.github.com/users/Codertocat/received_events",
82
+        "type": "User",
83
+        "site_admin": false
84
+      },
85
+      "repo": {
86
+        "id": 135493233,
87
+        "node_id": "MDEwOlJlcG9zaXRvcnkxMzU0OTMyMzM=",
88
+        "name": "Hello-World",
89
+        "full_name": "Codertocat/Hello-World",
90
+        "owner": {
91
+          "login": "Codertocat",
92
+          "id": 21031067,
93
+          "node_id": "MDQ6VXNlcjIxMDMxMDY3",
94
+          "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
95
+          "gravatar_id": "",
96
+          "url": "https://api.github.com/users/Codertocat",
97
+          "html_url": "https://github.com/Codertocat",
98
+          "followers_url": "https://api.github.com/users/Codertocat/followers",
99
+          "following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
100
+          "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
101
+          "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
102
+          "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
103
+          "organizations_url": "https://api.github.com/users/Codertocat/orgs",
104
+          "repos_url": "https://api.github.com/users/Codertocat/repos",
105
+          "events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
106
+          "received_events_url": "https://api.github.com/users/Codertocat/received_events",
107
+          "type": "User",
108
+          "site_admin": false
109
+        },
110
+        "private": false,
111
+        "html_url": "https://github.com/Codertocat/Hello-World",
112
+        "description": null,
113
+        "fork": false,
114
+        "url": "https://api.github.com/repos/Codertocat/Hello-World",
115
+        "forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks",
116
+        "keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}",
117
+        "collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}",
118
+        "teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams",
119
+        "hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks",
120
+        "issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}",
121
+        "events_url": "https://api.github.com/repos/Codertocat/Hello-World/events",
122
+        "assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}",
123
+        "branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}",
124
+        "tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags",
125
+        "blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}",
126
+        "git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}",
127
+        "git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}",
128
+        "trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}",
129
+        "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}",
130
+        "languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages",
131
+        "stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers",
132
+        "contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors",
133
+        "subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers",
134
+        "subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription",
135
+        "commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}",
136
+        "git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}",
137
+        "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}",
138
+        "issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}",
139
+        "contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}",
140
+        "compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}",
141
+        "merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges",
142
+        "archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}",
143
+        "downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads",
144
+        "issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}",
145
+        "pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}",
146
+        "milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}",
147
+        "notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}",
148
+        "labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}",
149
+        "releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}",
150
+        "deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments",
151
+        "created_at": "2018-05-30T20:18:04Z",
152
+        "updated_at": "2018-05-30T20:18:50Z",
153
+        "pushed_at": "2018-05-30T20:18:48Z",
154
+        "git_url": "git://github.com/Codertocat/Hello-World.git",
155
+        "ssh_url": "git@github.com:Codertocat/Hello-World.git",
156
+        "clone_url": "https://github.com/Codertocat/Hello-World.git",
157
+        "svn_url": "https://github.com/Codertocat/Hello-World",
158
+        "homepage": null,
159
+        "size": 0,
160
+        "stargazers_count": 0,
161
+        "watchers_count": 0,
162
+        "language": null,
163
+        "has_issues": true,
164
+        "has_projects": true,
165
+        "has_downloads": true,
166
+        "has_wiki": true,
167
+        "has_pages": true,
168
+        "forks_count": 0,
169
+        "mirror_url": null,
170
+        "archived": false,
171
+        "open_issues_count": 1,
172
+        "license": null,
173
+        "forks": 0,
174
+        "open_issues": 1,
175
+        "watchers": 0,
176
+        "default_branch": "master"
177
+      }
178
+    },
179
+    "base": {
180
+      "label": "Codertocat:master",
181
+      "ref": "master",
182
+      "sha": "a10867b14bb761a232cd80139fbd4c0d33264240",
183
+      "user": {
184
+        "login": "Codertocat",
185
+        "id": 21031067,
186
+        "node_id": "MDQ6VXNlcjIxMDMxMDY3",
187
+        "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
188
+        "gravatar_id": "",
189
+        "url": "https://api.github.com/users/Codertocat",
190
+        "html_url": "https://github.com/Codertocat",
191
+        "followers_url": "https://api.github.com/users/Codertocat/followers",
192
+        "following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
193
+        "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
194
+        "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
195
+        "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
196
+        "organizations_url": "https://api.github.com/users/Codertocat/orgs",
197
+        "repos_url": "https://api.github.com/users/Codertocat/repos",
198
+        "events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
199
+        "received_events_url": "https://api.github.com/users/Codertocat/received_events",
200
+        "type": "User",
201
+        "site_admin": false
202
+      },
203
+      "repo": {
204
+        "id": 135493233,
205
+        "node_id": "MDEwOlJlcG9zaXRvcnkxMzU0OTMyMzM=",
206
+        "name": "Hello-World",
207
+        "full_name": "Codertocat/Hello-World",
208
+        "owner": {
209
+          "login": "Codertocat",
210
+          "id": 21031067,
211
+          "node_id": "MDQ6VXNlcjIxMDMxMDY3",
212
+          "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
213
+          "gravatar_id": "",
214
+          "url": "https://api.github.com/users/Codertocat",
215
+          "html_url": "https://github.com/Codertocat",
216
+          "followers_url": "https://api.github.com/users/Codertocat/followers",
217
+          "following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
218
+          "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
219
+          "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
220
+          "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
221
+          "organizations_url": "https://api.github.com/users/Codertocat/orgs",
222
+          "repos_url": "https://api.github.com/users/Codertocat/repos",
223
+          "events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
224
+          "received_events_url": "https://api.github.com/users/Codertocat/received_events",
225
+          "type": "User",
226
+          "site_admin": false
227
+        },
228
+        "private": false,
229
+        "html_url": "https://github.com/Codertocat/Hello-World",
230
+        "description": null,
231
+        "fork": false,
232
+        "url": "https://api.github.com/repos/Codertocat/Hello-World",
233
+        "forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks",
234
+        "keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}",
235
+        "collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}",
236
+        "teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams",
237
+        "hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks",
238
+        "issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}",
239
+        "events_url": "https://api.github.com/repos/Codertocat/Hello-World/events",
240
+        "assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}",
241
+        "branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}",
242
+        "tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags",
243
+        "blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}",
244
+        "git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}",
245
+        "git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}",
246
+        "trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}",
247
+        "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}",
248
+        "languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages",
249
+        "stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers",
250
+        "contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors",
251
+        "subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers",
252
+        "subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription",
253
+        "commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}",
254
+        "git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}",
255
+        "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}",
256
+        "issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}",
257
+        "contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}",
258
+        "compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}",
259
+        "merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges",
260
+        "archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}",
261
+        "downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads",
262
+        "issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}",
263
+        "pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}",
264
+        "milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}",
265
+        "notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}",
266
+        "labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}",
267
+        "releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}",
268
+        "deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments",
269
+        "created_at": "2018-05-30T20:18:04Z",
270
+        "updated_at": "2018-05-30T20:18:50Z",
271
+        "pushed_at": "2018-05-30T20:18:48Z",
272
+        "git_url": "git://github.com/Codertocat/Hello-World.git",
273
+        "ssh_url": "git@github.com:Codertocat/Hello-World.git",
274
+        "clone_url": "https://github.com/Codertocat/Hello-World.git",
275
+        "svn_url": "https://github.com/Codertocat/Hello-World",
276
+        "homepage": null,
277
+        "size": 0,
278
+        "stargazers_count": 0,
279
+        "watchers_count": 0,
280
+        "language": null,
281
+        "has_issues": true,
282
+        "has_projects": true,
283
+        "has_downloads": true,
284
+        "has_wiki": true,
285
+        "has_pages": true,
286
+        "forks_count": 0,
287
+        "mirror_url": null,
288
+        "archived": false,
289
+        "open_issues_count": 1,
290
+        "license": null,
291
+        "forks": 0,
292
+        "open_issues": 1,
293
+        "watchers": 0,
294
+        "default_branch": "master"
295
+      }
296
+    },
297
+    "_links": {
298
+      "self": {
299
+        "href": "https://api.github.com/repos/Codertocat/Hello-World/pulls/1"
300
+      },
301
+      "html": {
302
+        "href": "https://github.com/Codertocat/Hello-World/pull/1"
303
+      },
304
+      "issue": {
305
+        "href": "https://api.github.com/repos/Codertocat/Hello-World/issues/1"
306
+      },
307
+      "comments": {
308
+        "href": "https://api.github.com/repos/Codertocat/Hello-World/issues/1/comments"
309
+      },
310
+      "review_comments": {
311
+        "href": "https://api.github.com/repos/Codertocat/Hello-World/pulls/1/comments"
312
+      },
313
+      "review_comment": {
314
+        "href": "https://api.github.com/repos/Codertocat/Hello-World/pulls/comments{/number}"
315
+      },
316
+      "commits": {
317
+        "href": "https://api.github.com/repos/Codertocat/Hello-World/pulls/1/commits"
318
+      },
319
+      "statuses": {
320
+        "href": "https://api.github.com/repos/Codertocat/Hello-World/statuses/34c5c7793cb3b279e22454cb6750c80560547b3a"
321
+      }
322
+    },
323
+    "author_association": "OWNER",
324
+    "merged": false,
325
+    "mergeable": true,
326
+    "rebaseable": true,
327
+    "mergeable_state": "clean",
328
+    "merged_by": null,
329
+    "comments": 0,
330
+    "review_comments": 1,
331
+    "maintainer_can_modify": false,
332
+    "commits": 1,
333
+    "additions": 1,
334
+    "deletions": 1,
335
+    "changed_files": 1
336
+  },
337
+  "repository": {
338
+    "id": 135493233,
339
+    "node_id": "MDEwOlJlcG9zaXRvcnkxMzU0OTMyMzM=",
340
+    "name": "Hello-World",
341
+    "full_name": "Codertocat/Hello-World",
342
+    "owner": {
343
+      "login": "Codertocat",
344
+      "id": 21031067,
345
+      "node_id": "MDQ6VXNlcjIxMDMxMDY3",
346
+      "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
347
+      "gravatar_id": "",
348
+      "url": "https://api.github.com/users/Codertocat",
349
+      "html_url": "https://github.com/Codertocat",
350
+      "followers_url": "https://api.github.com/users/Codertocat/followers",
351
+      "following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
352
+      "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
353
+      "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
354
+      "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
355
+      "organizations_url": "https://api.github.com/users/Codertocat/orgs",
356
+      "repos_url": "https://api.github.com/users/Codertocat/repos",
357
+      "events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
358
+      "received_events_url": "https://api.github.com/users/Codertocat/received_events",
359
+      "type": "User",
360
+      "site_admin": false
361
+    },
362
+    "private": false,
363
+    "html_url": "https://github.com/Codertocat/Hello-World",
364
+    "description": null,
365
+    "fork": false,
366
+    "url": "https://api.github.com/repos/Codertocat/Hello-World",
367
+    "forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks",
368
+    "keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}",
369
+    "collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}",
370
+    "teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams",
371
+    "hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks",
372
+    "issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}",
373
+    "events_url": "https://api.github.com/repos/Codertocat/Hello-World/events",
374
+    "assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}",
375
+    "branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}",
376
+    "tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags",
377
+    "blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}",
378
+    "git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}",
379
+    "git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}",
380
+    "trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}",
381
+    "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}",
382
+    "languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages",
383
+    "stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers",
384
+    "contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors",
385
+    "subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers",
386
+    "subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription",
387
+    "commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}",
388
+    "git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}",
389
+    "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}",
390
+    "issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}",
391
+    "contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}",
392
+    "compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}",
393
+    "merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges",
394
+    "archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}",
395
+    "downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads",
396
+    "issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}",
397
+    "pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}",
398
+    "milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}",
399
+    "notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}",
400
+    "labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}",
401
+    "releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}",
402
+    "deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments",
403
+    "created_at": "2018-05-30T20:18:04Z",
404
+    "updated_at": "2018-05-30T20:18:50Z",
405
+    "pushed_at": "2018-05-30T20:18:48Z",
406
+    "git_url": "git://github.com/Codertocat/Hello-World.git",
407
+    "ssh_url": "git@github.com:Codertocat/Hello-World.git",
408
+    "clone_url": "https://github.com/Codertocat/Hello-World.git",
409
+    "svn_url": "https://github.com/Codertocat/Hello-World",
410
+    "homepage": null,
411
+    "size": 0,
412
+    "stargazers_count": 0,
413
+    "watchers_count": 0,
414
+    "language": null,
415
+    "has_issues": true,
416
+    "has_projects": true,
417
+    "has_downloads": true,
418
+    "has_wiki": true,
419
+    "has_pages": true,
420
+    "forks_count": 0,
421
+    "mirror_url": null,
422
+    "archived": false,
423
+    "open_issues_count": 1,
424
+    "license": null,
425
+    "forks": 0,
426
+    "open_issues": 1,
427
+    "watchers": 0,
428
+    "default_branch": "master"
429
+  },
430
+  "sender": {
431
+    "login": "Codertocat",
432
+    "id": 21031067,
433
+    "node_id": "MDQ6VXNlcjIxMDMxMDY3",
434
+    "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
435
+    "gravatar_id": "",
436
+    "url": "https://api.github.com/users/Codertocat",
437
+    "html_url": "https://github.com/Codertocat",
438
+    "followers_url": "https://api.github.com/users/Codertocat/followers",
439
+    "following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
440
+    "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
441
+    "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
442
+    "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
443
+    "organizations_url": "https://api.github.com/users/Codertocat/orgs",
444
+    "repos_url": "https://api.github.com/users/Codertocat/repos",
445
+    "events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
446
+    "received_events_url": "https://api.github.com/users/Codertocat/received_events",
447
+    "type": "User",
448
+    "site_admin": false
449
+  }
450
+}

Loading…
Cancel
Save