Skip to content

Commit

Permalink
feat: ✨ Exposed Repository API getting and creating repositories
Browse files Browse the repository at this point in the history
  • Loading branch information
langecode committed Jan 10, 2023
1 parent a46c6a6 commit 0aa42c3
Show file tree
Hide file tree
Showing 12 changed files with 502 additions and 45 deletions.
3 changes: 3 additions & 0 deletions bitbucket/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ go_library(
"bitbucket.go",
"keys.go",
"keys_repos.go",
"projects.go",
"projects_repos.go",
],
importpath = "github.com/neticdk/go-bitbucket/bitbucket",
visibility = ["//visibility:public"],
Expand All @@ -21,6 +23,7 @@ go_test(
"access_tokens_users_test.go",
"bitbucket_test.go",
"keys_repos_test.go",
"projects_repos_test.go",
],
embed = [":go_default_library"],
deps = ["@com_github_stretchr_testify//assert:go_default_library"],
Expand Down
2 changes: 1 addition & 1 deletion bitbucket/access_tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const accessTokenApiName = "access-tokens"

type accessTokenList struct {
ListResponse
Tokens []AccessToken `json:"values"`
Tokens []*AccessToken `json:"values"`
}

type AccessToken struct {
Expand Down
14 changes: 3 additions & 11 deletions bitbucket/access_tokens_repos.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,10 @@ import (
"fmt"
)

func (s *AccessTokensService) ListRepositoryTokens(ctx context.Context, projectKey, repositorySlug string) ([]AccessToken, *Response, error) {
func (s *AccessTokensService) ListRepositoryTokens(ctx context.Context, projectKey, repositorySlug string, opts *ListOptions) ([]*AccessToken, *Response, error) {
p := fmt.Sprintf("projects/%s/repos/%s", projectKey, repositorySlug)
req, err := s.client.NewRequest("GET", accessTokenApiName, p, nil)
if err != nil {
return nil, nil, err
}
var list accessTokenList
resp, err := s.client.Do(ctx, req, &list)
resp, err := s.client.GetPaged(ctx, accessTokenApiName, p, &list, opts)
if err != nil {
return nil, resp, err
}
Expand All @@ -21,12 +17,8 @@ func (s *AccessTokensService) ListRepositoryTokens(ctx context.Context, projectK

func (s *AccessTokensService) GetRepositoryToken(ctx context.Context, projectKey, repositorySlug, tokenId string) (*AccessToken, *Response, error) {
p := fmt.Sprintf("projects/%s/repos/%s/%s", projectKey, repositorySlug, tokenId)
req, err := s.client.NewRequest("GET", accessTokenApiName, p, nil)
if err != nil {
return nil, nil, err
}
var token AccessToken
resp, err := s.client.Do(ctx, req, &token)
resp, err := s.client.Get(ctx, accessTokenApiName, p, &token)
if err != nil {
return nil, resp, err
}
Expand Down
2 changes: 1 addition & 1 deletion bitbucket/access_tokens_repos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestListRepositoryTokens(t *testing.T) {

client, _ := NewClient(server.URL, nil)
ctx := context.Background()
tokens, _, err := client.AccessTokens.ListRepositoryTokens(ctx, "PRJ", "repo")
tokens, _, err := client.AccessTokens.ListRepositoryTokens(ctx, "PRJ", "repo", &ListOptions{})
assert.NoError(t, err)
assert.Len(t, tokens, 2)
}
Expand Down
14 changes: 3 additions & 11 deletions bitbucket/access_tokens_users.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,10 @@ import (
"fmt"
)

func (s *AccessTokensService) ListUserTokens(ctx context.Context, userSlug string) ([]AccessToken, *Response, error) {
func (s *AccessTokensService) ListUserTokens(ctx context.Context, userSlug string, opts *ListOptions) ([]*AccessToken, *Response, error) {
p := fmt.Sprintf("users/%s", userSlug)
req, err := s.client.NewRequest("GET", accessTokenApiName, p, nil)
if err != nil {
return nil, nil, err
}
var list accessTokenList
resp, err := s.client.Do(ctx, req, &list)
resp, err := s.client.GetPaged(ctx, accessTokenApiName, p, &list, opts)
if err != nil {
return nil, resp, err
}
Expand All @@ -21,12 +17,8 @@ func (s *AccessTokensService) ListUserTokens(ctx context.Context, userSlug strin

func (s *AccessTokensService) GetUserToken(ctx context.Context, userSlug, tokenId string) (*AccessToken, *Response, error) {
p := fmt.Sprintf("users/%s/%s", userSlug, tokenId)
req, err := s.client.NewRequest("GET", accessTokenApiName, p, nil)
if err != nil {
return nil, nil, err
}
var token AccessToken
resp, err := s.client.Do(ctx, req, &token)
resp, err := s.client.Get(ctx, accessTokenApiName, p, &token)
if err != nil {
return nil, resp, err
}
Expand Down
2 changes: 1 addition & 1 deletion bitbucket/access_tokens_users_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestListUserTokens(t *testing.T) {

client, _ := NewClient(server.URL, nil)
ctx := context.Background()
tokens, _, err := client.AccessTokens.ListUserTokens(ctx, "user-id")
tokens, _, err := client.AccessTokens.ListUserTokens(ctx, "user-id", &ListOptions{})
assert.NoError(t, err)
assert.Len(t, tokens, 5)
}
Expand Down
62 changes: 60 additions & 2 deletions bitbucket/bitbucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,29 @@ type Client struct {

AccessTokens *AccessTokensService
Keys *KeysService
Projects *ProjectsService
}

type service struct {
client *Client
}

type Page struct {
// The following properties support the paged APIs.
Size int
Limit int
LastPage bool
Start int
// The next page start should be used with the ListOptions struct.
NextPageStart int
}

// Paged defines interface to be supported by responses from Paged APIs
type Paged interface {
Current() *Page
}

// ListResponse defines the common properties of a list response
type ListResponse struct {
Size int `json:"size"`
Limit int `json:"limit"`
Expand All @@ -45,6 +62,21 @@ type ListResponse struct {
NextPageStart int `json:"nextPageStart"`
}

func (r *ListResponse) Current() *Page {
return &Page{
r.Size,
r.Limit,
r.LastPage,
r.Start,
r.NextPageStart,
}
}

type ListOptions struct {
Limit uint
Start uint
}

type DateTime time.Time

func (t *DateTime) UnmarshalJSON(bytes []byte) error {
Expand Down Expand Up @@ -82,6 +114,7 @@ const (

type Response struct {
*http.Response
*Page
}

type ErrorResponse struct {
Expand Down Expand Up @@ -118,6 +151,7 @@ func NewClient(baseURL string, httpClient *http.Client) (*Client, error) {
c.common.client = c
c.AccessTokens = (*AccessTokensService)(&c.common)
c.Keys = (*KeysService)(&c.common)
c.Projects = (*ProjectsService)(&c.common)
return c, nil
}

Expand Down Expand Up @@ -169,8 +203,6 @@ func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Res
return resp, err
}

// If status code 4xx - parse errors and add to response?

if v == nil {
return resp, nil
}
Expand All @@ -182,6 +214,10 @@ func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Res
}
}

if p, ok := v.(Paged); ok {
resp.Page = p.Current()
}

return resp, nil
}

Expand All @@ -192,3 +228,25 @@ func CheckResponse(r *http.Response) error {

return &ErrorResponse{Response: r}
}

func (c *Client) Get(ctx context.Context, api, path string, v interface{}) (*Response, error) {
return c.GetPaged(ctx, api, path, v, nil)
}

func (c *Client) GetPaged(ctx context.Context, api, path string, v interface{}, opts *ListOptions) (*Response, error) {
req, err := c.NewRequest("GET", api, path, nil)
if err != nil {
return nil, err
}
if opts != nil {
query := req.URL.Query()
if opts.Limit != 0 {
query.Set("limit", fmt.Sprintf("%d", opts.Limit))
}
if opts.Start != 0 {
query.Set("start", fmt.Sprintf("%d", opts.Start))
}
req.URL.RawQuery = query.Encode()
}
return c.Do(ctx, req, v)
}
25 changes: 8 additions & 17 deletions bitbucket/keys_repos.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,14 @@ type SshKey struct {
Permission Permission
}

func (s *KeysService) ListRepositoryKeys(ctx context.Context, projectKey, repositorySlug string) ([]SshKey, *Response, error) {
func (s *KeysService) ListRepositoryKeys(ctx context.Context, projectKey, repositorySlug string, opts *ListOptions) ([]*SshKey, *Response, error) {
p := fmt.Sprintf("projects/%s/repos/%s/ssh", projectKey, repositorySlug)
req, err := s.client.NewRequest("GET", keysApiName, p, nil)
if err != nil {
return nil, nil, err
}
var list sshKeyList
resp, err := s.client.Do(ctx, req, &list)
resp, err := s.client.GetPaged(ctx, keysApiName, p, &list, opts)
if err != nil {
return nil, resp, err
}
keys := make([]SshKey, 0)
keys := make([]*SshKey, 0)
for _, k := range list.Values {
keys = append(keys, newSshKey(k))
}
Expand All @@ -51,17 +47,12 @@ func (s *KeysService) ListRepositoryKeys(ctx context.Context, projectKey, reposi

func (s *KeysService) GetRepositoryKey(ctx context.Context, projectKey, repositorySlug string, keyId uint64) (*SshKey, *Response, error) {
p := fmt.Sprintf("projects/%s/repos/%s/ssh/%d", projectKey, repositorySlug, keyId)
req, err := s.client.NewRequest("GET", keysApiName, p, nil)
if err != nil {
return nil, nil, err
}
var k internalSshKey
resp, err := s.client.Do(ctx, req, &k)
resp, err := s.client.Get(ctx, keysApiName, p, &k)
if err != nil {
return nil, resp, err
}
key := newSshKey(k)
return &key, resp, nil
return newSshKey(k), resp, nil
}

func (s *KeysService) CreateRepositoryKey(ctx context.Context, projectKey, repositorySlug string, key *SshKey) (*SshKey, *Response, error) {
Expand All @@ -83,7 +74,7 @@ func (s *KeysService) CreateRepositoryKey(ctx context.Context, projectKey, repos
return nil, resp, err
}
result := newSshKey(*k)
return &result, resp, nil
return result, resp, nil
}

func (s *KeysService) DeleteRepositoryKey(ctx context.Context, projectKey, repositorySlug string, keyId uint64) (*Response, error) {
Expand All @@ -95,8 +86,8 @@ func (s *KeysService) DeleteRepositoryKey(ctx context.Context, projectKey, repos
return s.client.Do(ctx, req, nil)
}

func newSshKey(k internalSshKey) SshKey {
return SshKey{
func newSshKey(k internalSshKey) *SshKey {
return &SshKey{
ID: k.Key.ID,
Text: k.Key.Text,
Label: k.Key.Label,
Expand Down
2 changes: 1 addition & 1 deletion bitbucket/keys_repos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestListRepositoryKeys(t *testing.T) {

client, _ := NewClient(server.URL, nil)
ctx := context.Background()
keys, _, err := client.Keys.ListRepositoryKeys(ctx, "PRJ", "repo")
keys, _, err := client.Keys.ListRepositoryKeys(ctx, "PRJ", "repo", &ListOptions{})
assert.NoError(t, err)
assert.Len(t, keys, 1)
assert.Equal(t, uint64(601), keys[0].ID)
Expand Down
5 changes: 5 additions & 0 deletions bitbucket/projects.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package bitbucket

type ProjectsService service

const projectsApiName = "api"
81 changes: 81 additions & 0 deletions bitbucket/projects_repos.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package bitbucket

import (
"context"
"fmt"
)

type repositoryList struct {
ListResponse
Repositories []*Repository `json:"values"`
}

type Repository struct {
ID uint64 `json:"id,omitempty"`
Slug string `json:"slug,omitempty"`
ScmID string `json:"scmId"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Public bool `json:"public,omitempty"`
Archived bool `json:"archived,omitempty"`
State RepositoryState `json:"state,omitempty"`
Links map[string][]Link `json:"links,omitempty"`
}

type Link struct {
Href string `json:"href"`
Name string `json:"name,omitempty"`
}

type RepositoryState string

const (
RepositoryStateAvailable RepositoryState = "AVAILABLE"
RepositoryStateInitFailed RepositoryState = "INITIALISATION_FAILED"
RepositoryStateInit RepositoryState = "INITIALISING"
RepositoryStateOffline RepositoryState = "OFFLINE"
)

func (s *ProjectsService) ListRepositories(ctx context.Context, projectKey string, opts *ListOptions) ([]*Repository, *Response, error) {
p := fmt.Sprintf("projects/%s/repos", projectKey)
var l repositoryList
resp, err := s.client.GetPaged(ctx, projectsApiName, p, &l, opts)
if err != nil {
return nil, resp, err
}
return l.Repositories, resp, nil
}

func (s *ProjectsService) GetRepository(ctx context.Context, projectKey, repositorySlug string) (*Repository, *Response, error) {
p := fmt.Sprintf("projects/%s/repos/%s", projectKey, repositorySlug)
var r Repository
resp, err := s.client.Get(ctx, projectsApiName, p, &r)
if err != nil {
return nil, resp, err
}
return &r, resp, nil
}

func (s *ProjectsService) CreateRepository(ctx context.Context, projectKey string, repo *Repository) (*Repository, *Response, error) {
p := fmt.Sprintf("projects/%s/repos", projectKey)
req, err := s.client.NewRequest("POST", projectsApiName, p, repo)
if err != nil {
return nil, nil, err
}

var r Repository
resp, err := s.client.Do(ctx, req, &r)
if err != nil {
return nil, resp, err
}
return &r, resp, nil
}

func (s *ProjectsService) DeleteRepository(ctx context.Context, projectKey, repositorySlug string) (*Response, error) {
p := fmt.Sprintf("projects/%s/repos/%s", projectKey, repositorySlug)
req, err := s.client.NewRequest("DELETE", projectsApiName, p, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
Loading

0 comments on commit 0aa42c3

Please sign in to comment.