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/stash/pr.go

308 lines
10 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 stash
import (
"context"
"fmt"
"time"
"git.awesome-for.me/liuzhiguo/go-scm/scm"
)
type pullService struct {
client *wrapper
}
func (s *pullService) Find(ctx context.Context, repo string, number int) (*scm.PullRequest, *scm.Response, error) {
namespace, name := scm.Split(repo)
path := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/pull-requests/%d", namespace, name, number)
out := new(pr)
res, err := s.client.do(ctx, "GET", path, nil, out)
return convertPullRequest(out), res, err
}
func (s *pullService) FindComment(ctx context.Context, repo string, number int, id int) (*scm.Comment, *scm.Response, error) {
namespace, name := scm.Split(repo)
path := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/pull-requests/%d/comments/%d", namespace, name, number, id)
out := new(pullRequestComment)
res, err := s.client.do(ctx, "GET", path, nil, out)
return convertPullRequestComment(out), res, err
}
func (s *pullService) List(ctx context.Context, repo string, opts scm.PullRequestListOptions) ([]*scm.PullRequest, *scm.Response, error) {
namespace, name := scm.Split(repo)
path := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/pull-requests", namespace, name)
out := new(prs)
res, err := s.client.do(ctx, "GET", path, nil, out)
if !out.pagination.LastPage.Bool {
res.Page.First = 1
res.Page.Next = opts.Page + 1
}
return convertPullRequests(out), res, err
}
func (s *pullService) ListChanges(ctx context.Context, repo string, number int, opts scm.ListOptions) ([]*scm.Change, *scm.Response, error) {
namespace, name := scm.Split(repo)
path := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/pull-requests/%d/changes", namespace, name, number)
out := new(diffstats)
res, err := s.client.do(ctx, "GET", path, nil, out)
if !out.pagination.LastPage.Bool {
res.Page.First = 1
res.Page.Next = opts.Page + 1
}
return convertDiffstats(out), res, err
}
func (s *pullService) ListComments(context.Context, string, int, scm.ListOptions) ([]*scm.Comment, *scm.Response, error) {
// TODO(bradrydzewski) the challenge with comments is that we need to use
// the activities endpoint, which returns entries that may or may not be
// comments. This complicates how we handle counts and pagination.
// GET /rest/api/1.0/projects/PRJ/repos/my-repo/pull-requests/1/activities
return nil, nil, scm.ErrNotSupported
}
func (s *pullService) ListCommits(ctx context.Context, repo string, number int, opts scm.ListOptions) ([]*scm.Commit, *scm.Response, error) {
namespace, name := scm.Split(repo)
path := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/pull-requests/%d/commits", namespace, name, number)
out := new(commits)
res, err := s.client.do(ctx, "GET", path, nil, out)
if !out.pagination.LastPage.Bool {
res.Page.First = 1
res.Page.Next = opts.Page + 1
}
return convertCommitList(out), res, err
}
func (s *pullService) Merge(ctx context.Context, repo string, number int) (*scm.Response, error) {
namespace, name := scm.Split(repo)
path := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/pull-requests/%d/merge", namespace, name, number)
res, err := s.client.do(ctx, "POST", path, nil, nil)
return res, err
}
func (s *pullService) Close(ctx context.Context, repo string, number int) (*scm.Response, error) {
namespace, name := scm.Split(repo)
path := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/pull-requests/%d/decline", namespace, name, number)
res, err := s.client.do(ctx, "POST", path, nil, nil)
return res, err
}
func (s *pullService) Create(ctx context.Context, repo string, input *scm.PullRequestInput) (*scm.PullRequest, *scm.Response, error) {
namespace, name := scm.Split(repo)
path := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/pull-requests", namespace, name)
in := new(prInput)
in.Title = input.Title
in.Description = input.Body
in.FromRef.Repository.Project.Key = namespace
in.FromRef.Repository.Slug = name
in.FromRef.ID = scm.ExpandRef(input.Source, "refs/heads")
in.ToRef.Repository.Project.Key = namespace
in.ToRef.Repository.Slug = name
in.ToRef.ID = scm.ExpandRef(input.Target, "refs/heads")
out := new(pr)
res, err := s.client.do(ctx, "POST", path, in, out)
return convertPullRequest(out), res, err
}
func (s *pullService) CreateComment(ctx context.Context, repo string, number int, in *scm.CommentInput) (*scm.Comment, *scm.Response, error) {
input := pullRequestCommentInput{Text: in.Body}
namespace, name := scm.Split(repo)
path := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/pull-requests/%d/comments", namespace, name, number)
out := new(pullRequestComment)
res, err := s.client.do(ctx, "POST", path, &input, out)
return convertPullRequestComment(out), res, err
}
func (s *pullService) DeleteComment(context.Context, string, int, int) (*scm.Response, error) {
// TODO(bradrydzewski) the challenge with deleting comments is that we need to specify
// the comment version number. The proposal is to use 0 as the initial version number,
// and then to use expectedVersion on error and re-attempt the API call.
// DELETE /rest/api/1.0/projects/PRJ/repos/my-repo/pull-requests/1/comments/1?version=0
return nil, scm.ErrNotSupported
}
type pr struct {
ID int `json:"id"`
Version int `json:"version"`
Title string `json:"title"`
Description string `json:"description"`
State string `json:"state"`
Open bool `json:"open"`
Closed bool `json:"closed"`
CreatedDate int64 `json:"createdDate"`
UpdatedDate int64 `json:"updatedDate"`
FromRef struct {
ID string `json:"id"`
DisplayID string `json:"displayId"`
LatestCommit string `json:"latestCommit"`
Repository repository `json:"repository"`
} `json:"fromRef"`
ToRef struct {
ID string `json:"id"`
DisplayID string `json:"displayId"`
LatestCommit string `json:"latestCommit"`
Repository repository `json:"repository"`
} `json:"toRef"`
Locked bool `json:"locked"`
Author struct {
User struct {
Name string `json:"name"`
EmailAddress string `json:"emailAddress"`
ID int `json:"id"`
DisplayName string `json:"displayName"`
Active bool `json:"active"`
Slug string `json:"slug"`
Type string `json:"type"`
Links struct {
Self []struct {
Href string `json:"href"`
} `json:"self"`
} `json:"links"`
} `json:"user"`
Role string `json:"role"`
Approved bool `json:"approved"`
Status string `json:"status"`
} `json:"author"`
Reviewers []interface{} `json:"reviewers"`
Participants []interface{} `json:"participants"`
Links struct {
Self []link `json:"self"`
} `json:"links"`
Properties struct {
MergeCommit struct {
ID string `json:"id"`
DisplayID string `json:"displayId"`
} `json:"mergeCommit"`
} `json:"properties"`
}
type prs struct {
pagination
Values []*pr `json:"values"`
}
type prInput struct {
Title string `json:"title"`
Description string `json:"description"`
FromRef struct {
ID string `json:"id"`
Repository struct {
Slug string `json:"slug"`
Project struct {
Key string `json:"key"`
} `json:"project"`
} `json:"repository"`
} `json:"fromRef"`
ToRef struct {
ID string `json:"id"`
Repository struct {
Slug string `json:"slug"`
Project struct {
Key string `json:"key"`
} `json:"project"`
} `json:"repository"`
} `json:"toRef"`
}
func convertPullRequests(from *prs) []*scm.PullRequest {
to := []*scm.PullRequest{}
for _, v := range from.Values {
to = append(to, convertPullRequest(v))
}
return to
}
func convertPullRequest(from *pr) *scm.PullRequest {
fork := scm.Join(
from.FromRef.Repository.Project.Key,
from.FromRef.Repository.Slug,
)
return &scm.PullRequest{
Number: from.ID,
Title: from.Title,
Body: from.Description,
Sha: from.FromRef.LatestCommit,
Merge: from.Properties.MergeCommit.ID,
Ref: fmt.Sprintf("refs/pull-requests/%d/from", from.ID),
Source: from.FromRef.DisplayID,
Target: from.ToRef.DisplayID,
Fork: fork,
Link: extractSelfLink(from.Links.Self),
Closed: from.Closed,
Merged: from.State == "MERGED",
Created: time.Unix(from.CreatedDate/1000, 0),
Updated: time.Unix(from.UpdatedDate/1000, 0),
Head: scm.Reference{
Name: from.FromRef.DisplayID,
Path: from.FromRef.ID,
Sha: from.FromRef.LatestCommit,
},
Base: scm.Reference{
Name: from.ToRef.DisplayID,
Path: from.ToRef.ID,
Sha: from.ToRef.LatestCommit,
},
Author: scm.User{
Login: from.Author.User.Slug,
Name: from.Author.User.DisplayName,
Email: from.Author.User.EmailAddress,
Avatar: avatarLink(from.Author.User.EmailAddress),
},
}
}
type pullRequestComment struct {
Properties struct {
RepositoryID int `json:"repositoryId"`
} `json:"properties"`
ID int `json:"id"`
Version int `json:"version"`
Text string `json:"text"`
Author struct {
Name string `json:"name"`
EmailAddress string `json:"emailAddress"`
ID int `json:"id"`
DisplayName string `json:"displayName"`
Active bool `json:"active"`
Slug string `json:"slug"`
Type string `json:"type"`
Links struct {
Self []struct {
Href string `json:"href"`
} `json:"self"`
} `json:"links"`
} `json:"author"`
CreatedDate int64 `json:"createdDate"`
UpdatedDate int64 `json:"updatedDate"`
Comments []interface{} `json:"comments"`
Tasks []interface{} `json:"tasks"`
PermittedOperations struct {
Editable bool `json:"editable"`
Deletable bool `json:"deletable"`
} `json:"permittedOperations"`
}
type pullRequestCommentInput struct {
Text string `json:"text"`
}
func convertPullRequestComment(from *pullRequestComment) *scm.Comment {
return &scm.Comment{
ID: from.ID,
Body: from.Text,
Created: time.Unix(from.CreatedDate/1000, 0),
Updated: time.Unix(from.UpdatedDate/1000, 0),
Author: scm.User{
Login: from.Author.Slug,
Name: from.Author.DisplayName,
Email: from.Author.EmailAddress,
Avatar: avatarLink(from.Author.EmailAddress),
},
}
}