Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: added bitbucket cloud comment support #259

Merged
merged 5 commits into from
Mar 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions scm/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ package scm
import (
"context"
"errors"

"io"
"net/http"

"net/url"
"strconv"
"strings"
Expand Down
69 changes: 66 additions & 3 deletions scm/driver/bitbucket/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import (

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

"fmt"
"time"

"github.com/jenkins-x/go-scm/scm"
)

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

func convertIssueCommentList(from []*issueComment) []*scm.Comment {
to := []*scm.Comment{}
for _, v := range from {
to = append(to, convertIssueComment(v))
}
return to
}

func (s *issueService) ListComments(ctx context.Context, repo string, index int, opts scm.ListOptions) ([]*scm.Comment, *scm.Response, error) {
return nil, nil, scm.ErrNotSupported
path := fmt.Sprintf("2.0/repositories/%s/pullrequests/%d/comments?%s", repo, index, encodeListOptions(opts))
out := []*issueComment{}
res, err := s.client.do(ctx, "GET", path, nil, &out)
return convertIssueCommentList(out), res, err
}

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

type issueCommentInput struct {
Content struct {
Raw string `json:"raw,omitempty"`
} `json:"content"`
}

type issueComment struct {
ID int `json:"id"`
Links struct {
HTML struct {
Href string `json:"href"`
} `json:"html"`
} `json:"links"`
User struct {
AccountID string `json:"account_id"`
DisplayName string `json:"display_name"`
Links struct {
Avatar struct {
Href string `json:"href"`
} `json:"avatar"`
} `json:"links"`
} `json:"user"`
Content struct {
Raw string `json:"raw"`
} `json:"content"`
CreatedOn time.Time `json:"created_on"`
UpdatedOn time.Time `json:"updated_on"`
}

func convertIssueComment(from *issueComment) *scm.Comment {
return &scm.Comment{
ID: from.ID,
Body: from.Content.Raw,
Author: scm.User{
Login: from.User.DisplayName,
Avatar: from.User.Links.Avatar.Href,
},
Link: from.Links.HTML.Href,
Created: from.CreatedOn,
Updated: from.UpdatedOn,
}
}

func (s *issueService) CreateComment(ctx context.Context, repo string, number int, input *scm.CommentInput) (*scm.Comment, *scm.Response, error) {
return nil, nil, scm.ErrNotSupported
path := fmt.Sprintf("2.0/repositories/%s/pullrequests/%d/comments", repo, number)
in := new(issueCommentInput)
in.Content.Raw = input.Body
out := new(issueComment)
res, err := s.client.do(ctx, "POST", path, in, out)
return convertIssueComment(out), res, err
}

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

func (s *issueService) EditComment(ctx context.Context, repo string, number int, id int, input *scm.CommentInput) (*scm.Comment, *scm.Response, error) {
Expand Down
15 changes: 3 additions & 12 deletions scm/driver/bitbucket/issue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ func TestIssueList(t *testing.T) {
}

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

func TestIssueCreate(t *testing.T) {
Expand All @@ -47,17 +44,11 @@ func TestIssueCreate(t *testing.T) {
}

func TestIssueCreateComment(t *testing.T) {
_, _, err := NewDefault().Issues.CreateComment(context.Background(), "", 0, &scm.CommentInput{})
if err != nil && err != scm.ErrNotSupported {
t.Errorf("Expect Not Supported error")
}
// TODO
}

func TestIssueCommentDelete(t *testing.T) {
_, err := NewDefault().Issues.DeleteComment(context.Background(), "", 0, 0)
if err != nil && err != scm.ErrNotSupported {
t.Errorf("Expect Not Supported error")
}
// TODO
}

func TestIssueClose(t *testing.T) {
Expand Down
116 changes: 116 additions & 0 deletions scm/driver/bitbucket/pr.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,122 @@ func (s *pullService) List(ctx context.Context, repo string, opts scm.PullReques
return convertPullRequests(out), res, err
}

type prCommentInput struct {
Content struct {
Raw string `json:"raw,omitempty"`
} `json:"content"`
}

type pullRequestComments struct {
pagination
Values []*prComment `json:"values"`
}

type prComment struct {
ID int `json:"id"`
Type string `json:"type"`
Links struct {
HTML struct {
Href string `json:"href"`
} `json:"html,omitempty"`
Self struct {
Href string `json:"href"`
} `json:"self,omitempty"`
Code struct {
Href string `json:"href"`
} `json:"code,omitempty"`
} `json:"links"`
PR struct {
Title string `json:"title"`
ID int `json:"id"`
Type string `json:"type"`
Links struct {
HTML struct {
Href string `json:"href"`
} `json:"html"`
Self struct {
Href string `json:"href"`
} `json:"self"`
} `json:"links"`
} `json:"pullrequest"`
User struct {
AccountID string `json:"account_id"`
DisplayName string `json:"display_name"`
UUID string `json:"uuid"`
Type string `json:"type"`
NickName string `json:"nickname"`
Links struct {
HTML struct {
Href string `json:"href"`
} `json:"html"`
Self struct {
Href string `json:"href"`
} `json:"self"`
Avatar struct {
Href string `json:"href"`
} `json:"avatar"`
} `json:"links"`
} `json:"user"`
Content struct {
Raw string `json:"raw"`
Markup string `json:"markup"`
HTML string `json:"html"`
Type string `json:"type"`
} `json:"content"`
Inline struct {
To int `json:"to,omitempty"`
From int `json:"from,omitempty"`
Path string `json:"path,omitempty"`
} `json:"inline,omitempty"`
Deleted bool `json:"deleted"`
UpdatedOn time.Time `json:"updated_on"`
CreatedOn time.Time `json:"created_on"`
}

func convertPRComment(from *prComment) *scm.Comment {

return &scm.Comment{
ID: from.ID,
Body: from.Content.Raw,
Author: scm.User{
Login: from.User.DisplayName,
Avatar: from.User.Links.Avatar.Href,
},
Link: from.Links.HTML.Href,
Created: from.CreatedOn,
Updated: from.UpdatedOn,
}
}

func (s *pullService) CreateComment(ctx context.Context, repo string, number int, input *scm.CommentInput) (*scm.Comment, *scm.Response, error) {
path := fmt.Sprintf("2.0/repositories/%s/pullrequests/%d/comments", repo, number)
in := new(prCommentInput)
in.Content.Raw = input.Body
out := new(prComment)
res, err := s.client.do(ctx, "POST", path, in, out)
return convertPRComment(out), res, err
}

func (s *pullService) DeleteComment(ctx context.Context, repo string, number, id int) (*scm.Response, error) {
path := fmt.Sprintf("2.0/repositories/%s/pullrequests/%d/comments/%d", repo, number, id)
return s.client.do(ctx, "DELETE", path, nil, nil)
}

func convertPRCommentList(from *pullRequestComments) []*scm.Comment {
to := []*scm.Comment{}
for _, v := range from.Values {
to = append(to, convertPRComment(v))
}
return to
}

func (s *pullService) ListComments(ctx context.Context, repo string, index int, opts scm.ListOptions) ([]*scm.Comment, *scm.Response, error) {
path := fmt.Sprintf("2.0/repositories/%s/pullrequests/%d/comments?%s", repo, index, encodeListOptions(opts))
out := new(pullRequestComments)
res, err := s.client.do(ctx, "GET", path, nil, &out)
return convertPRCommentList(out), res, err
}

func (s *pullService) ListChanges(ctx context.Context, repo string, number int, opts scm.ListOptions) ([]*scm.Change, *scm.Response, error) {
path := fmt.Sprintf("2.0/repositories/%s/pullrequests/%d/diffstat?%s", repo, number, encodeListOptions(opts))
out := new(diffstats)
Expand Down
38 changes: 37 additions & 1 deletion scm/driver/bitbucket/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"
"io/ioutil"
"net/url"
"sort"
"strings"
"time"

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

func (s *repositoryService) FindCombinedStatus(ctx context.Context, repo, ref string) (*scm.CombinedStatus, *scm.Response, error) {
return nil, nil, scm.ErrNotSupported
statusList, resp, err := s.ListStatus(ctx, repo, ref, scm.ListOptions{})
if err != nil {
return nil, resp, errors.Wrapf(err, "failed to list statuses")
}

combinedState := scm.StateUnknown

byContext := make(map[string]*scm.Status)
for _, s := range statusList {
byContext[s.Target] = s
}

keys := make([]string, 0, len(byContext))
for k := range byContext {
keys = append(keys, k)
}
sort.Strings(keys)
var statuses []*scm.Status
for _, k := range keys {
s := byContext[k]
statuses = append(statuses, s)
}

for _, s := range statuses {
// If we've still got a default state, or the state of the current status is worse than the current state, set it.
if combinedState == scm.StateUnknown || combinedState > s.State {
combinedState = s.State
}
}

combined := &scm.CombinedStatus{
State: 0,
Sha: ref,
Statuses: statuses,
}
return combined, resp, nil
}

func (s *repositoryService) FindUserPermission(ctx context.Context, repo string, user string) (string, *scm.Response, error) {
Expand Down
16 changes: 12 additions & 4 deletions scm/driver/bitbucket/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@ func encodeListOptions(opts scm.ListOptions) string {
if opts.Page != 0 {
params.Set("page", strconv.Itoa(opts.Page))
}
if opts.Size != 0 {
params.Set("pagelen", strconv.Itoa(opts.Size))
if opts.Size == 0 {
opts.Size = 50
}
if opts.Size > 50 {
opts.Size = 50
}
params.Set("pagelen", strconv.Itoa(opts.Size))
return params.Encode()
}

Expand Down Expand Up @@ -85,9 +89,13 @@ func encodePullRequestListOptions(opts scm.PullRequestListOptions) string {
if opts.Page != 0 {
params.Set("page", strconv.Itoa(opts.Page))
}
if opts.Size != 0 {
params.Set("pagelen", strconv.Itoa(opts.Size))
if opts.Size == 0 {
opts.Size = 50
}
if opts.Size > 50 {
opts.Size = 50
}
params.Set("pagelen", strconv.Itoa(opts.Size))
if opts.Open && opts.Closed {
params.Set("state", "all")
} else if opts.Closed {
Expand Down
44 changes: 44 additions & 0 deletions scm/factory/examples/combinedstatus/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package main

import (
"context"
"fmt"
"os"

"github.com/jenkins-x/go-scm/scm/factory"
"github.com/jenkins-x/go-scm/scm/factory/examples/helpers"
)

func main() {
args := os.Args
if len(args) < 2 {
fmt.Println("usage: repo ref")
os.Exit(1)
return
}
repo := args[1]
ref := "master"
if len(args) > 2 {
ref = args[2]
}

client, err := factory.NewClientFromEnvironment()
if err != nil {
helpers.Fail(err)
return
}

fmt.Printf("finding combined status in repo: %s ref: %s\n", repo, ref)

ctx := context.Background()
results, _, err := client.Repositories.FindCombinedStatus(ctx, repo, ref)
if err != nil {
helpers.Fail(err)
return
}
fmt.Printf("state: %v sha: %s\n", results.State, results.Sha)

for _, s := range results.Statuses {
fmt.Printf("target %s state %v label %s\n", s.Target, s.State, s.Label)
}
}