Skip to content

Commit

Permalink
Merge pull request jenkins-x#286 from NTTDATA-CLOUDHEDGE-POC/bitbucke…
Browse files Browse the repository at this point in the history
…t-pr-comment-support

feat: Bitbucket cloud PR comment support
  • Loading branch information
jenkins-x-bot-test authored Jun 24, 2021
2 parents cc7042f + dfb98df commit 336622b
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 15 deletions.
29 changes: 28 additions & 1 deletion scm/driver/bitbucket/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,25 @@ func (s *organizationService) Delete(context.Context, string) (*scm.Response, er
}

func (s *organizationService) IsMember(ctx context.Context, org string, user string) (bool, *scm.Response, error) {
return false, nil, scm.ErrNotSupported
path := fmt.Sprintf("2.0/workspaces/%s/permissions?q=user.account_id=\"%s\"", org, user)
result := new(organizationMemberships)
res, err := s.client.do(ctx, "GET", path, nil, result)

if err != nil {
return false, res, err
}

if len(result.Values) == 0 {
return false, res, nil
}

permission := result.Values[0].Permission

if permission == "admin" || permission == "owner" {
return true, res, nil
}

return false, res, nil
}

func (s *organizationService) IsAdmin(ctx context.Context, org string, user string) (bool, *scm.Response, error) {
Expand Down Expand Up @@ -81,6 +99,11 @@ func convertOrganizationList(from *organizationList) []*scm.Organization {
return to
}

type organizationMemberships struct {
pagination
Values []*orgMemberPermission `json:"values"`
}

type organizationList struct {
pagination
Values []*organization `json:"values"`
Expand All @@ -90,6 +113,10 @@ type organization struct {
Login string `json:"username"`
}

type orgMemberPermission struct {
Permission string `json:"permission"`
}

func convertOrganization(from *organization) *scm.Organization {
return &scm.Organization{
Name: from.Login,
Expand Down
19 changes: 15 additions & 4 deletions scm/driver/bitbucket/pr.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ type prDestination struct {
Commit struct {
Type string `json:"type"`
Ref string `json:"ref"`
Commit string `json:"Commit"`
Commit string `json:"hash"`
} `json:"commit"`
Repository repository `json:"repository"`
Branch struct {
Expand Down Expand Up @@ -327,10 +327,21 @@ type pullRequests struct {
Values []*pullRequest `json:"values"`
}

func findRefs(from *pullRequest) (string, string) {
baseRef := from.Destination.Commit.Ref
headRef := from.Source.Commit.Ref
if baseRef == "" {
baseRef = from.Destination.Branch.Name
}
if headRef == "" {
headRef = from.Source.Branch.Name
}
return baseRef, headRef
}
func convertPullRequest(from *pullRequest) *scm.PullRequest {
// TODO
fork := "false"
closed := strings.ToLower(from.State) != "open"
baseRef, headRef := findRefs(from)
return &scm.PullRequest{
Number: from.ID,
Title: from.Title,
Expand All @@ -340,8 +351,8 @@ func convertPullRequest(from *pullRequest) *scm.PullRequest {
Source: from.Source.Commit.Commit,
Target: from.Destination.Commit.Commit,
Fork: fork,
Base: convertPullRequestBranch(from.Destination.Commit.Ref, from.Destination.Commit.Commit, from.Destination.Repository),
Head: convertPullRequestBranch(from.Source.Commit.Ref, from.Source.Commit.Commit, from.Source.Repository),
Base: convertPullRequestBranch(baseRef, from.Destination.Commit.Commit, from.Destination.Repository),
Head: convertPullRequestBranch(headRef, from.Source.Commit.Commit, from.Source.Repository),
Link: from.Links.HTML.Href,
DiffLink: from.Links.Diff.Href,
State: strings.ToLower(from.State),
Expand Down
2 changes: 1 addition & 1 deletion scm/driver/bitbucket/testdata/pr_create.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"id": 2,
"destination": {
"commit": {
"Commit": "ec8d3da80fc5",
"hash": "ec8d3da80fc5",
"type": "commit",
"links": {
"self": {
Expand Down
4 changes: 2 additions & 2 deletions scm/driver/bitbucket/testdata/pr_create.json.golden
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"Source": "42f383c381d9",
"Target": "ec8d3da80fc5",
"Base": {
"Ref": "",
"Ref": "master",
"Sha": "ec8d3da80fc5",
"Repo": {
"ID": "{b7c5a1c5-28bb-4e20-b262-3beaff0dd827}",
Expand All @@ -26,7 +26,7 @@
}
},
"Head": {
"Ref": "",
"Ref": "somestuff",
"Sha": "42f383c381d9",
"Repo": {
"ID": "{b7c5a1c5-28bb-4e20-b262-3beaff0dd827}",
Expand Down
2 changes: 1 addition & 1 deletion scm/driver/bitbucket/testdata/pulls.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"id": 2,
"destination": {
"commit": {
"Commit": "ec8d3da80fc5",
"hash": "ec8d3da80fc5",
"type": "commit",
"links": {
"self": {
Expand Down
12 changes: 6 additions & 6 deletions scm/driver/bitbucket/testdata/pulls.json.golden
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"Source": "42f383c381d9",
"Target": "ec8d3da80fc5",
"Base": {
"Ref": "",
"Ref": "master",
"Sha": "ec8d3da80fc5",
"Repo": {
"ID": "{b7c5a1c5-28bb-4e20-b262-3beaff0dd827}",
Expand All @@ -27,7 +27,7 @@
}
},
"Head": {
"Ref": "",
"Ref": "somestuff",
"Sha": "42f383c381d9",
"Repo": {
"ID": "{b7c5a1c5-28bb-4e20-b262-3beaff0dd827}",
Expand Down Expand Up @@ -84,10 +84,10 @@
"Sha": "bf9b796a1dd0",
"Ref": "refs/pull-requests/1/from",
"Source": "bf9b796a1dd0",
"Target": "",
"Target": "ec8d3da80fc5",
"Base": {
"Ref": "",
"Sha": "",
"Ref": "master",
"Sha": "ec8d3da80fc5",
"Repo": {
"ID": "{b7c5a1c5-28bb-4e20-b262-3beaff0dd827}",
"Namespace": "jstrachan",
Expand All @@ -104,7 +104,7 @@
}
},
"Head": {
"Ref": "",
"Ref": "cheese",
"Sha": "bf9b796a1dd0",
"Repo": {
"ID": "{b7c5a1c5-28bb-4e20-b262-3beaff0dd827}",
Expand Down
149 changes: 149 additions & 0 deletions scm/driver/bitbucket/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ func (s *webhookService) Parse(req *http.Request, fn scm.SecretFunc) (scm.Webhoo
if hook != nil {
hook.(*scm.PullRequestHook).Action = scm.ActionClose
}
case "pullrequest:comment_created", "pullrequest:comment_updated":
hook, err = s.parsePullRequestCommentHook(data)
if hook != nil {
hook.(*scm.PullRequestCommentHook).Action = scm.ActionCreate
}
}
if err != nil {
return nil, err
Expand Down Expand Up @@ -126,6 +131,15 @@ func (s *webhookService) parsePullRequestHook(data []byte) (*scm.PullRequestHook
}
}

func (s *webhookService) parsePullRequestCommentHook(data []byte) (*scm.PullRequestCommentHook, error) {
dst := new(webhookPRComment)
err := json.Unmarshal(data, dst)
if err != nil {
return nil, err
}
return s.convertPullRequestCommentHook(dst)
}

//
// native data structures
//
Expand Down Expand Up @@ -496,6 +510,13 @@ type (
}
)

type webhookPRComment struct {
PullRequest *webhookPullRequest `json:"pullrequest"`
Comment *prComment `json:"comment"` //this struct definition is available in pr.go
Repository *webhookRepository `json:"repository"`
Actor *webhookActor `json:"actor"`
}

//
// push hooks
//
Expand Down Expand Up @@ -751,3 +772,131 @@ func (s *webhookService) convertPullRequestHook(src *webhook) (*scm.PullRequestH
}
return dst, nil
}

func (s *webhookService) convertPullRequestCommentHook(src *webhookPRComment) (*scm.PullRequestCommentHook, error) {
namespace, name := scm.Split(src.PullRequest.Source.Repository.FullName)
prRepo := scm.Repository{
ID: src.Repository.UUID,
Namespace: namespace,
Name: src.Repository.Name,
FullName: src.Repository.FullName,
Branch: src.PullRequest.Destination.Branch.Name,
Private: src.Repository.IsPrivate,
Clone: fmt.Sprintf("https://bitbucket.org/%s.git", src.Repository.FullName),
CloneSSH: fmt.Sprintf("[email protected]:%s.git", src.Repository.FullName),
Link: src.Repository.Links.HTML.Href,
}

feature_repo := scm.Repository{
ID: src.PullRequest.Source.Repository.UUID,
Namespace: namespace,
Name: name,
FullName: src.PullRequest.Source.Repository.FullName,
Branch: src.PullRequest.Source.Branch.Name,
Private: true, //(TODO) Private value is set to default(true) as this value does not come with the PR Source Repo payload
Clone: fmt.Sprintf("https://bitbucket.org/%s.git", src.PullRequest.Source.Repository.FullName),
CloneSSH: fmt.Sprintf("[email protected]:%s.git", src.PullRequest.Source.Repository.FullName),
Link: src.PullRequest.Source.Repository.Links.HTML.Href,
}
base_repo := scm.Repository{
ID: src.PullRequest.Destination.Repository.UUID,
Namespace: namespace,
Name: name,
FullName: src.PullRequest.Destination.Repository.FullName,
Branch: src.PullRequest.Destination.Branch.Name,
Private: true, //(TODO) Private value is set to default(true) as this value does not come with the PR Destination Repo payload
Clone: fmt.Sprintf("https://bitbucket.org/%s.git", src.PullRequest.Destination.Repository.FullName),
CloneSSH: fmt.Sprintf("[email protected]:%s.git", src.PullRequest.Destination.Repository.FullName),
Link: src.PullRequest.Destination.Repository.Links.HTML.Href,
}
featureSha := src.PullRequest.Source.Commit.Hash

dst := &scm.PullRequestCommentHook{
Action: scm.ActionCreate,
PullRequest: scm.PullRequest{
Number: src.PullRequest.ID,
Title: src.PullRequest.Title,
Body: src.PullRequest.Description,
Sha: featureSha,
Ref: fmt.Sprintf("refs/pull-requests/%d/from", src.PullRequest.ID),
Source: src.PullRequest.Source.Branch.Name,
Target: src.PullRequest.Destination.Branch.Name,
Fork: src.PullRequest.Source.Repository.FullName,
Link: src.PullRequest.Links.HTML.Href,
Closed: src.PullRequest.State != "OPEN",
Merged: src.PullRequest.State == "MERGED",
Author: scm.User{
Login: src.PullRequest.Author.Username,
Name: src.PullRequest.Author.DisplayName,
Avatar: src.PullRequest.Author.Links.Avatar.Href,
},
Created: src.PullRequest.CreatedOn,
Updated: src.PullRequest.UpdatedOn,
State: strings.ToLower(src.PullRequest.State),
},
Repo: prRepo,
Sender: scm.User{
Login: src.Actor.Username,
Name: src.Actor.DisplayName,
Avatar: src.Actor.Links.Avatar.Href,
},
Comment: scm.Comment{
ID: src.Comment.ID,
Body: src.Comment.Content.Raw,
Author: scm.User{
Login: src.Comment.User.AccountID,
Name: src.Comment.User.DisplayName,
Avatar: src.Comment.User.Links.Avatar.Href,
},
Created: src.Comment.CreatedOn,
Updated: src.Comment.UpdatedOn,
},
}
dst.PullRequest.Base.Repo = base_repo
dst.PullRequest.Head.Repo = feature_repo
dst.PullRequest.Base.Ref = src.PullRequest.Destination.Branch.Name
dst.PullRequest.Head.Ref = src.PullRequest.Source.Branch.Name

if len(featureSha) <= 12 && featureSha != "" && s.client != nil {
//TODO - need to consider the forking scenario to determine which Repo whould be considered for mapping "repo" variable Full name
repo := feature_repo.FullName
fullHash, _, err := s.client.Git.FindRef(context.TODO(), repo, featureSha)
if err != nil {
return nil, errors.Wrapf(err, "failed to resolve full hash %s", featureSha)
}
fullHash = strings.TrimSpace(fullHash)
if fullHash != "" {
dst.PullRequest.Sha = fullHash
}
}

if dst.PullRequest.Head.Sha == "" && dst.PullRequest.Head.Ref != "" && s.client != nil {
repo := feature_repo.FullName
fullHash, _, err := s.client.Git.FindRef(context.TODO(), repo, dst.PullRequest.Head.Ref)
if err != nil {
return nil, errors.Wrapf(err, "failed to resolve sha for ref %s", dst.PullRequest.Head.Ref)
}
fullHash = strings.TrimSpace(fullHash)
if fullHash != "" {
dst.PullRequest.Head.Sha = fullHash
}
}

if dst.PullRequest.Head.Sha == "" {
dst.PullRequest.Head.Sha = dst.PullRequest.Sha
}

masterSha := src.PullRequest.Destination.Commit.Hash
if len(masterSha) <= 12 && masterSha != "" && s.client != nil {
repo := base_repo.FullName
fullHash, _, err := s.client.Git.FindRef(context.TODO(), repo, masterSha)
if err != nil {
return nil, errors.Wrapf(err, "failed to resolve full hash %s", masterSha)
}
fullHash = strings.TrimSpace(fullHash)
if fullHash != "" {
dst.PullRequest.Base.Sha = fullHash
}
}
return dst, nil
}

0 comments on commit 336622b

Please sign in to comment.