diff --git a/scm/driver/harness/git.go b/scm/driver/harness/git.go index b66562797..9186112e6 100644 --- a/scm/driver/harness/git.go +++ b/scm/driver/harness/git.go @@ -21,8 +21,11 @@ type gitService struct { func (s *gitService) CreateBranch(ctx context.Context, repo string, params *scm.ReferenceInput) (*scm.Response, error) { harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo) path := fmt.Sprintf("api/v1/repos/%s/branches", harnessURI) - out := new(branch) - return s.client.do(ctx, "GET", path, nil, out) + in := &branchInput{ + Name: params.Name, + Target: params.Sha, + } + return s.client.do(ctx, "POST", path, in, nil) } func (s *gitService) FindBranch(ctx context.Context, repo, name string) (*scm.Reference, *scm.Response, error) { @@ -106,6 +109,10 @@ type ( Sha string `json:"sha"` Title string `json:"title"` } + branchInput struct { + Name string `json:"name"` + Target string `json:"target"` + } branch struct { Commit struct { Author struct { diff --git a/scm/driver/harness/git_test.go b/scm/driver/harness/git_test.go index 133905253..016d65e49 100644 --- a/scm/driver/harness/git_test.go +++ b/scm/driver/harness/git_test.go @@ -173,3 +173,37 @@ func TestListBranches(t *testing.T) { t.Log(diff) } } + +func TestCreateBranch(t *testing.T) { + + defer gock.Off() + + gock.New(gockOrigin). + Post("/gateway/code/api/v1/repos/px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/thomas/+/branches"). + Reply(200). + Type("application/json"). + File("testdata/branch.json") + + client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject) + client.Client = &http.Client{ + Transport: &transport.Custom{ + Before: func(r *http.Request) { + r.Header.Set("x-api-key", harnessPAT) + }, + }, + } + input := &scm.ReferenceInput{ + Name: "test", + Sha: "e8ef0374ca0cee8048e94b28eaf0d9e2e2515a14", + } + result, err := client.Git.CreateBranch(context.Background(), harnessRepo, input) + if err != nil { + t.Error(err) + return + } + + if result.Status != 200 { + t.Errorf("Unexpected Results") + } + +} diff --git a/scm/driver/harness/issue.go b/scm/driver/harness/issue.go index 6eab881c5..99d904791 100644 --- a/scm/driver/harness/issue.go +++ b/scm/driver/harness/issue.go @@ -6,7 +6,6 @@ package harness import ( "context" - "time" "github.com/drone/go-scm/scm" ) @@ -25,17 +24,14 @@ func (s *issueService) FindComment(ctx context.Context, repo string, index, id i func (s *issueService) List(ctx context.Context, repo string, opts scm.IssueListOptions) ([]*scm.Issue, *scm.Response, error) { return nil, nil, scm.ErrNotSupported - } func (s *issueService) ListComments(ctx context.Context, repo string, index int, opts scm.ListOptions) ([]*scm.Comment, *scm.Response, error) { return nil, nil, scm.ErrNotSupported - } func (s *issueService) Create(ctx context.Context, repo string, input *scm.IssueInput) (*scm.Issue, *scm.Response, error) { return nil, nil, scm.ErrNotSupported - } func (s *issueService) CreateComment(ctx context.Context, repo string, index int, input *scm.CommentInput) (*scm.Comment, *scm.Response, error) { @@ -57,91 +53,3 @@ func (s *issueService) Lock(ctx context.Context, repo string, number int) (*scm. func (s *issueService) Unlock(ctx context.Context, repo string, number int) (*scm.Response, error) { return nil, scm.ErrNotSupported } - -// -// native data structures -// - -type ( - // gitea issue response object. - issue struct { - ID int `json:"id"` - Number int `json:"number"` - User user `json:"user"` - Title string `json:"title"` - Body string `json:"body"` - State string `json:"state"` - Labels []string `json:"labels"` - Comments int `json:"comments"` - Created time.Time `json:"created_at"` - Updated time.Time `json:"updated_at"` - PullRequest *struct { - Merged bool `json:"merged"` - MergedAt interface{} `json:"merged_at"` - } `json:"pull_request"` - } - - // gitea issue request object. - issueInput struct { - Title string `json:"title"` - Body string `json:"body"` - } - - // gitea issue comment response object. - issueComment struct { - ID int `json:"id"` - HTMLURL string `json:"html_url"` - User user `json:"user"` - Body string `json:"body"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - } - - // gitea issue comment request object. - issueCommentInput struct { - Body string `json:"body"` - } -) - -// -// native data structure conversion -// - -func convertIssueList(from []*issue) []*scm.Issue { - to := []*scm.Issue{} - for _, v := range from { - to = append(to, convertIssue(v)) - } - return to -} - -func convertIssue(from *issue) *scm.Issue { - return &scm.Issue{ - Number: from.Number, - Title: from.Title, - Body: from.Body, - Link: "", // TODO construct the link to the issue. - Closed: from.State == "closed", - Author: *convertUser(&from.User), - Created: from.Created, - Updated: from.Updated, - } -} - -func convertIssueCommentList(from []*issueComment) []*scm.Comment { - to := []*scm.Comment{} - for _, v := range from { - to = append(to, convertIssueComment(v)) - } - return to -} - -func convertIssueComment(from *issueComment) *scm.Comment { - return &scm.Comment{ - ID: from.ID, - Body: from.Body, - Author: *convertUser(&from.User), - Created: from.CreatedAt, - Updated: from.UpdatedAt, - } -} diff --git a/scm/driver/harness/pr.go b/scm/driver/harness/pr.go index 631d994b7..6e0266a0d 100644 --- a/scm/driver/harness/pr.go +++ b/scm/driver/harness/pr.go @@ -17,7 +17,11 @@ type pullService struct { } func (s *pullService) Find(ctx context.Context, repo string, index int) (*scm.PullRequest, *scm.Response, error) { - return nil, nil, scm.ErrNotSupported + harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo) + path := fmt.Sprintf("api/v1/repos/%s/pullreq/%d", harnessURI, index) + out := new(pr) + res, err := s.client.do(ctx, "GET", path, nil, out) + return convertPullRequest(out), res, err } @@ -33,8 +37,12 @@ func (s *pullService) ListComments(context.Context, string, int, scm.ListOptions return nil, nil, scm.ErrNotSupported } -func (s *pullService) ListCommits(context.Context, string, int, scm.ListOptions) ([]*scm.Commit, *scm.Response, error) { - return nil, nil, scm.ErrNotSupported +func (s *pullService) ListCommits(ctx context.Context, repo string, index int, opts scm.ListOptions) ([]*scm.Commit, *scm.Response, error) { + harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo) + path := fmt.Sprintf("api/v1/repos/%s/pullreq/%d/commits?%s", harnessURI, index, encodeListOptions(opts)) + out := []*commit{} + res, err := s.client.do(ctx, "GET", path, nil, &out) + return convertCommits(out), res, err } func (s *pullService) ListChanges(context.Context, string, int, scm.ListOptions) ([]*scm.Change, *scm.Response, error) { @@ -42,7 +50,17 @@ func (s *pullService) ListChanges(context.Context, string, int, scm.ListOptions) } func (s *pullService) Create(ctx context.Context, repo string, input *scm.PullRequestInput) (*scm.PullRequest, *scm.Response, error) { - return nil, nil, scm.ErrNotSupported + harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo) + path := fmt.Sprintf("api/v1/repos/%s/pullreq", harnessURI) + in := &prInput{ + Title: input.Title, + Description: input.Body, + SourceBranch: input.Source, + TargetBranch: input.Target, + } + out := new(pr) + res, err := s.client.do(ctx, "POST", path, in, out) + return convertPullRequest(out), res, err } func (s *pullService) CreateComment(context.Context, string, int, *scm.CommentInput) (*scm.Comment, *scm.Response, error) { @@ -61,52 +79,86 @@ func (s *pullService) Close(context.Context, string, int) (*scm.Response, error) return nil, scm.ErrNotSupported } -// // native data structures -// - -type pr struct { - ID int `json:"id"` - Number int `json:"number"` - User user `json:"user"` - Title string `json:"title"` - Body string `json:"body"` - State string `json:"state"` - HeadBranch string `json:"head_branch"` - HeadRepo repository `json:"head_repo"` - Head reference `json:"head"` - BaseBranch string `json:"base_branch"` - BaseRepo repository `json:"base_repo"` - Base reference `json:"base"` - HTMLURL string `json:"html_url"` - DiffURL string `json:"diff_url"` - Mergeable bool `json:"mergeable"` - Merged bool `json:"merged"` - Created time.Time `json:"created_at"` - Updated time.Time `json:"updated_at"` - Labels []struct { - Name string `json:"name"` - Color string `json:"color"` - } `json:"labels"` -} - -type reference struct { - Repo repository `json:"repo"` - Name string `json:"ref"` - Sha string `json:"sha"` -} - -type prInput struct { - Title string `json:"title"` - Body string `json:"body"` - Head string `json:"head"` - Base string `json:"base"` -} - -// -// native data structure conversion -// +type ( + pr struct { + Author struct { + Created int `json:"created"` + DisplayName string `json:"display_name"` + Email string `json:"email"` + ID int `json:"id"` + Type string `json:"type"` + UID string `json:"uid"` + Updated int `json:"updated"` + } `json:"author"` + Created int `json:"created"` + Description string `json:"description"` + Edited int `json:"edited"` + IsDraft bool `json:"is_draft"` + MergeBaseSha string `json:"merge_base_sha"` + MergeHeadSha string `json:"merge_head_sha"` + MergeStrategy string `json:"merge_strategy"` + Merged int `json:"merged"` + Merger struct { + Created int `json:"created"` + DisplayName string `json:"display_name"` + Email string `json:"email"` + ID int `json:"id"` + Type string `json:"type"` + UID string `json:"uid"` + Updated int `json:"updated"` + } `json:"merger"` + Number int `json:"number"` + SourceBranch string `json:"source_branch"` + SourceRepoID int `json:"source_repo_id"` + State string `json:"state"` + Stats struct { + Commits int `json:"commits"` + Conversations int `json:"conversations"` + FilesChanged int `json:"files_changed"` + } `json:"stats"` + TargetBranch string `json:"target_branch"` + TargetRepoID int `json:"target_repo_id"` + Title string `json:"title"` + } + reference struct { + Repo repository `json:"repo"` + Name string `json:"ref"` + Sha string `json:"sha"` + } + + prInput struct { + Description string `json:"description"` + IsDraft bool `json:"is_draft"` + SourceBranch string `json:"source_branch"` + SourceRepoRef string `json:"source_repo_ref"` + TargetBranch string `json:"target_branch"` + Title string `json:"title"` + } + + commit struct { + Author struct { + Identity struct { + Email string `json:"email"` + Name string `json:"name"` + } `json:"identity"` + When time.Time `json:"when"` + } `json:"author"` + Committer struct { + Identity struct { + Email string `json:"email"` + Name string `json:"name"` + } `json:"identity"` + When time.Time `json:"when"` + } `json:"committer"` + Message string `json:"message"` + Sha string `json:"sha"` + Title string `json:"title"` + } +) + +// native data structure conversion func convertPullRequests(src []*pr) []*scm.PullRequest { dst := []*scm.PullRequest{} for _, v := range src { @@ -116,42 +168,44 @@ func convertPullRequests(src []*pr) []*scm.PullRequest { } func convertPullRequest(src *pr) *scm.PullRequest { - var labels []scm.Label - for _, label := range src.Labels { - labels = append(labels, scm.Label{ - Name: label.Name, - Color: label.Color, - }) - } return &scm.PullRequest{ - Number: src.Number, - Title: src.Title, - Body: src.Body, - Sha: src.Head.Sha, - Source: src.Head.Name, - Target: src.Base.Name, - Link: src.HTMLURL, - Diff: src.DiffURL, - Fork: "fork", - Ref: fmt.Sprintf("refs/pull/%d/head", src.Number), - Closed: src.State == "closed", - Author: *convertUser(&src.User), - Merged: src.Merged, - Created: src.Created, - Updated: src.Updated, - Labels: labels, + Number: src.Number, + Title: src.Title, + Body: src.Description, + Source: src.SourceBranch, + Target: src.TargetBranch, + Merged: src.Merged != 0, + Author: scm.User{ + Login: src.Author.Email, + Name: src.Author.DisplayName, + ID: src.Author.UID, + Email: src.Author.Email, + }, + Fork: "fork", + Ref: fmt.Sprintf("refs/pull/%d/head", src.Number), + Closed: src.State == "closed", } } -func convertPullRequestFromIssue(src *issue) *scm.PullRequest { - return &scm.PullRequest{ - Number: src.Number, - Title: src.Title, - Body: src.Body, - Closed: src.State == "closed", - Author: *convertUser(&src.User), - Merged: src.PullRequest.Merged, - Created: src.Created, - Updated: src.Updated, +func convertCommits(src []*commit) []*scm.Commit { + dst := []*scm.Commit{} + for _, v := range src { + dst = append(dst, convertCommit(v)) + } + return dst +} + +func convertCommit(src *commit) *scm.Commit { + return &scm.Commit{ + Message: src.Message, + Sha: src.Sha, + Author: scm.Signature{ + Name: src.Author.Identity.Name, + Email: src.Author.Identity.Email, + }, + Committer: scm.Signature{ + Name: src.Committer.Identity.Name, + Email: src.Committer.Identity.Email, + }, } } diff --git a/scm/driver/harness/pr_test.go b/scm/driver/harness/pr_test.go index 5d232e2ff..b9bc5a631 100644 --- a/scm/driver/harness/pr_test.go +++ b/scm/driver/harness/pr_test.go @@ -3,3 +3,134 @@ // license that can be found in the LICENSE file. package harness + +import ( + "context" + "encoding/json" + "io/ioutil" + "net/http" + "testing" + + "github.com/drone/go-scm/scm" + "github.com/drone/go-scm/scm/transport" + "github.com/google/go-cmp/cmp" + "github.com/h2non/gock" +) + +func TestPRFind(t *testing.T) { + if harnessPAT == "" { + defer gock.Off() + + gock.New(gockOrigin). + Get("/gateway/code/api/v1/repos/px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/thomas/+/pullreq/1"). + Reply(200). + Type("plain/text"). + File("testdata/pr.json") + } + client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject) + client.Client = &http.Client{ + Transport: &transport.Custom{ + Before: func(r *http.Request) { + r.Header.Set("x-api-key", harnessPAT) + }, + }, + } + got, _, err := client.PullRequests.Find(context.Background(), harnessRepo, 1) + if err != nil { + t.Error(err) + } + + want := new(scm.PullRequest) + raw, err := ioutil.ReadFile("testdata/pr.json.golden") + if err != nil { + t.Error(err) + } + err = json.Unmarshal(raw, want) + if err != nil { + t.Error(err) + } + + if diff := cmp.Diff(got, want); diff != "" { + t.Errorf("Unexpected Results") + t.Log(diff) + } +} + +func TestPRCommits(t *testing.T) { + if harnessPAT == "" { + defer gock.Off() + + gock.New(gockOrigin). + Get("/gateway/code/api/v1/repos/px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/thomas/+/pullreq/1/commits"). + Reply(200). + Type("plain/text"). + File("testdata/pr_commits.json") + } + client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject) + client.Client = &http.Client{ + Transport: &transport.Custom{ + Before: func(r *http.Request) { + r.Header.Set("x-api-key", harnessPAT) + }, + }, + } + got, _, err := client.PullRequests.ListCommits(context.Background(), harnessRepo, 1, scm.ListOptions{}) + if err != nil { + t.Error(err) + } + + want := []*scm.Commit{} + raw, err := ioutil.ReadFile("testdata/pr_commits.json.golden") + if err != nil { + t.Error(err) + } + err = json.Unmarshal(raw, &want) + if err != nil { + t.Error(err) + } + + if diff := cmp.Diff(got, want); diff != "" { + t.Errorf("Unexpected Results") + t.Log(diff) + } +} + +func TestPullCreate(t *testing.T) { + defer gock.Off() + gock.New(gockOrigin). + Post("/gateway/code/api/v1/repos/px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/thomas/+/pullreq"). + Reply(200). + Type("plain/text"). + File("testdata/pr.json") + + client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject) + client.Client = &http.Client{ + Transport: &transport.Custom{ + Before: func(r *http.Request) { + r.Header.Set("x-api-key", harnessPAT) + }, + }, + } + + input := scm.PullRequestInput{ + Title: "pull title", + Body: "pull description", + Source: "bla", + Target: "main", + } + + got, _, err := client.PullRequests.Create(context.Background(), harnessRepo, &input) + if err != nil { + t.Error(err) + return + } + + want := new(scm.PullRequest) + raw, _ := ioutil.ReadFile("testdata/pr.json.golden") + _ = json.Unmarshal(raw, want) + + if diff := cmp.Diff(got, want); diff != "" { + t.Errorf("Unexpected Results") + t.Log(diff) + } +} diff --git a/scm/driver/harness/release.go b/scm/driver/harness/release.go index 45e10704a..66d62cdb3 100644 --- a/scm/driver/harness/release.go +++ b/scm/driver/harness/release.go @@ -67,7 +67,7 @@ type release struct { IsPrerelease bool `json:"prerelease"` CreatedAt null.Time `json:"created_at"` PublishedAt null.Time `json:"published_at"` - Publisher *user `json:"author"` + Publisher *string `json:"author"` Attachments []*Attachment `json:"assets"` } diff --git a/scm/driver/harness/testdata/branches.json b/scm/driver/harness/testdata/branches.json index e35f84b4e..2490e4ae3 100644 --- a/scm/driver/harness/testdata/branches.json +++ b/scm/driver/harness/testdata/branches.json @@ -22,6 +22,29 @@ } } }, + { + "name": "branch3", + "sha": "59e1cdf0e421fd14b106f4861fb02dfd56b4dc34", + "commit": { + "sha": "59e1cdf0e421fd14b106f4861fb02dfd56b4dc34", + "title": "delete README.2", + "message": "delete README.2\n\ndelete README.2", + "author": { + "identity": { + "name": "thomas.honey", + "email": "thomas.honey@harness.io" + }, + "when": "2023-02-09T12:39:37Z" + }, + "committer": { + "identity": { + "name": "Harness", + "email": "noreply@harness.io" + }, + "when": "2023-02-09T12:39:37Z" + } + } + }, { "name": "main", "sha": "de2837f8911710cfb7bbb323d0de285fd2ef9155", diff --git a/scm/driver/harness/testdata/branches.json.golden b/scm/driver/harness/testdata/branches.json.golden index 66ad737e5..3f2458b08 100644 --- a/scm/driver/harness/testdata/branches.json.golden +++ b/scm/driver/harness/testdata/branches.json.golden @@ -1,9 +1,14 @@ [ - { + { "Name": "bla", "Path": "refs/heads/bla", "Sha": "0c221fd126b9457d0ad2037641416083549f59c5" }, + { + "Name": "branch3", + "Path": "refs/heads/branch3", + "Sha": "59e1cdf0e421fd14b106f4861fb02dfd56b4dc34" + }, { "Name": "main", "Path": "refs/heads/main", diff --git a/scm/driver/harness/testdata/pr.json b/scm/driver/harness/testdata/pr.json new file mode 100644 index 000000000..89e773b96 --- /dev/null +++ b/scm/driver/harness/testdata/pr.json @@ -0,0 +1,32 @@ +{ + "number": 1, + "created": 1675960384081, + "edited": 1675960384081, + "state": "open", + "is_draft": false, + "title": "pull title", + "description": "pull description", + "source_repo_id": 11, + "source_branch": "bla", + "target_repo_id": 11, + "target_branch": "main", + "merged": null, + "merge_strategy": null, + "merge_head_sha": null, + "merge_base_sha": null, + "author": { + "id": 14, + "uid": "0Nnoezs6RGa_fOWvG_Ta4w", + "display_name": "thomas.honey", + "email": "thomas.honey@harness.io", + "type": "user", + "created": 1675248918372, + "updated": 1675248918372 + }, + "merger": null, + "stats": { + "conversations": 0, + "commits": 1, + "files_changed": 1 + } +} \ No newline at end of file diff --git a/scm/driver/harness/testdata/pr.json.golden b/scm/driver/harness/testdata/pr.json.golden new file mode 100644 index 000000000..224b66050 --- /dev/null +++ b/scm/driver/harness/testdata/pr.json.golden @@ -0,0 +1,20 @@ +{ + "Number": 1, + "Title": "pull title", + "Body": "pull description", + "Sha": "", + "Ref": "refs/pull/1/head", + "Source": "bla", + "Target": "main", + "Fork": "fork", + "Link": "", + "Diff": "", + "Closed": false, + "Merged": false, + "Author": { + "ID": "0Nnoezs6RGa_fOWvG_Ta4w", + "Login": "thomas.honey@harness.io", + "Name": "thomas.honey", + "Email": "thomas.honey@harness.io" + } +} \ No newline at end of file diff --git a/scm/driver/harness/testdata/pr_commits.json b/scm/driver/harness/testdata/pr_commits.json new file mode 100644 index 000000000..286654d14 --- /dev/null +++ b/scm/driver/harness/testdata/pr_commits.json @@ -0,0 +1,21 @@ +[ + { + "author": { + "identity": { + "email": "thomas.honey@harness.io", + "name": "thomas.honey" + }, + "when": "2023-02-09T17:12:10.976Z" + }, + "committer": { + "identity": { + "email": "noreply@harness.io", + "name": "Harness" + }, + "when": "2023-02-09T17:12:10.976Z" + }, + "message": "Create bla_file", + "sha": "0c221fd126b9457d0ad2037641416083549f59c5", + "title": "string" + } +] \ No newline at end of file diff --git a/scm/driver/harness/testdata/pr_commits.json.golden b/scm/driver/harness/testdata/pr_commits.json.golden new file mode 100644 index 000000000..aeceed89f --- /dev/null +++ b/scm/driver/harness/testdata/pr_commits.json.golden @@ -0,0 +1,19 @@ +[ + { + "Sha": "0c221fd126b9457d0ad2037641416083549f59c5", + "Message": "Create bla_file", + "Author": { + "Name": "thomas.honey", + "Email": "thomas.honey@harness.io", + "Login": "", + "Avatar": "" + }, + "Committer": { + "Name": "Harness", + "Email": "noreply@harness.io", + "Login": "", + "Avatar": "" + }, + "Link": "" + } +] \ No newline at end of file