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/repo.go

383 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 bitbucket
import (
"context"
"fmt"
"net/url"
"time"
"git.awesome-for.me/liuzhiguo/go-scm/scm"
)
type cloneLink struct {
link
Name string `json:"name"`
}
type repository struct {
UUID string `json:"uuid"`
SCM string `json:"scm"`
FullName string `json:"full_name"`
IsPrivate bool `json:"is_private"`
CreatedOn time.Time `json:"created_on"`
UpdatedOn time.Time `json:"updated_on"`
Mainbranch struct {
Type string `json:"type"`
Name string `json:"name"`
} `json:"mainbranch"`
Links struct {
HTML link `json:"html"`
Clone []cloneLink `json:"clone"`
} `json:"links"`
}
type perms struct {
Values []*perm `json:"values"`
}
type perm struct {
Permissions string `json:"permission"`
}
type hooks struct {
pagination
Values []*hook `json:"values"`
}
type hook struct {
Description string `json:"description"`
URL string `json:"url"`
SkipCertVerification bool `json:"skip_cert_verification,omitempty"`
Active bool `json:"active"`
Events []string `json:"events"`
UUID string `json:"uuid"`
}
type hookInput struct {
Description string `json:"description"`
URL string `json:"url"`
SkipCertVerification bool `json:"skip_cert_verification,omitempty"`
Active bool `json:"active"`
Events []string `json:"events"`
}
type repositoryService struct {
client *wrapper
}
// Find returns the repository by name.
func (s *repositoryService) Find(ctx context.Context, repo string) (*scm.Repository, *scm.Response, error) {
path := fmt.Sprintf("2.0/repositories/%s", repo)
out := new(repository)
res, err := s.client.do(ctx, "GET", path, nil, out)
return convertRepository(out), res, err
}
// FindHook returns a repository hook.
func (s *repositoryService) FindHook(ctx context.Context, repo string, id string) (*scm.Hook, *scm.Response, error) {
path := fmt.Sprintf("2.0/repositories/%s/hooks/%s", repo, id)
out := new(hook)
res, err := s.client.do(ctx, "GET", path, nil, out)
return convertHook(out), res, err
}
// FindPerms returns the repository permissions.
func (s *repositoryService) FindPerms(ctx context.Context, repo string) (*scm.Perm, *scm.Response, error) {
path := fmt.Sprintf("2.0/user/permissions/repositories?q=repository.full_name=%q", repo)
out := new(perms)
res, err := s.client.do(ctx, "GET", path, nil, out)
return convertPerms(out), res, err
}
// List returns the user repository list.
func (s *repositoryService) List(ctx context.Context, opts scm.ListOptions) ([]*scm.Repository, *scm.Response, error) {
path := fmt.Sprintf("2.0/repositories?%s", encodeListRoleOptions(opts))
if opts.URL != "" {
path = opts.URL
}
out := new(repositories)
res, err := s.client.do(ctx, "GET", path, nil, &out)
copyPagination(out.pagination, res)
return convertRepositoryList(out), res, err
}
// ListV2 returns the user repository list based on the searchTerm passed.
func (s *repositoryService) ListV2(ctx context.Context, opts scm.RepoListOptions) ([]*scm.Repository, *scm.Response, error) {
path := fmt.Sprintf("2.0/repositories?%s", encodeRepoListOptions(opts))
if opts.ListOptions.URL != "" {
path = opts.ListOptions.URL
}
out := new(repositories)
res, err := s.client.do(ctx, "GET", path, nil, &out)
copyPagination(out.pagination, res)
return convertRepositoryList(out), res, err
}
// ListHooks returns a list or repository hooks.
func (s *repositoryService) ListHooks(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Hook, *scm.Response, error) {
path := fmt.Sprintf("2.0/repositories/%s/hooks?%s", repo, encodeListOptions(opts))
out := new(hooks)
res, err := s.client.do(ctx, "GET", path, nil, out)
copyPagination(out.pagination, res)
return convertHookList(out), res, err
}
// ListStatus returns a list of commit statuses.
func (s *repositoryService) ListStatus(ctx context.Context, repo, ref string, opts scm.ListOptions) ([]*scm.Status, *scm.Response, error) {
path := fmt.Sprintf("2.0/repositories/%s/commit/%s/statuses?%s", repo, ref, encodeListOptions(opts))
out := new(statuses)
res, err := s.client.do(ctx, "GET", path, nil, out)
copyPagination(out.pagination, res)
return convertStatusList(out), res, err
}
// CreateHook creates a new repository webhook.
func (s *repositoryService) CreateHook(ctx context.Context, repo string, input *scm.HookInput) (*scm.Hook, *scm.Response, error) {
target, err := url.Parse(input.Target)
if err != nil {
return nil, nil, err
}
params := target.Query()
params.Set("secret", input.Secret)
target.RawQuery = params.Encode()
path := fmt.Sprintf("2.0/repositories/%s/hooks", repo)
in := new(hookInput)
in.URL = target.String()
in.SkipCertVerification = input.SkipVerify
in.Active = true
in.Description = input.Name
in.Events = append(
input.NativeEvents,
convertFromHookEvents(input.Events)...,
)
out := new(hook)
res, err := s.client.do(ctx, "POST", path, in, out)
return convertHook(out), res, err
}
// CreateStatus creates a new commit status.
func (s *repositoryService) CreateStatus(ctx context.Context, repo, ref string, input *scm.StatusInput) (*scm.Status, *scm.Response, error) {
path := fmt.Sprintf("2.0/repositories/%s/commit/%s/statuses/build", repo, ref)
in := &status{
State: convertFromState(input.State),
Name: input.Title,
Desc: input.Desc,
Key: input.Label,
URL: input.Target,
}
out := new(status)
res, err := s.client.do(ctx, "POST", path, in, out)
return convertStatus(out), res, err
}
// UpdateHook updates a repository webhook.
func (s *repositoryService) UpdateHook(ctx context.Context, repo, id string, input *scm.HookInput) (*scm.Hook, *scm.Response, error) {
target, err := url.Parse(input.Target)
if err != nil {
return nil, nil, err
}
params := target.Query()
params.Set("secret", input.Secret)
target.RawQuery = params.Encode()
path := fmt.Sprintf("2.0/repositories/%s/hooks/%s", repo, id)
in := new(hookInput)
in.URL = target.String()
in.SkipCertVerification = input.SkipVerify
in.Active = true
in.Description = input.Name
in.Events = append(
input.NativeEvents,
convertFromHookEvents(input.Events)...,
)
out := new(hook)
res, err := s.client.do(ctx, "PUT", path, in, out)
return convertHook(out), res, err
}
// DeleteHook deletes a repository webhook.
func (s *repositoryService) DeleteHook(ctx context.Context, repo string, id string) (*scm.Response, error) {
path := fmt.Sprintf("2.0/repositories/%s/hooks/%s", repo, id)
return s.client.do(ctx, "DELETE", path, nil, nil)
}
// helper function to convert from the gogs repository list to
// the common repository structure.
func convertRepositoryList(from *repositories) []*scm.Repository {
to := []*scm.Repository{}
for _, v := range from.Values {
to = append(to, convertRepository(v))
}
return to
}
// helper function to convert from the gogs repository structure
// to the common repository structure.
func convertRepository(from *repository) *scm.Repository {
namespace, name := scm.Split(from.FullName)
return &scm.Repository{
ID: from.UUID,
Name: name,
Namespace: namespace,
Link: from.Links.HTML.Href,
Branch: from.Mainbranch.Name,
Private: from.IsPrivate,
CloneSSH: extractCloneLink(from.Links.Clone, "ssh"),
Clone: anonymizeLink(extractCloneLink(from.Links.Clone, "https", "http")),
Created: from.CreatedOn,
Updated: from.UpdatedOn,
}
}
func extractCloneLink(links []cloneLink, names ...string) (href string) {
for _, name := range names {
for _, link := range links {
if link.Name == name && link.Href != "" {
return link.Href
}
}
}
return
}
func anonymizeLink(link string) (href string) {
parsed, err := url.Parse(link)
if err != nil {
return link
}
parsed.User = nil
return parsed.String()
}
func convertPerms(from *perms) *scm.Perm {
to := new(scm.Perm)
if len(from.Values) != 1 {
return to
}
switch from.Values[0].Permissions {
case "admin":
to.Pull = true
to.Push = true
to.Admin = true
case "write":
to.Pull = true
to.Push = true
default:
to.Pull = true
}
return to
}
func convertHookList(from *hooks) []*scm.Hook {
to := []*scm.Hook{}
for _, v := range from.Values {
to = append(to, convertHook(v))
}
return to
}
func convertHook(from *hook) *scm.Hook {
return &scm.Hook{
ID: from.UUID,
Name: from.Description,
Active: from.Active,
Target: from.URL,
Events: from.Events,
SkipVerify: from.SkipCertVerification,
}
}
func convertFromHookEvents(from scm.HookEvents) []string {
var events []string
if from.Push {
events = append(events, "repo:push")
}
if from.PullRequest {
events = append(events, "pullrequest:updated")
events = append(events, "pullrequest:unapproved")
events = append(events, "pullrequest:approved")
events = append(events, "pullrequest:rejected")
events = append(events, "pullrequest:fulfilled")
events = append(events, "pullrequest:created")
}
if from.PullRequestComment {
events = append(events, "pullrequest:comment_created")
events = append(events, "pullrequest:comment_updated")
events = append(events, "pullrequest:comment_deleted")
}
if from.Issue {
events = append(events, "issues")
events = append(events, "issue:created")
events = append(events, "issue:updated")
}
if from.IssueComment {
events = append(events, "issue:comment_created")
}
return events
}
type repositories struct {
pagination
Values []*repository `json:"values"`
}
type statuses struct {
pagination
Values []*status `json:"values"`
}
type status struct {
State string `json:"state"`
Key string `json:"key"`
Name string `json:"name,omitempty"`
URL string `json:"url"`
Desc string `json:"description,omitempty"`
}
func convertStatusList(from *statuses) []*scm.Status {
to := []*scm.Status{}
for _, v := range from.Values {
to = append(to, convertStatus(v))
}
return to
}
func convertStatus(from *status) *scm.Status {
return &scm.Status{
State: convertState(from.State),
Label: from.Key,
Title: from.Name,
Desc: from.Desc,
Target: from.URL,
}
}
func convertState(from string) scm.State {
switch from {
case "FAILED":
return scm.StateFailure
case "INPROGRESS":
return scm.StatePending
case "SUCCESSFUL":
return scm.StateSuccess
default:
return scm.StateUnknown
}
}
func convertFromState(from scm.State) string {
switch from {
case scm.StatePending, scm.StateRunning:
return "INPROGRESS"
case scm.StateSuccess:
return "SUCCESSFUL"
default:
return "FAILED"
}
}