2018-02-19 22:24:10 +00:00
|
|
|
// Copyright 2018 Drone.IO Inc.
|
2018-03-21 13:02:17 +00:00
|
|
|
//
|
2018-02-19 22:24:10 +00:00
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
2018-03-21 13:02:17 +00:00
|
|
|
//
|
2018-02-19 22:24:10 +00:00
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
2018-03-21 13:02:17 +00:00
|
|
|
//
|
2018-02-19 22:24:10 +00:00
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2016-04-29 19:39:56 +00:00
|
|
|
package internal
|
2015-10-04 04:50:11 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2021-09-28 10:56:59 +00:00
|
|
|
"context"
|
2015-10-04 04:50:11 +00:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
|
|
|
|
"golang.org/x/oauth2"
|
|
|
|
"golang.org/x/oauth2/bitbucket"
|
2024-01-14 17:22:06 +00:00
|
|
|
|
|
|
|
shared_utils "go.woodpecker-ci.org/woodpecker/v2/shared/utils"
|
2015-10-04 04:50:11 +00:00
|
|
|
)
|
|
|
|
|
2015-10-05 00:40:27 +00:00
|
|
|
const (
|
2023-07-17 19:30:06 +00:00
|
|
|
pathUser = "%s/2.0/user/"
|
|
|
|
pathEmails = "%s/2.0/user/emails"
|
2024-01-19 03:15:47 +00:00
|
|
|
pathPermission = "%s/2.0/user/permissions/repositories?q=repository.full_name=%q"
|
|
|
|
pathPermissions = "%s/2.0/user/permissions/repositories?%s"
|
2023-12-01 19:45:42 +00:00
|
|
|
pathWorkspaces = "%s/2.0/workspaces/?%s"
|
|
|
|
pathWorkspace = "%s/2.0/workspaces/%s"
|
2023-07-17 19:30:06 +00:00
|
|
|
pathRepo = "%s/2.0/repositories/%s/%s"
|
|
|
|
pathRepos = "%s/2.0/repositories/%s?%s"
|
|
|
|
pathHook = "%s/2.0/repositories/%s/%s/hooks/%s"
|
|
|
|
pathHooks = "%s/2.0/repositories/%s/%s/hooks?%s"
|
|
|
|
pathSource = "%s/2.0/repositories/%s/%s/src/%s/%s"
|
|
|
|
pathStatus = "%s/2.0/repositories/%s/%s/commit/%s/statuses/build"
|
2023-09-29 16:01:29 +00:00
|
|
|
pathBranches = "%s/2.0/repositories/%s/%s/refs/branches?%s"
|
2023-07-17 19:30:06 +00:00
|
|
|
pathOrgPerms = "%s/2.0/workspaces/%s/permissions?%s"
|
2024-01-20 21:41:54 +00:00
|
|
|
pathPullRequests = "%s/2.0/repositories/%s/%s/pullrequests?%s"
|
2023-07-17 19:30:06 +00:00
|
|
|
pathBranchCommits = "%s/2.0/repositories/%s/%s/commits/%s"
|
2024-05-01 10:22:07 +00:00
|
|
|
pathDir = "%s/2.0/repositories/%s/%s/src/%s/%s"
|
2024-03-15 17:00:25 +00:00
|
|
|
pageSize = 100
|
2015-10-05 00:40:27 +00:00
|
|
|
)
|
2015-10-04 04:50:11 +00:00
|
|
|
|
|
|
|
type Client struct {
|
|
|
|
*http.Client
|
2016-04-30 08:00:39 +00:00
|
|
|
base string
|
2021-09-28 10:56:59 +00:00
|
|
|
ctx context.Context
|
2015-10-04 04:50:11 +00:00
|
|
|
}
|
|
|
|
|
2021-09-28 10:56:59 +00:00
|
|
|
func NewClient(ctx context.Context, url string, client *http.Client) *Client {
|
|
|
|
return &Client{
|
|
|
|
Client: client,
|
|
|
|
base: url,
|
|
|
|
ctx: ctx,
|
|
|
|
}
|
2015-10-04 04:50:11 +00:00
|
|
|
}
|
|
|
|
|
2021-09-28 10:56:59 +00:00
|
|
|
func NewClientToken(ctx context.Context, url, client, secret string, token *oauth2.Token) *Client {
|
2015-10-04 04:50:11 +00:00
|
|
|
config := &oauth2.Config{
|
|
|
|
ClientID: client,
|
|
|
|
ClientSecret: secret,
|
|
|
|
Endpoint: bitbucket.Endpoint,
|
|
|
|
}
|
2021-09-28 10:56:59 +00:00
|
|
|
return NewClient(ctx, url, config.Client(ctx, token))
|
2015-10-04 04:50:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) FindCurrent() (*Account, error) {
|
2015-10-05 00:40:27 +00:00
|
|
|
out := new(Account)
|
2016-04-30 08:00:39 +00:00
|
|
|
uri := fmt.Sprintf(pathUser, c.base)
|
2024-06-06 09:16:45 +00:00
|
|
|
_, err := c.do(uri, http.MethodGet, nil, out)
|
2015-10-04 04:50:11 +00:00
|
|
|
return out, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) ListEmail() (*EmailResp, error) {
|
2015-10-05 00:40:27 +00:00
|
|
|
out := new(EmailResp)
|
2016-04-30 08:00:39 +00:00
|
|
|
uri := fmt.Sprintf(pathEmails, c.base)
|
2024-06-06 09:16:45 +00:00
|
|
|
_, err := c.do(uri, http.MethodGet, nil, out)
|
2015-10-04 04:50:11 +00:00
|
|
|
return out, err
|
|
|
|
}
|
|
|
|
|
2022-09-18 07:26:13 +00:00
|
|
|
func (c *Client) ListWorkspaces(opts *ListWorkspacesOpts) (*WorkspacesResp, error) {
|
|
|
|
out := new(WorkspacesResp)
|
2023-12-01 19:45:42 +00:00
|
|
|
uri := fmt.Sprintf(pathWorkspaces, c.base, opts.Encode())
|
2024-06-06 09:16:45 +00:00
|
|
|
_, err := c.do(uri, http.MethodGet, nil, out)
|
2015-10-04 04:50:11 +00:00
|
|
|
return out, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) FindRepo(owner, name string) (*Repo, error) {
|
2015-10-05 00:40:27 +00:00
|
|
|
out := new(Repo)
|
2016-04-30 08:00:39 +00:00
|
|
|
uri := fmt.Sprintf(pathRepo, c.base, owner, name)
|
2024-06-06 09:16:45 +00:00
|
|
|
_, err := c.do(uri, http.MethodGet, nil, out)
|
2015-10-04 04:50:11 +00:00
|
|
|
return out, err
|
|
|
|
}
|
|
|
|
|
2022-09-18 07:26:13 +00:00
|
|
|
func (c *Client) ListRepos(workspace string, opts *ListOpts) (*RepoResp, error) {
|
2015-10-05 00:40:27 +00:00
|
|
|
out := new(RepoResp)
|
2022-09-18 07:26:13 +00:00
|
|
|
uri := fmt.Sprintf(pathRepos, c.base, workspace, opts.Encode())
|
2024-06-06 09:16:45 +00:00
|
|
|
_, err := c.do(uri, http.MethodGet, nil, out)
|
2015-10-04 04:50:11 +00:00
|
|
|
return out, err
|
|
|
|
}
|
|
|
|
|
2022-09-18 07:26:13 +00:00
|
|
|
func (c *Client) ListReposAll(workspace string) ([]*Repo, error) {
|
2023-10-23 16:44:25 +00:00
|
|
|
return shared_utils.Paginate(func(page int) ([]*Repo, error) {
|
2024-03-15 17:00:25 +00:00
|
|
|
resp, err := c.ListRepos(workspace, &ListOpts{Page: page, PageLen: pageSize})
|
2015-10-05 00:40:27 +00:00
|
|
|
if err != nil {
|
2023-10-23 16:44:25 +00:00
|
|
|
return nil, err
|
2015-10-05 00:40:27 +00:00
|
|
|
}
|
2023-10-23 16:44:25 +00:00
|
|
|
return resp.Values, nil
|
|
|
|
})
|
2015-10-05 00:40:27 +00:00
|
|
|
}
|
|
|
|
|
2015-10-04 04:50:11 +00:00
|
|
|
func (c *Client) FindHook(owner, name, id string) (*Hook, error) {
|
2015-10-05 00:40:27 +00:00
|
|
|
out := new(Hook)
|
2016-04-30 08:00:39 +00:00
|
|
|
uri := fmt.Sprintf(pathHook, c.base, owner, name, id)
|
2024-06-06 09:16:45 +00:00
|
|
|
_, err := c.do(uri, http.MethodGet, nil, out)
|
2015-10-04 04:50:11 +00:00
|
|
|
return out, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) ListHooks(owner, name string, opts *ListOpts) (*HookResp, error) {
|
2015-10-05 00:40:27 +00:00
|
|
|
out := new(HookResp)
|
2016-04-30 08:00:39 +00:00
|
|
|
uri := fmt.Sprintf(pathHooks, c.base, owner, name, opts.Encode())
|
2024-06-06 09:16:45 +00:00
|
|
|
_, err := c.do(uri, http.MethodGet, nil, out)
|
2015-10-04 04:50:11 +00:00
|
|
|
return out, err
|
|
|
|
}
|
|
|
|
|
2015-10-05 00:40:27 +00:00
|
|
|
func (c *Client) CreateHook(owner, name string, hook *Hook) error {
|
2016-04-30 08:00:39 +00:00
|
|
|
uri := fmt.Sprintf(pathHooks, c.base, owner, name, "")
|
2024-06-06 09:16:45 +00:00
|
|
|
_, err := c.do(uri, http.MethodPost, hook, nil)
|
2019-06-13 14:33:51 +00:00
|
|
|
return err
|
2015-10-04 04:50:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) DeleteHook(owner, name, id string) error {
|
2016-04-30 08:00:39 +00:00
|
|
|
uri := fmt.Sprintf(pathHook, c.base, owner, name, id)
|
2024-06-06 09:16:45 +00:00
|
|
|
_, err := c.do(uri, http.MethodDelete, nil, nil)
|
2019-06-13 14:33:51 +00:00
|
|
|
return err
|
2015-10-04 04:50:11 +00:00
|
|
|
}
|
|
|
|
|
2019-06-13 14:33:51 +00:00
|
|
|
func (c *Client) FindSource(owner, name, revision, path string) (*string, error) {
|
2016-04-30 08:00:39 +00:00
|
|
|
uri := fmt.Sprintf(pathSource, c.base, owner, name, revision, path)
|
2024-06-06 09:16:45 +00:00
|
|
|
return c.do(uri, http.MethodGet, nil, nil)
|
2015-10-04 05:23:37 +00:00
|
|
|
}
|
|
|
|
|
2022-10-18 01:24:12 +00:00
|
|
|
func (c *Client) CreateStatus(owner, name, revision string, status *PipelineStatus) error {
|
2016-04-30 08:00:39 +00:00
|
|
|
uri := fmt.Sprintf(pathStatus, c.base, owner, name, revision)
|
2024-06-06 09:16:45 +00:00
|
|
|
_, err := c.do(uri, http.MethodPost, status, nil)
|
2019-06-13 14:33:51 +00:00
|
|
|
return err
|
2015-11-21 05:22:28 +00:00
|
|
|
}
|
|
|
|
|
2018-04-28 07:25:30 +00:00
|
|
|
func (c *Client) GetPermission(fullName string) (*RepoPerm, error) {
|
2018-04-27 15:59:36 +00:00
|
|
|
out := new(RepoPermResp)
|
2024-01-19 03:15:47 +00:00
|
|
|
uri := fmt.Sprintf(pathPermission, c.base, fullName)
|
2024-06-06 09:16:45 +00:00
|
|
|
_, err := c.do(uri, http.MethodGet, nil, out)
|
2018-04-27 15:59:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(out.Values) == 0 {
|
2018-04-30 08:50:49 +00:00
|
|
|
return nil, fmt.Errorf("no permissions in repository %s", fullName)
|
2018-04-27 15:59:36 +00:00
|
|
|
}
|
2021-12-01 13:22:06 +00:00
|
|
|
return out.Values[0], nil
|
2018-04-27 15:59:36 +00:00
|
|
|
}
|
|
|
|
|
2024-01-19 03:15:47 +00:00
|
|
|
func (c *Client) ListPermissions(opts *ListOpts) (*RepoPermResp, error) {
|
|
|
|
out := new(RepoPermResp)
|
|
|
|
uri := fmt.Sprintf(pathPermissions, c.base, opts.Encode())
|
2024-06-06 09:16:45 +00:00
|
|
|
_, err := c.do(uri, http.MethodGet, nil, out)
|
2024-01-19 03:15:47 +00:00
|
|
|
return out, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) ListPermissionsAll() ([]*RepoPerm, error) {
|
|
|
|
return shared_utils.Paginate(func(page int) ([]*RepoPerm, error) {
|
2024-03-15 17:00:25 +00:00
|
|
|
resp, err := c.ListPermissions(&ListOpts{Page: page, PageLen: pageSize})
|
2024-01-19 03:15:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return resp.Values, nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-09-29 16:01:29 +00:00
|
|
|
func (c *Client) ListBranches(owner, name string, opts *ListOpts) ([]*Branch, error) {
|
2022-05-12 17:31:01 +00:00
|
|
|
out := new(BranchResp)
|
2023-09-29 16:01:29 +00:00
|
|
|
uri := fmt.Sprintf(pathBranches, c.base, owner, name, opts.Encode())
|
2024-06-06 09:16:45 +00:00
|
|
|
_, err := c.do(uri, http.MethodGet, nil, out)
|
2022-05-12 17:31:01 +00:00
|
|
|
return out.Values, err
|
|
|
|
}
|
|
|
|
|
2024-02-11 09:44:50 +00:00
|
|
|
func (c *Client) GetBranchHead(owner, name, branch string) (*Commit, error) {
|
2023-07-17 19:30:06 +00:00
|
|
|
out := new(CommitsResp)
|
|
|
|
uri := fmt.Sprintf(pathBranchCommits, c.base, owner, name, branch)
|
2024-06-06 09:16:45 +00:00
|
|
|
_, err := c.do(uri, http.MethodGet, nil, out)
|
2023-07-17 19:30:06 +00:00
|
|
|
if err != nil {
|
2024-02-11 09:44:50 +00:00
|
|
|
return nil, err
|
2023-07-17 19:30:06 +00:00
|
|
|
}
|
|
|
|
if len(out.Values) == 0 {
|
2024-02-11 09:44:50 +00:00
|
|
|
return nil, fmt.Errorf("no commits in branch %s", branch)
|
2023-07-17 19:30:06 +00:00
|
|
|
}
|
2024-02-11 09:44:50 +00:00
|
|
|
return out.Values[0], nil
|
2023-07-17 19:30:06 +00:00
|
|
|
}
|
|
|
|
|
2022-07-25 01:09:35 +00:00
|
|
|
func (c *Client) GetUserWorkspaceMembership(workspace, user string) (string, error) {
|
|
|
|
out := new(WorkspaceMembershipResp)
|
2024-03-15 17:00:25 +00:00
|
|
|
opts := &ListOpts{Page: 1, PageLen: pageSize}
|
2022-07-25 01:09:35 +00:00
|
|
|
for {
|
|
|
|
uri := fmt.Sprintf(pathOrgPerms, c.base, workspace, opts.Encode())
|
2024-06-06 09:16:45 +00:00
|
|
|
_, err := c.do(uri, http.MethodGet, nil, out)
|
2022-07-25 01:09:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
for _, m := range out.Values {
|
|
|
|
if m.User.Nickname == user {
|
|
|
|
return m.Permission, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(out.Next) == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
opts.Page++
|
|
|
|
}
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
2023-06-27 21:40:28 +00:00
|
|
|
func (c *Client) ListPullRequests(owner, name string, opts *ListOpts) ([]*PullRequest, error) {
|
|
|
|
out := new(PullRequestResp)
|
2024-01-20 21:41:54 +00:00
|
|
|
uri := fmt.Sprintf(pathPullRequests, c.base, owner, name, opts.Encode())
|
2024-06-06 09:16:45 +00:00
|
|
|
_, err := c.do(uri, http.MethodGet, nil, out)
|
2023-06-27 21:40:28 +00:00
|
|
|
return out.Values, err
|
|
|
|
}
|
|
|
|
|
2023-07-21 17:45:32 +00:00
|
|
|
func (c *Client) GetWorkspace(name string) (*Workspace, error) {
|
|
|
|
out := new(Workspace)
|
|
|
|
uri := fmt.Sprintf(pathWorkspace, c.base, name)
|
2024-06-06 09:16:45 +00:00
|
|
|
_, err := c.do(uri, http.MethodGet, nil, out)
|
2023-07-21 17:45:32 +00:00
|
|
|
return out, err
|
|
|
|
}
|
|
|
|
|
2023-08-02 11:15:57 +00:00
|
|
|
func (c *Client) GetRepoFiles(owner, name, revision, path string, page *string) (*DirResp, error) {
|
|
|
|
out := new(DirResp)
|
|
|
|
uri := fmt.Sprintf(pathDir, c.base, owner, name, revision, path)
|
|
|
|
if page != nil {
|
|
|
|
uri += "?page=" + *page
|
|
|
|
}
|
2024-06-06 09:16:45 +00:00
|
|
|
_, err := c.do(uri, http.MethodGet, nil, out)
|
2023-08-02 11:15:57 +00:00
|
|
|
return out, err
|
|
|
|
}
|
|
|
|
|
2024-06-04 06:30:54 +00:00
|
|
|
func (c *Client) do(rawURL, method string, in, out any) (*string, error) {
|
|
|
|
uri, err := url.Parse(rawURL)
|
2015-10-04 04:50:11 +00:00
|
|
|
if err != nil {
|
2019-06-13 14:33:51 +00:00
|
|
|
return nil, err
|
2015-10-04 04:50:11 +00:00
|
|
|
}
|
2015-10-04 05:23:37 +00:00
|
|
|
|
2015-10-04 04:50:11 +00:00
|
|
|
// if we are posting or putting data, we need to
|
|
|
|
// write it to the body of the request.
|
|
|
|
var buf io.ReadWriter
|
|
|
|
if in != nil {
|
|
|
|
buf = new(bytes.Buffer)
|
|
|
|
err := json.NewEncoder(buf).Encode(in)
|
|
|
|
if err != nil {
|
2019-06-13 14:33:51 +00:00
|
|
|
return nil, err
|
2015-10-04 04:50:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// creates a new http request to bitbucket.
|
2021-09-28 10:56:59 +00:00
|
|
|
req, err := http.NewRequestWithContext(c.ctx, method, uri.String(), buf)
|
2015-10-04 04:50:11 +00:00
|
|
|
if err != nil {
|
2019-06-13 14:33:51 +00:00
|
|
|
return nil, err
|
2015-10-04 04:50:11 +00:00
|
|
|
}
|
2015-10-05 00:40:27 +00:00
|
|
|
if in != nil {
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
}
|
2015-10-04 04:50:11 +00:00
|
|
|
|
|
|
|
resp, err := c.Do(req)
|
|
|
|
if err != nil {
|
2019-06-13 14:33:51 +00:00
|
|
|
return nil, err
|
2015-10-04 04:50:11 +00:00
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
// if an error is encountered, parse and return the
|
|
|
|
// error response.
|
|
|
|
if resp.StatusCode > http.StatusPartialContent {
|
|
|
|
err := Error{}
|
2021-11-23 14:36:52 +00:00
|
|
|
_ = json.NewDecoder(resp.Body).Decode(&err)
|
2015-10-04 04:50:11 +00:00
|
|
|
err.Status = resp.StatusCode
|
2019-06-13 14:33:51 +00:00
|
|
|
return nil, err
|
2015-10-04 04:50:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if a json response is expected, parse and return
|
|
|
|
// the json response.
|
|
|
|
if out != nil {
|
2019-06-13 14:33:51 +00:00
|
|
|
return nil, json.NewDecoder(resp.Body).Decode(out)
|
|
|
|
}
|
|
|
|
|
2022-08-29 23:14:07 +00:00
|
|
|
bodyBytes, err := io.ReadAll(resp.Body)
|
2019-06-13 14:33:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2015-10-04 04:50:11 +00:00
|
|
|
}
|
2019-06-13 14:33:51 +00:00
|
|
|
bodyString := string(bodyBytes)
|
2015-10-04 04:50:11 +00:00
|
|
|
|
2019-06-13 14:33:51 +00:00
|
|
|
return &bodyString, nil
|
2015-10-04 04:50:11 +00:00
|
|
|
}
|