You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
go-scm/scm/driver/bitbucket/webhook.go

905 lines
26 KiB
Go

// Copyright 2017 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bitbucket
import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"time"
"git.awesome-for.me/liuzhiguo/go-scm/scm"
)
// TODO(bradrydzewski) default repository branch is missing in push webhook payloads
// TODO(bradrydzewski) default repository branch is missing in pr webhook payloads
// TODO(bradrydzewski) default repository is_private is missing in pr webhook payloads
type webhookService struct {
client *wrapper
}
func (s *webhookService) Parse(req *http.Request, fn scm.SecretFunc) (scm.Webhook, error) {
data, err := ioutil.ReadAll(
io.LimitReader(req.Body, 10000000),
)
if err != nil {
return nil, err
}
var hook scm.Webhook
switch req.Header.Get("x-event-key") {
case "repo:push":
hook, err = s.parsePushHook(data)
case "pullrequest:created":
hook, err = s.parsePullRequestHook(data)
case "pullrequest:updated":
hook, err = s.parsePullRequestHook(data)
if hook != nil {
hook.(*scm.PullRequestHook).Action = scm.ActionSync
}
case "pullrequest:fulfilled":
hook, err = s.parsePullRequestHook(data)
if hook != nil {
hook.(*scm.PullRequestHook).Action = scm.ActionMerge
}
case "pullrequest:rejected":
hook, err = s.parsePullRequestHook(data)
if hook != nil {
hook.(*scm.PullRequestHook).Action = scm.ActionClose
}
case "pullrequest:comment_created":
hook, err = s.parsePullRequestCommentHook(data)
if hook != nil {
hook.(*scm.IssueCommentHook).Action = scm.ActionCreate
}
case "pullrequest:comment_updated":
// Bitbucket PR Comment Update is unreliable and does not send events
// most of the time https://github.com/iterative/cml/issues/817
hook, err = s.parsePullRequestCommentHook(data)
if hook != nil {
hook.(*scm.IssueCommentHook).Action = scm.ActionEdit
}
case "pullrequest:comment_deleted":
hook, err = s.parsePullRequestCommentHook(data)
if hook != nil {
hook.(*scm.IssueCommentHook).Action = scm.ActionDelete
}
}
if err != nil {
return nil, err
}
if hook == nil {
return nil, nil
}
// get the gogs signature key to verify the payload
// signature. If no key is provided, no validation
// is performed.
key, err := fn(hook)
if err != nil {
return hook, err
} else if key == "" {
return hook, nil
}
if req.FormValue("secret") != key {
return hook, scm.ErrSignatureInvalid
}
return hook, nil
}
func (s *webhookService) parsePullRequestCommentHook(data []byte) (scm.Webhook, error) {
dst := new(prCommentHook)
err := json.Unmarshal(data, dst)
return convertPrCommentHook(dst), err
}
func (s *webhookService) parsePushHook(data []byte) (scm.Webhook, error) {
dst := new(pushHook)
err := json.Unmarshal(data, dst)
if err != nil {
return nil, err
}
if len(dst.Push.Changes) == 0 {
return nil, errors.New("Push hook has empty changeset")
}
change := dst.Push.Changes[0]
switch {
// case change.New.Type == "branch" && change.Created:
// return convertBranchCreateHook(dst), nil
case change.Old.Type == "branch" && change.Closed:
return convertBranchDeleteHook(dst), nil
// case change.New.Type == "tag" && change.Created:
// return convertTagCreateHook(dst), nil
case change.Old.Type == "tag" && change.Closed:
return convertTagDeleteHook(dst), nil
default:
return convertPushHook(dst), err
}
}
func (s *webhookService) parsePullRequestHook(data []byte) (*scm.PullRequestHook, error) {
dst := new(webhook)
err := json.Unmarshal(data, dst)
if err != nil {
return nil, err
}
switch {
default:
return convertPullRequestHook(dst), err
}
}
//
// native data structures
//
type (
pushHook struct {
Push struct {
Changes []struct {
Forced bool `json:"forced"`
Old struct {
Type string `json:"type"`
Name string `json:"name"`
Links struct {
Commits struct {
Href string `json:"href"`
} `json:"commits"`
Self struct {
Href string `json:"href"`
} `json:"self"`
HTML struct {
Href string `json:"href"`
} `json:"html"`
} `json:"links"`
Target struct {
Hash string `json:"hash"`
Links struct {
Self struct {
Href string `json:"href"`
} `json:"self"`
HTML struct {
Href string `json:"href"`
} `json:"html"`
} `json:"links"`
Author struct {
Raw string `json:"raw"`
Type string `json:"type"`
User struct {
Username string `json:"username"`
DisplayName string `json:"display_name"`
AccountID string `json:"account_id"`
Links struct {
Self struct {
Href string `json:"href"`
} `json:"self"`
HTML struct {
Href string `json:"href"`
} `json:"html"`
Avatar struct {
Href string `json:"href"`
} `json:"avatar"`
} `json:"links"`
Type string `json:"type"`
UUID string `json:"uuid"`
} `json:"user"`
} `json:"author"`
Summary struct {
Raw string `json:"raw"`
Markup string `json:"markup"`
HTML string `json:"html"`
Type string `json:"type"`
} `json:"summary"`
Parents []interface{} `json:"parents"`
Date time.Time `json:"date"`
Message string `json:"message"`
Type string `json:"type"`
} `json:"target"`
} `json:"old"`
Links struct {
Commits struct {
Href string `json:"href"`
} `json:"commits"`
HTML struct {
Href string `json:"href"`
} `json:"html"`
Diff struct {
Href string `json:"href"`
} `json:"diff"`
} `json:"links"`
Truncated bool `json:"truncated"`
Commits []struct {
Hash string `json:"hash"`
Links struct {
Self struct {
Href string `json:"href"`
} `json:"self"`
Comments struct {
Href string `json:"href"`
} `json:"comments"`
Patch struct {
Href string `json:"href"`
} `json:"patch"`
HTML struct {
Href string `json:"href"`
} `json:"html"`
Diff struct {
Href string `json:"href"`
} `json:"diff"`
Approve struct {
Href string `json:"href"`
} `json:"approve"`
Statuses struct {
Href string `json:"href"`
} `json:"statuses"`
} `json:"links"`
Author struct {
Raw string `json:"raw"`
Type string `json:"type"`
User struct {
Username string `json:"username"`
DisplayName string `json:"display_name"`
AccountID string `json:"account_id"`
Links struct {
Self struct {
Href string `json:"href"`
} `json:"self"`
HTML struct {
Href string `json:"href"`
} `json:"html"`
Avatar struct {
Href string `json:"href"`
} `json:"avatar"`
} `json:"links"`
Type string `json:"type"`
UUID string `json:"uuid"`
} `json:"user"`
} `json:"author"`
Summary struct {
Raw string `json:"raw"`
Markup string `json:"markup"`
HTML string `json:"html"`
Type string `json:"type"`
} `json:"summary"`
Parents []struct {
Type string `json:"type"`
Hash string `json:"hash"`
Links struct {
Self struct {
Href string `json:"href"`
} `json:"self"`
HTML struct {
Href string `json:"href"`
} `json:"html"`
} `json:"links"`
} `json:"parents"`
Date time.Time `json:"date"`
Message string `json:"message"`
Type string `json:"type"`
} `json:"commits"`
Created bool `json:"created"`
Closed bool `json:"closed"`
New struct {
Type string `json:"type"`
Name string `json:"name"`
Links struct {
Commits struct {
Href string `json:"href"`
} `json:"commits"`
Self struct {
Href string `json:"href"`
} `json:"self"`
HTML struct {
Href string `json:"href"`
} `json:"html"`
} `json:"links"`
Target struct {
Hash string `json:"hash"`
Links struct {
Self struct {
Href string `json:"href"`
} `json:"self"`
HTML struct {
Href string `json:"href"`
} `json:"html"`
} `json:"links"`
Author struct {
Raw string `json:"raw"`
Type string `json:"type"`
User struct {
Username string `json:"username"`
DisplayName string `json:"display_name"`
AccountID string `json:"account_id"`
Links struct {
Self struct {
Href string `json:"href"`
} `json:"self"`
HTML struct {
Href string `json:"href"`
} `json:"html"`
Avatar struct {
Href string `json:"href"`
} `json:"avatar"`
} `json:"links"`
Type string `json:"type"`
UUID string `json:"uuid"`
} `json:"user"`
} `json:"author"`
Summary struct {
Raw string `json:"raw"`
Markup string `json:"markup"`
HTML string `json:"html"`
Type string `json:"type"`
} `json:"summary"`
Parents []struct {
Type string `json:"type"`
Hash string `json:"hash"`
Links struct {
Self struct {
Href string `json:"href"`
} `json:"self"`
HTML struct {
Href string `json:"href"`
} `json:"html"`
} `json:"links"`
} `json:"parents"`
Date time.Time `json:"date"`
Message string `json:"message"`
Type string `json:"type"`
} `json:"target"`
} `json:"new"`
} `json:"changes"`
} `json:"push"`
Repository webhookRepository `json:"repository"`
Actor webhookActor `json:"actor"`
}
webhook struct {
PullRequest pr `json:"pullrequest"`
Repository webhookRepository `json:"repository"`
Actor webhookActor `json:"actor"`
}
webhookRepository struct {
Scm string `json:"scm"`
Name string `json:"name"`
Links struct {
HTML struct {
Href string `json:"href"`
} `json:"html"`
} `json:"links"`
FullName string `json:"full_name"`
Owner struct {
Username string `json:"username"`
DisplayName string `json:"display_name"`
AccountID string `json:"account_id"`
Links struct {
HTML struct {
Href string `json:"href"`
} `json:"html"`
} `json:"links"`
UUID string `json:"uuid"`
} `json:"owner"`
IsPrivate bool `json:"is_private"`
UUID string `json:"uuid"`
}
webhookActor struct {
Username string `json:"username"`
DisplayName string `json:"display_name"`
AccountID string `json:"account_id"`
Links struct {
Avatar struct {
Href string `json:"href"`
} `json:"avatar"`
} `json:"links"`
UUID string `json:"uuid"`
}
prCommentInput struct {
Content struct {
Raw string `json:"raw"`
} `json:"content"`
}
prComment struct {
Links struct {
Self link `json:"self"`
HTML link `json:"html"`
} `json:"links"`
Deleted bool `json:"deleted"`
PullRequest struct {
Type string `json:"type"`
ID int `json:"id"`
Links struct {
Self link `json:"self"`
HTML link `json:"html"`
} `json:"links"`
Title string `json:"title"`
}
Content struct {
Raw string `json:"raw"`
Markup string `json:"markup"`
Html string `json:"html"`
Type string `json:"type"`
}
CreatedOn time.Time `json:"created_on"`
User prCommentHookUser `json:"user"`
UpdatedOn time.Time `json:"updated_on"`
Type string `json:"type"`
ID int `json:"id"`
}
prCommentHookRepo struct {
Scm string `json:"scm"`
Website string `json:"website"`
UUID string `json:"uuid"`
Links struct {
Self link `json:"self"`
HTML link `json:"html"`
Avatar link `json:"avatar"`
} `json:"links"`
Project struct {
Links struct {
Self link `json:"self"`
HTML link `json:"html"`
Avatar link `json:"avatar"`
} `json:"links"`
Type string `json:"type"`
Name string `json:"name"`
Key string `json:"key"`
UUID string `json:"uuid"`
} `json:"project"`
FullName string `json:"full_name"`
Owner prCommentHookUser `json:"owner"`
Workspace struct {
Slug string `json:"slugg"`
Type string `json:"type"`
Name string `json:"name"`
Links struct {
Self link `json:"self"`
HTML link `json:"html"`
Avatar link `json:"avatar"`
} `json:"links"`
UUID string `json:"uuid"`
} `json:"workspace"`
Type string `json:"type"`
IsPrivate bool `json:"is_private"`
Name string `json:"name"`
}
prCommentHookUser struct {
Username string `json:"username"`
DisplayName string `json:"display_name"`
UUID string `json:"uuid"`
Links struct {
Self link `json:"self"`
HTML link `json:"html"`
Avatar link `json:"avatar"`
} `json:"links"`
Type string `json:"type"`
Nickname string `json:"nickname"`
AccountID string `json:"account_id"`
}
prCommentHookPullRequest struct {
Rendered struct {
Description struct {
Raw string `json:"raw"`
Markup string `json:"markup"`
Html string `json:"html"`
Type string `json:"type"`
} `json:"description"`
Title struct {
Raw string `json:"raw"`
Markup string `json:"markup"`
Html string `json:"html"`
Type string `json:"type"`
} `json:"title"`
} `json:"rendered"`
Type string `json:"type"`
Description string `json:"description"`
Links struct {
Decline link `json:"decline"`
Diffstat link `json:"diffstat"`
Commits link `json:"commits"`
Self link `json:"self"`
Comments link `json:"comments"`
Merge link `json:"merge"`
Html link `json:"html"`
Activity link `json:"activity"`
RequestChanges link `json:"request-changes"`
Diff link `json:"diff"`
Approve link `json:"approve"`
Statuses link `json:"statuses"`
} `json:"links"`
Title string `json:"title"`
CloseSourceBranch bool `json:"close_source_branch"`
Reviewers []interface{} `json:"reviewers"`
ID int `json:"id"`
Destination struct {
Commit struct {
Hash string `json:"hash"`
Type string `json:"type"`
Links struct {
Self link `json:"self"`
HTML link `json:"html"`
} `json:"links"`
}
Repository struct {
Links struct {
Self link `json:"self"`
HTML link `json:"html"`
Avatar link `json:"avatar"`
} `json:"links"`
Type string `json:"type"`
Name string `json:"name"`
FullName string `json:"full_name"`
UUID string `json:"uuid"`
} `json:"repository"`
Branch struct {
Name string `json:"name"`
} `json:"branch"`
} `json:"destination"`
CreatedOn time.Time `json:"created_on"`
Summary struct {
Raw string `json:"raw"`
Markup string `json:"markup"`
Html string `json:"html"`
Type string `json:"type"`
} `json:"summary"`
Source struct {
Commit struct {
Hash string `json:"hash"`
Type string `json:"type"`
Links struct {
Self link `json:"self"`
HTML link `json:"html"`
} `json:"links"`
}
Repository struct {
Links struct {
Self link `json:"self"`
HTML link `json:"html"`
Avatar link `json:"avatar"`
} `json:"links"`
Type string `json:"type"`
Name string `json:"name"`
FullName string `json:"full_name"`
UUID string `json:"uuid"`
} `json:"repository"`
Branch struct {
Name string `json:"name"`
} `json:"branch"`
} `json:"source"`
CommentCount int `json:"comment_count"`
State string `json:"state"`
TaskCount int `json:"task_count"`
Participants []interface{} `json:"participants"`
Reason string `json:"reason"`
UpdatedOn time.Time `json:"updated_on"`
Author prCommentHookUser `json:"author"`
MergeCommit interface{} `json:"merge_commit"`
ClosedBy interface{} `json:"closed_by"`
}
prCommentHook struct {
Comment prComment `json:"comment"`
PullRequest prCommentHookPullRequest `json:"pullRequest"`
Repository prCommentHookRepo `json:"repository"`
Actor prCommentHookUser `json:"actor"`
}
)
//
// push hooks
//
func convertPushHook(src *pushHook) *scm.PushHook {
change := src.Push.Changes[0]
var commits []scm.Commit
for _, c := range change.Commits {
commits = append(commits,
scm.Commit{
Sha: c.Hash,
Message: c.Message,
Link: c.Links.HTML.Href,
Author: scm.Signature{
Login: c.Author.User.Username,
Email: extractEmail(c.Author.Raw),
Name: c.Author.User.DisplayName,
Avatar: c.Author.User.Links.Avatar.Href,
Date: c.Date,
},
Committer: scm.Signature{
Login: c.Author.User.Username,
Email: extractEmail(c.Author.Raw),
Name: c.Author.User.DisplayName,
Avatar: c.Author.User.Links.Avatar.Href,
Date: c.Date,
},
})
}
namespace, name := scm.Split(src.Repository.FullName)
dst := &scm.PushHook{
Ref: scm.ExpandRef(change.New.Name, "refs/heads/"),
Before: change.Old.Target.Hash,
After: change.New.Target.Hash,
Commit: scm.Commit{
Sha: change.New.Target.Hash,
Message: change.New.Target.Message,
Link: change.New.Target.Links.HTML.Href,
Author: scm.Signature{
Login: change.New.Target.Author.User.Username,
Email: extractEmail(change.New.Target.Author.Raw),
Name: change.New.Target.Author.User.DisplayName,
Avatar: change.New.Target.Author.User.Links.Avatar.Href,
Date: change.New.Target.Date,
},
Committer: scm.Signature{
Login: change.New.Target.Author.User.Username,
Email: extractEmail(change.New.Target.Author.Raw),
Name: change.New.Target.Author.User.DisplayName,
Avatar: change.New.Target.Author.User.Links.Avatar.Href,
Date: change.New.Target.Date,
},
},
Repo: scm.Repository{
ID: src.Repository.UUID,
Namespace: namespace,
Name: name,
Private: src.Repository.IsPrivate,
Clone: fmt.Sprintf("https://bitbucket.org/%s.git", src.Repository.FullName),
CloneSSH: fmt.Sprintf("git@bitbucket.org:%s.git", src.Repository.FullName),
Link: src.Repository.Links.HTML.Href,
},
Sender: scm.User{
ID: src.Actor.UUID,
Login: src.Actor.Username,
Name: src.Actor.DisplayName,
Avatar: src.Actor.Links.Avatar.Href,
},
Commits: commits,
}
if change.New.Type == "tag" {
dst.Ref = scm.ExpandRef(change.New.Name, "refs/tags/")
}
return dst
}
func convertBranchCreateHook(src *pushHook) *scm.BranchHook {
namespace, name := scm.Split(src.Repository.FullName)
change := src.Push.Changes[0].New
action := scm.ActionCreate
return &scm.BranchHook{
Action: action,
Ref: scm.Reference{
Name: change.Name,
Sha: change.Target.Hash,
},
Repo: scm.Repository{
ID: src.Repository.UUID,
Namespace: namespace,
Name: name,
Private: src.Repository.IsPrivate,
Clone: fmt.Sprintf("https://bitbucket.org/%s.git", src.Repository.FullName),
CloneSSH: fmt.Sprintf("git@bitbucket.org:%s.git", src.Repository.FullName),
Link: src.Repository.Links.HTML.Href,
},
Sender: scm.User{
ID: src.Actor.UUID,
Login: src.Actor.Username,
Name: src.Actor.DisplayName,
Avatar: src.Actor.Links.Avatar.Href,
},
}
}
func convertBranchDeleteHook(src *pushHook) *scm.BranchHook {
namespace, name := scm.Split(src.Repository.FullName)
change := src.Push.Changes[0].Old
action := scm.ActionDelete
return &scm.BranchHook{
Action: action,
Ref: scm.Reference{
Name: change.Name,
Sha: change.Target.Hash,
},
Repo: scm.Repository{
ID: src.Repository.UUID,
Namespace: namespace,
Name: name,
Private: src.Repository.IsPrivate,
Clone: fmt.Sprintf("https://bitbucket.org/%s.git", src.Repository.FullName),
CloneSSH: fmt.Sprintf("git@bitbucket.org:%s.git", src.Repository.FullName),
Link: src.Repository.Links.HTML.Href,
},
Sender: scm.User{
ID: src.Actor.UUID,
Login: src.Actor.Username,
Name: src.Actor.DisplayName,
Avatar: src.Actor.Links.Avatar.Href,
},
}
}
func convertTagCreateHook(src *pushHook) *scm.TagHook {
namespace, name := scm.Split(src.Repository.FullName)
change := src.Push.Changes[0].New
action := scm.ActionCreate
return &scm.TagHook{
Action: action,
Ref: scm.Reference{
Name: change.Name,
Sha: change.Target.Hash,
},
Repo: scm.Repository{
ID: src.Repository.UUID,
Namespace: namespace,
Name: name,
Private: src.Repository.IsPrivate,
Clone: fmt.Sprintf("https://bitbucket.org/%s.git", src.Repository.FullName),
CloneSSH: fmt.Sprintf("git@bitbucket.org:%s.git", src.Repository.FullName),
Link: src.Repository.Links.HTML.Href,
},
Sender: scm.User{
ID: src.Actor.UUID,
Login: src.Actor.Username,
Name: src.Actor.DisplayName,
Avatar: src.Actor.Links.Avatar.Href,
},
}
}
func convertTagDeleteHook(src *pushHook) *scm.TagHook {
namespace, name := scm.Split(src.Repository.FullName)
change := src.Push.Changes[0].Old
action := scm.ActionDelete
return &scm.TagHook{
Action: action,
Ref: scm.Reference{
Name: change.Name,
Sha: change.Target.Hash,
},
Repo: scm.Repository{
ID: src.Repository.UUID,
Namespace: namespace,
Name: name,
Private: src.Repository.IsPrivate,
Clone: fmt.Sprintf("https://bitbucket.org/%s.git", src.Repository.FullName),
CloneSSH: fmt.Sprintf("git@bitbucket.org:%s.git", src.Repository.FullName),
Link: src.Repository.Links.HTML.Href,
},
Sender: scm.User{
ID: src.Actor.UUID,
Login: src.Actor.Username,
Name: src.Actor.DisplayName,
Avatar: src.Actor.Links.Avatar.Href,
},
}
}
//
// pull request hooks
//
func convertPullRequestHook(src *webhook) *scm.PullRequestHook {
namespace, name := scm.Split(src.Repository.FullName)
return &scm.PullRequestHook{
Action: scm.ActionOpen,
PullRequest: scm.PullRequest{
Number: src.PullRequest.ID,
Title: src.PullRequest.Title,
Body: src.PullRequest.Description,
Sha: src.PullRequest.Source.Commit.Hash,
Merge: src.PullRequest.MergeCommit.Hash,
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,
},
Repo: scm.Repository{
ID: src.Repository.UUID,
Namespace: namespace,
Name: name,
Private: src.Repository.IsPrivate,
Clone: fmt.Sprintf("https://bitbucket.org/%s.git", src.Repository.FullName),
CloneSSH: fmt.Sprintf("git@bitbucket.org:%s.git", src.Repository.FullName),
Link: src.Repository.Links.HTML.Href,
},
Sender: scm.User{
ID: src.Actor.UUID,
Login: src.Actor.Username,
Name: src.Actor.DisplayName,
Avatar: src.Actor.Links.Avatar.Href,
},
}
}
func convertPrCommentHook(src *prCommentHook) *scm.IssueCommentHook {
namespace, _ := scm.Split(src.Repository.FullName)
dst := scm.IssueCommentHook{
Repo: scm.Repository{
ID: src.Repository.UUID,
Namespace: namespace,
Name: src.Repository.Name,
Clone: fmt.Sprintf("https://bitbucket.org/%s.git", src.Repository.FullName),
CloneSSH: fmt.Sprintf("git@bitbucket.org:%s.git", src.Repository.FullName),
Link: src.Repository.Links.HTML.Href,
Private: src.Repository.IsPrivate,
},
Issue: scm.Issue{
Number: src.PullRequest.ID,
Title: src.PullRequest.Title,
Body: src.PullRequest.Description,
Link: src.PullRequest.Links.Html.Href,
Author: scm.User{
Login: src.PullRequest.Author.Username,
Name: src.PullRequest.Author.DisplayName,
Avatar: src.PullRequest.Author.Links.Avatar.Href,
},
PullRequest: scm.PullRequest{
Number: src.PullRequest.ID,
Title: src.PullRequest.Title,
Body: src.PullRequest.Description,
Sha: src.PullRequest.Source.Commit.Hash,
// Bitbucket does not support PR Refs: https://jira.atlassian.com/browse/BCLOUD-5814
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,
},
Created: src.PullRequest.CreatedOn,
Updated: src.PullRequest.UpdatedOn,
},
Comment: scm.Comment{
ID: src.Comment.ID,
Body: src.Comment.Content.Raw,
Author: scm.User{
ID: src.Comment.User.UUID,
Login: src.Comment.User.Username,
Name: src.Comment.User.DisplayName,
Avatar: src.Comment.User.Links.Avatar.Href,
},
Created: src.Comment.CreatedOn,
Updated: src.Comment.UpdatedOn,
},
Sender: scm.User{
ID: src.Actor.UUID,
Login: src.Actor.Username,
Name: src.Actor.DisplayName,
Avatar: src.Actor.Links.Avatar.Href,
},
}
return &dst
}