Skip to content

Commit e4c033d

Browse files
authoredMar 25, 2021
Merge pull request #259 from jstrachan/frazergibsonntt-master
fix: added bitbucket cloud comment support
2 parents ee723d0 + 27c2c7d commit e4c033d

File tree

7 files changed

+280
-20
lines changed

7 files changed

+280
-20
lines changed
 

‎scm/client.go

+2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ package scm
77
import (
88
"context"
99
"errors"
10+
1011
"io"
1112
"net/http"
13+
1214
"net/url"
1315
"strconv"
1416
"strings"

‎scm/driver/bitbucket/issue.go

+66-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import (
99

1010
"github.com/jenkins-x/go-scm/scm/labels"
1111

12+
"fmt"
13+
"time"
14+
1215
"github.com/jenkins-x/go-scm/scm"
1316
)
1417

@@ -66,20 +69,80 @@ func (s *issueService) List(ctx context.Context, repo string, opts scm.IssueList
6669
return nil, nil, scm.ErrNotSupported
6770
}
6871

72+
func convertIssueCommentList(from []*issueComment) []*scm.Comment {
73+
to := []*scm.Comment{}
74+
for _, v := range from {
75+
to = append(to, convertIssueComment(v))
76+
}
77+
return to
78+
}
79+
6980
func (s *issueService) ListComments(ctx context.Context, repo string, index int, opts scm.ListOptions) ([]*scm.Comment, *scm.Response, error) {
70-
return nil, nil, scm.ErrNotSupported
81+
path := fmt.Sprintf("2.0/repositories/%s/pullrequests/%d/comments?%s", repo, index, encodeListOptions(opts))
82+
out := []*issueComment{}
83+
res, err := s.client.do(ctx, "GET", path, nil, &out)
84+
return convertIssueCommentList(out), res, err
7185
}
7286

7387
func (s *issueService) Create(ctx context.Context, repo string, input *scm.IssueInput) (*scm.Issue, *scm.Response, error) {
7488
return nil, nil, scm.ErrNotSupported
7589
}
7690

91+
type issueCommentInput struct {
92+
Content struct {
93+
Raw string `json:"raw,omitempty"`
94+
} `json:"content"`
95+
}
96+
97+
type issueComment struct {
98+
ID int `json:"id"`
99+
Links struct {
100+
HTML struct {
101+
Href string `json:"href"`
102+
} `json:"html"`
103+
} `json:"links"`
104+
User struct {
105+
AccountID string `json:"account_id"`
106+
DisplayName string `json:"display_name"`
107+
Links struct {
108+
Avatar struct {
109+
Href string `json:"href"`
110+
} `json:"avatar"`
111+
} `json:"links"`
112+
} `json:"user"`
113+
Content struct {
114+
Raw string `json:"raw"`
115+
} `json:"content"`
116+
CreatedOn time.Time `json:"created_on"`
117+
UpdatedOn time.Time `json:"updated_on"`
118+
}
119+
120+
func convertIssueComment(from *issueComment) *scm.Comment {
121+
return &scm.Comment{
122+
ID: from.ID,
123+
Body: from.Content.Raw,
124+
Author: scm.User{
125+
Login: from.User.DisplayName,
126+
Avatar: from.User.Links.Avatar.Href,
127+
},
128+
Link: from.Links.HTML.Href,
129+
Created: from.CreatedOn,
130+
Updated: from.UpdatedOn,
131+
}
132+
}
133+
77134
func (s *issueService) CreateComment(ctx context.Context, repo string, number int, input *scm.CommentInput) (*scm.Comment, *scm.Response, error) {
78-
return nil, nil, scm.ErrNotSupported
135+
path := fmt.Sprintf("2.0/repositories/%s/pullrequests/%d/comments", repo, number)
136+
in := new(issueCommentInput)
137+
in.Content.Raw = input.Body
138+
out := new(issueComment)
139+
res, err := s.client.do(ctx, "POST", path, in, out)
140+
return convertIssueComment(out), res, err
79141
}
80142

81143
func (s *issueService) DeleteComment(ctx context.Context, repo string, number, id int) (*scm.Response, error) {
82-
return nil, scm.ErrNotSupported
144+
path := fmt.Sprintf("2.0/repositories/%s/pullrequests/%d/comments/%d", repo, number, id)
145+
return s.client.do(ctx, "DELETE", path, nil, nil)
83146
}
84147

85148
func (s *issueService) EditComment(ctx context.Context, repo string, number int, id int, input *scm.CommentInput) (*scm.Comment, *scm.Response, error) {

‎scm/driver/bitbucket/issue_test.go

+3-12
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,7 @@ func TestIssueList(t *testing.T) {
3333
}
3434

3535
func TestIssueListComments(t *testing.T) {
36-
_, _, err := NewDefault().Issues.ListComments(context.Background(), "", 0, scm.ListOptions{})
37-
if err != nil && err != scm.ErrNotSupported {
38-
t.Errorf("Expect Not Supported error")
39-
}
36+
// TODO
4037
}
4138

4239
func TestIssueCreate(t *testing.T) {
@@ -47,17 +44,11 @@ func TestIssueCreate(t *testing.T) {
4744
}
4845

4946
func TestIssueCreateComment(t *testing.T) {
50-
_, _, err := NewDefault().Issues.CreateComment(context.Background(), "", 0, &scm.CommentInput{})
51-
if err != nil && err != scm.ErrNotSupported {
52-
t.Errorf("Expect Not Supported error")
53-
}
47+
// TODO
5448
}
5549

5650
func TestIssueCommentDelete(t *testing.T) {
57-
_, err := NewDefault().Issues.DeleteComment(context.Background(), "", 0, 0)
58-
if err != nil && err != scm.ErrNotSupported {
59-
t.Errorf("Expect Not Supported error")
60-
}
51+
// TODO
6152
}
6253

6354
func TestIssueClose(t *testing.T) {

‎scm/driver/bitbucket/pr.go

+116
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,122 @@ func (s *pullService) List(ctx context.Context, repo string, opts scm.PullReques
4646
return convertPullRequests(out), res, err
4747
}
4848

49+
type prCommentInput struct {
50+
Content struct {
51+
Raw string `json:"raw,omitempty"`
52+
} `json:"content"`
53+
}
54+
55+
type pullRequestComments struct {
56+
pagination
57+
Values []*prComment `json:"values"`
58+
}
59+
60+
type prComment struct {
61+
ID int `json:"id"`
62+
Type string `json:"type"`
63+
Links struct {
64+
HTML struct {
65+
Href string `json:"href"`
66+
} `json:"html,omitempty"`
67+
Self struct {
68+
Href string `json:"href"`
69+
} `json:"self,omitempty"`
70+
Code struct {
71+
Href string `json:"href"`
72+
} `json:"code,omitempty"`
73+
} `json:"links"`
74+
PR struct {
75+
Title string `json:"title"`
76+
ID int `json:"id"`
77+
Type string `json:"type"`
78+
Links struct {
79+
HTML struct {
80+
Href string `json:"href"`
81+
} `json:"html"`
82+
Self struct {
83+
Href string `json:"href"`
84+
} `json:"self"`
85+
} `json:"links"`
86+
} `json:"pullrequest"`
87+
User struct {
88+
AccountID string `json:"account_id"`
89+
DisplayName string `json:"display_name"`
90+
UUID string `json:"uuid"`
91+
Type string `json:"type"`
92+
NickName string `json:"nickname"`
93+
Links struct {
94+
HTML struct {
95+
Href string `json:"href"`
96+
} `json:"html"`
97+
Self struct {
98+
Href string `json:"href"`
99+
} `json:"self"`
100+
Avatar struct {
101+
Href string `json:"href"`
102+
} `json:"avatar"`
103+
} `json:"links"`
104+
} `json:"user"`
105+
Content struct {
106+
Raw string `json:"raw"`
107+
Markup string `json:"markup"`
108+
HTML string `json:"html"`
109+
Type string `json:"type"`
110+
} `json:"content"`
111+
Inline struct {
112+
To int `json:"to,omitempty"`
113+
From int `json:"from,omitempty"`
114+
Path string `json:"path,omitempty"`
115+
} `json:"inline,omitempty"`
116+
Deleted bool `json:"deleted"`
117+
UpdatedOn time.Time `json:"updated_on"`
118+
CreatedOn time.Time `json:"created_on"`
119+
}
120+
121+
func convertPRComment(from *prComment) *scm.Comment {
122+
123+
return &scm.Comment{
124+
ID: from.ID,
125+
Body: from.Content.Raw,
126+
Author: scm.User{
127+
Login: from.User.DisplayName,
128+
Avatar: from.User.Links.Avatar.Href,
129+
},
130+
Link: from.Links.HTML.Href,
131+
Created: from.CreatedOn,
132+
Updated: from.UpdatedOn,
133+
}
134+
}
135+
136+
func (s *pullService) CreateComment(ctx context.Context, repo string, number int, input *scm.CommentInput) (*scm.Comment, *scm.Response, error) {
137+
path := fmt.Sprintf("2.0/repositories/%s/pullrequests/%d/comments", repo, number)
138+
in := new(prCommentInput)
139+
in.Content.Raw = input.Body
140+
out := new(prComment)
141+
res, err := s.client.do(ctx, "POST", path, in, out)
142+
return convertPRComment(out), res, err
143+
}
144+
145+
func (s *pullService) DeleteComment(ctx context.Context, repo string, number, id int) (*scm.Response, error) {
146+
path := fmt.Sprintf("2.0/repositories/%s/pullrequests/%d/comments/%d", repo, number, id)
147+
return s.client.do(ctx, "DELETE", path, nil, nil)
148+
}
149+
150+
func convertPRCommentList(from *pullRequestComments) []*scm.Comment {
151+
to := []*scm.Comment{}
152+
for _, v := range from.Values {
153+
to = append(to, convertPRComment(v))
154+
}
155+
return to
156+
}
157+
158+
func (s *pullService) ListComments(ctx context.Context, repo string, index int, opts scm.ListOptions) ([]*scm.Comment, *scm.Response, error) {
159+
path := fmt.Sprintf("2.0/repositories/%s/pullrequests/%d/comments?%s", repo, index, encodeListOptions(opts))
160+
out := new(pullRequestComments)
161+
res, err := s.client.do(ctx, "GET", path, nil, &out)
162+
return convertPRCommentList(out), res, err
163+
}
164+
49165
func (s *pullService) ListChanges(ctx context.Context, repo string, number int, opts scm.ListOptions) ([]*scm.Change, *scm.Response, error) {
50166
path := fmt.Sprintf("2.0/repositories/%s/pullrequests/%d/diffstat?%s", repo, number, encodeListOptions(opts))
51167
out := new(diffstats)

‎scm/driver/bitbucket/repo.go

+37-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"fmt"
1010
"io/ioutil"
1111
"net/url"
12+
"sort"
1213
"strings"
1314
"time"
1415

@@ -122,7 +123,42 @@ func (s *repositoryService) Fork(context.Context, *scm.RepositoryInput, string)
122123
}
123124

124125
func (s *repositoryService) FindCombinedStatus(ctx context.Context, repo, ref string) (*scm.CombinedStatus, *scm.Response, error) {
125-
return nil, nil, scm.ErrNotSupported
126+
statusList, resp, err := s.ListStatus(ctx, repo, ref, scm.ListOptions{})
127+
if err != nil {
128+
return nil, resp, errors.Wrapf(err, "failed to list statuses")
129+
}
130+
131+
combinedState := scm.StateUnknown
132+
133+
byContext := make(map[string]*scm.Status)
134+
for _, s := range statusList {
135+
byContext[s.Target] = s
136+
}
137+
138+
keys := make([]string, 0, len(byContext))
139+
for k := range byContext {
140+
keys = append(keys, k)
141+
}
142+
sort.Strings(keys)
143+
var statuses []*scm.Status
144+
for _, k := range keys {
145+
s := byContext[k]
146+
statuses = append(statuses, s)
147+
}
148+
149+
for _, s := range statuses {
150+
// If we've still got a default state, or the state of the current status is worse than the current state, set it.
151+
if combinedState == scm.StateUnknown || combinedState > s.State {
152+
combinedState = s.State
153+
}
154+
}
155+
156+
combined := &scm.CombinedStatus{
157+
State: 0,
158+
Sha: ref,
159+
Statuses: statuses,
160+
}
161+
return combined, resp, nil
126162
}
127163

128164
func (s *repositoryService) FindUserPermission(ctx context.Context, repo string, user string) (string, *scm.Response, error) {

‎scm/driver/bitbucket/util.go

+12-4
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,13 @@ func encodeListOptions(opts scm.ListOptions) string {
2929
if opts.Page != 0 {
3030
params.Set("page", strconv.Itoa(opts.Page))
3131
}
32-
if opts.Size != 0 {
33-
params.Set("pagelen", strconv.Itoa(opts.Size))
32+
if opts.Size == 0 {
33+
opts.Size = 50
34+
}
35+
if opts.Size > 50 {
36+
opts.Size = 50
3437
}
38+
params.Set("pagelen", strconv.Itoa(opts.Size))
3539
return params.Encode()
3640
}
3741

@@ -85,9 +89,13 @@ func encodePullRequestListOptions(opts scm.PullRequestListOptions) string {
8589
if opts.Page != 0 {
8690
params.Set("page", strconv.Itoa(opts.Page))
8791
}
88-
if opts.Size != 0 {
89-
params.Set("pagelen", strconv.Itoa(opts.Size))
92+
if opts.Size == 0 {
93+
opts.Size = 50
94+
}
95+
if opts.Size > 50 {
96+
opts.Size = 50
9097
}
98+
params.Set("pagelen", strconv.Itoa(opts.Size))
9199
if opts.Open && opts.Closed {
92100
params.Set("state", "all")
93101
} else if opts.Closed {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
8+
"github.com/jenkins-x/go-scm/scm/factory"
9+
"github.com/jenkins-x/go-scm/scm/factory/examples/helpers"
10+
)
11+
12+
func main() {
13+
args := os.Args
14+
if len(args) < 2 {
15+
fmt.Println("usage: repo ref")
16+
os.Exit(1)
17+
return
18+
}
19+
repo := args[1]
20+
ref := "master"
21+
if len(args) > 2 {
22+
ref = args[2]
23+
}
24+
25+
client, err := factory.NewClientFromEnvironment()
26+
if err != nil {
27+
helpers.Fail(err)
28+
return
29+
}
30+
31+
fmt.Printf("finding combined status in repo: %s ref: %s\n", repo, ref)
32+
33+
ctx := context.Background()
34+
results, _, err := client.Repositories.FindCombinedStatus(ctx, repo, ref)
35+
if err != nil {
36+
helpers.Fail(err)
37+
return
38+
}
39+
fmt.Printf("state: %v sha: %s\n", results.State, results.Sha)
40+
41+
for _, s := range results.Statuses {
42+
fmt.Printf("target %s state %v label %s\n", s.Target, s.State, s.Label)
43+
}
44+
}

0 commit comments

Comments
 (0)
Please sign in to comment.