X Tutup
Skip to content

Commit 1e4fa60

Browse files
authored
Merge pull request cli#2462 from cli/view-comments
Add issue comment viewing
2 parents d81b292 + efc05de commit 1e4fa60

16 files changed

+1150
-87
lines changed

api/queries_comments.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package api
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/cli/cli/internal/ghrepo"
8+
"github.com/shurcooL/githubv4"
9+
)
10+
11+
type Comments struct {
12+
Nodes []Comment
13+
TotalCount int
14+
PageInfo PageInfo
15+
}
16+
17+
type Comment struct {
18+
Author Author
19+
AuthorAssociation string
20+
Body string
21+
CreatedAt time.Time
22+
IncludesCreatedEdit bool
23+
ReactionGroups ReactionGroups
24+
}
25+
26+
type PageInfo struct {
27+
HasNextPage bool
28+
EndCursor string
29+
}
30+
31+
func CommentsForIssue(client *Client, repo ghrepo.Interface, issue *Issue) (*Comments, error) {
32+
type response struct {
33+
Repository struct {
34+
Issue struct {
35+
Comments Comments `graphql:"comments(first: 100, after: $endCursor)"`
36+
} `graphql:"issue(number: $number)"`
37+
} `graphql:"repository(owner: $owner, name: $repo)"`
38+
}
39+
40+
variables := map[string]interface{}{
41+
"owner": githubv4.String(repo.RepoOwner()),
42+
"repo": githubv4.String(repo.RepoName()),
43+
"number": githubv4.Int(issue.Number),
44+
"endCursor": (*githubv4.String)(nil),
45+
}
46+
47+
gql := graphQLClient(client.http, repo.RepoHost())
48+
49+
var comments []Comment
50+
for {
51+
var query response
52+
err := gql.QueryNamed(context.Background(), "CommentsForIssue", &query, variables)
53+
if err != nil {
54+
return nil, err
55+
}
56+
57+
comments = append(comments, query.Repository.Issue.Comments.Nodes...)
58+
if !query.Repository.Issue.Comments.PageInfo.HasNextPage {
59+
break
60+
}
61+
variables["endCursor"] = githubv4.String(query.Repository.Issue.Comments.PageInfo.EndCursor)
62+
}
63+
64+
return &Comments{Nodes: comments, TotalCount: len(comments)}, nil
65+
}

api/queries_issue.go

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,8 @@ type Issue struct {
3333
Body string
3434
CreatedAt time.Time
3535
UpdatedAt time.Time
36-
Comments struct {
37-
TotalCount int
38-
}
39-
Author struct {
40-
Login string
41-
}
36+
Comments Comments
37+
Author Author
4238
Assignees struct {
4339
Nodes []struct {
4440
Login string
@@ -65,12 +61,17 @@ type Issue struct {
6561
Milestone struct {
6662
Title string
6763
}
64+
ReactionGroups ReactionGroups
6865
}
6966

7067
type IssuesDisabledError struct {
7168
error
7269
}
7370

71+
type Author struct {
72+
Login string
73+
}
74+
7475
const fragments = `
7576
fragment issue on Issue {
7677
number
@@ -341,7 +342,22 @@ func IssueByNumber(client *Client, repo ghrepo.Interface, number int) (*Issue, e
341342
author {
342343
login
343344
}
344-
comments {
345+
comments(last: 1) {
346+
nodes {
347+
author {
348+
login
349+
}
350+
authorAssociation
351+
body
352+
createdAt
353+
includesCreatedEdit
354+
reactionGroups {
355+
content
356+
users {
357+
totalCount
358+
}
359+
}
360+
}
345361
totalCount
346362
}
347363
number
@@ -370,9 +386,15 @@ func IssueByNumber(client *Client, repo ghrepo.Interface, number int) (*Issue, e
370386
}
371387
totalCount
372388
}
373-
milestone{
389+
milestone {
374390
title
375391
}
392+
reactionGroups {
393+
content
394+
users {
395+
totalCount
396+
}
397+
}
376398
}
377399
}
378400
}`

api/reaction_groups.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package api
2+
3+
type ReactionGroups []ReactionGroup
4+
5+
type ReactionGroup struct {
6+
Content string
7+
Users ReactionGroupUsers
8+
}
9+
10+
type ReactionGroupUsers struct {
11+
TotalCount int
12+
}
13+
14+
func (rg ReactionGroup) Count() int {
15+
return rg.Users.TotalCount
16+
}
17+
18+
func (rg ReactionGroup) Emoji() string {
19+
return reactionEmoji[rg.Content]
20+
}
21+
22+
var reactionEmoji = map[string]string{
23+
"THUMBS_UP": "\U0001f44d",
24+
"THUMBS_DOWN": "\U0001f44e",
25+
"LAUGH": "\U0001f604",
26+
"HOORAY": "\U0001f389",
27+
"CONFUSED": "\U0001f615",
28+
"HEART": "\u2764\ufe0f",
29+
"ROCKET": "\U0001f680",
30+
"EYES": "\U0001f440",
31+
}

api/reaction_groups_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package api
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func Test_String(t *testing.T) {
10+
tests := map[string]struct {
11+
rg ReactionGroup
12+
emoji string
13+
count int
14+
}{
15+
"empty reaction group": {
16+
rg: ReactionGroup{},
17+
emoji: "",
18+
count: 0,
19+
},
20+
"unknown reaction group": {
21+
rg: ReactionGroup{
22+
Content: "UNKNOWN",
23+
Users: ReactionGroupUsers{TotalCount: 1},
24+
},
25+
emoji: "",
26+
count: 1,
27+
},
28+
"thumbs up reaction group": {
29+
rg: ReactionGroup{
30+
Content: "THUMBS_UP",
31+
Users: ReactionGroupUsers{TotalCount: 2},
32+
},
33+
emoji: "\U0001f44d",
34+
count: 2,
35+
},
36+
"thumbs down reaction group": {
37+
rg: ReactionGroup{
38+
Content: "THUMBS_DOWN",
39+
Users: ReactionGroupUsers{TotalCount: 3},
40+
},
41+
emoji: "\U0001f44e",
42+
count: 3,
43+
},
44+
"laugh reaction group": {
45+
rg: ReactionGroup{
46+
Content: "LAUGH",
47+
Users: ReactionGroupUsers{TotalCount: 4},
48+
},
49+
emoji: "\U0001f604",
50+
count: 4,
51+
},
52+
"hooray reaction group": {
53+
rg: ReactionGroup{
54+
Content: "HOORAY",
55+
Users: ReactionGroupUsers{TotalCount: 5},
56+
},
57+
emoji: "\U0001f389",
58+
count: 5,
59+
},
60+
"confused reaction group": {
61+
rg: ReactionGroup{
62+
Content: "CONFUSED",
63+
Users: ReactionGroupUsers{TotalCount: 6},
64+
},
65+
emoji: "\U0001f615",
66+
count: 6,
67+
},
68+
"heart reaction group": {
69+
rg: ReactionGroup{
70+
Content: "HEART",
71+
Users: ReactionGroupUsers{TotalCount: 7},
72+
},
73+
emoji: "\u2764\ufe0f",
74+
count: 7,
75+
},
76+
"rocket reaction group": {
77+
rg: ReactionGroup{
78+
Content: "ROCKET",
79+
Users: ReactionGroupUsers{TotalCount: 8},
80+
},
81+
emoji: "\U0001f680",
82+
count: 8,
83+
},
84+
"eyes reaction group": {
85+
rg: ReactionGroup{
86+
Content: "EYES",
87+
Users: ReactionGroupUsers{TotalCount: 9},
88+
},
89+
emoji: "\U0001f440",
90+
count: 9,
91+
},
92+
}
93+
94+
for name, tt := range tests {
95+
t.Run(name, func(t *testing.T) {
96+
assert.Equal(t, tt.emoji, tt.rg.Emoji())
97+
assert.Equal(t, tt.count, tt.rg.Count())
98+
})
99+
}
100+
}

pkg/cmd/issue/shared/lookup.go

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,49 +12,48 @@ import (
1212
)
1313

1414
func IssueFromArg(apiClient *api.Client, baseRepoFn func() (ghrepo.Interface, error), arg string) (*api.Issue, ghrepo.Interface, error) {
15-
issue, baseRepo, err := issueFromURL(apiClient, arg)
16-
if err != nil {
17-
return nil, nil, err
18-
}
19-
if issue != nil {
20-
return issue, baseRepo, nil
21-
}
22-
23-
baseRepo, err = baseRepoFn()
24-
if err != nil {
25-
return nil, nil, fmt.Errorf("could not determine base repo: %w", err)
15+
issueNumber, baseRepo := issueMetadataFromURL(arg)
16+
17+
if issueNumber == 0 {
18+
var err error
19+
issueNumber, err = strconv.Atoi(strings.TrimPrefix(arg, "#"))
20+
if err != nil {
21+
return nil, nil, fmt.Errorf("invalid issue format: %q", arg)
22+
}
2623
}
2724

28-
issueNumber, err := strconv.Atoi(strings.TrimPrefix(arg, "#"))
29-
if err != nil {
30-
return nil, nil, fmt.Errorf("invalid issue format: %q", arg)
25+
if baseRepo == nil {
26+
var err error
27+
baseRepo, err = baseRepoFn()
28+
if err != nil {
29+
return nil, nil, fmt.Errorf("could not determine base repo: %w", err)
30+
}
3131
}
3232

33-
issue, err = issueFromNumber(apiClient, baseRepo, issueNumber)
33+
issue, err := issueFromNumber(apiClient, baseRepo, issueNumber)
3434
return issue, baseRepo, err
3535
}
3636

3737
var issueURLRE = regexp.MustCompile(`^/([^/]+)/([^/]+)/issues/(\d+)`)
3838

39-
func issueFromURL(apiClient *api.Client, s string) (*api.Issue, ghrepo.Interface, error) {
39+
func issueMetadataFromURL(s string) (int, ghrepo.Interface) {
4040
u, err := url.Parse(s)
4141
if err != nil {
42-
return nil, nil, nil
42+
return 0, nil
4343
}
4444

4545
if u.Scheme != "https" && u.Scheme != "http" {
46-
return nil, nil, nil
46+
return 0, nil
4747
}
4848

4949
m := issueURLRE.FindStringSubmatch(u.Path)
5050
if m == nil {
51-
return nil, nil, nil
51+
return 0, nil
5252
}
5353

5454
repo := ghrepo.NewWithHost(m[1], m[2], u.Hostname())
5555
issueNumber, _ := strconv.Atoi(m[3])
56-
issue, err := issueFromNumber(apiClient, repo, issueNumber)
57-
return issue, repo, err
56+
return issueNumber, repo
5857
}
5958

6059
func issueFromNumber(apiClient *api.Client, repo ghrepo.Interface, issueNumber int) (*api.Issue, error) {

pkg/cmd/issue/view/fixtures/issueView_preview.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,21 @@
77
"body": "**bold story**",
88
"title": "ix of coins",
99
"state": "OPEN",
10-
"created_at": "2011-01-26T19:01:12Z",
10+
"createdAt": "2011-01-26T19:01:12Z",
1111
"author": {
1212
"login": "marseilles"
1313
},
1414
"assignees": {
1515
"nodes": [],
16-
"totalcount": 0
16+
"totalCount": 0
1717
},
1818
"labels": {
1919
"nodes": [],
20-
"totalcount": 0
20+
"totalCount": 0
2121
},
2222
"projectcards": {
2323
"nodes": [],
24-
"totalcount": 0
24+
"totalCount": 0
2525
},
2626
"milestone": {
2727
"title": ""

pkg/cmd/issue/view/fixtures/issueView_previewClosedState.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"body": "**bold story**",
88
"title": "ix of coins",
99
"state": "CLOSED",
10-
"created_at": "2011-01-26T19:01:12Z",
10+
"createdAt": "2011-01-26T19:01:12Z",
1111
"author": {
1212
"login": "marseilles"
1313
},

0 commit comments

Comments
 (0)
X Tutup