Skip to content

Commit 90ada25

Browse files
Add user SSH keys. Add List deploy keys. Fix keyword search in List repositories (#294)
1 parent c56d071 commit 90ada25

11 files changed

+494
-13
lines changed

bitbucket.go

+7
Original file line numberDiff line numberDiff line change
@@ -663,3 +663,10 @@ func (dk *DeployKeyOptions) WithContext(ctx context.Context) *DeployKeyOptions {
663663
dk.ctx = ctx
664664
return dk
665665
}
666+
667+
type SSHKeyOptions struct {
668+
Owner string `json:"owner"`
669+
Uuid string `json:"uuid"`
670+
Label string `json:"label"`
671+
Key string `json:"key"`
672+
}

client.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func apiBaseUrl() (*url.URL, error) {
3636

3737
type Client struct {
3838
Auth *auth
39-
Users users
39+
Users *Users
4040
User user
4141
Teams teams
4242
Repositories *Repositories
@@ -191,7 +191,10 @@ func injectClient(a *auth) *Client {
191191
Downloads: &Downloads{c: c},
192192
DeployKeys: &DeployKeys{c: c},
193193
}
194-
c.Users = &Users{c: c}
194+
c.Users = &Users{
195+
c: c,
196+
SSHKeys: &SSHKeys{c: c},
197+
}
195198
c.User = &User{c: c}
196199
c.Teams = &Teams{c: c}
197200
c.Workspaces = &Workspace{c: c, Repositories: c.Repositories, Permissions: &Permission{c: c}}

deploykeys.go

+63
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package bitbucket
22

33
import (
44
"encoding/json"
5+
"errors"
56

67
"github.com/mitchellh/mapstructure"
78
)
@@ -17,6 +18,14 @@ type DeployKey struct {
1718
Comment string `json:"comment"`
1819
}
1920

21+
type DeployKeysRes struct {
22+
Page int32
23+
Pagelen int32
24+
MaxDepth int32
25+
Size int32
26+
Items []DeployKey
27+
}
28+
2029
func decodeDeployKey(response interface{}) (*DeployKey, error) {
2130
respMap := response.(map[string]interface{})
2231

@@ -33,6 +42,50 @@ func decodeDeployKey(response interface{}) (*DeployKey, error) {
3342
return deployKey, nil
3443
}
3544

45+
func decodeDeployKeys(deployKeysResponse interface{}) (*DeployKeysRes, error) {
46+
deployKeysResponseMap, ok := deployKeysResponse.(map[string]interface{})
47+
if !ok {
48+
return nil, errors.New("not a valid format")
49+
}
50+
51+
repoArray := deployKeysResponseMap["values"].([]interface{})
52+
var deployKeys []DeployKey
53+
for _, deployKeyEntry := range repoArray {
54+
var deployKey DeployKey
55+
err := mapstructure.Decode(deployKeyEntry, &deployKey)
56+
if err == nil {
57+
deployKeys = append(deployKeys, deployKey)
58+
}
59+
}
60+
61+
page, ok := deployKeysResponseMap["page"].(float64)
62+
if !ok {
63+
page = 0
64+
}
65+
66+
pagelen, ok := deployKeysResponseMap["pagelen"].(float64)
67+
if !ok {
68+
pagelen = 0
69+
}
70+
maxDepth, ok := deployKeysResponseMap["max_width"].(float64)
71+
if !ok {
72+
maxDepth = 0
73+
}
74+
size, ok := deployKeysResponseMap["size"].(float64)
75+
if !ok {
76+
size = 0
77+
}
78+
79+
repositories := DeployKeysRes{
80+
Page: int32(page),
81+
Pagelen: int32(pagelen),
82+
MaxDepth: int32(maxDepth),
83+
Size: int32(size),
84+
Items: deployKeys,
85+
}
86+
return &repositories, nil
87+
}
88+
3689
func buildDeployKeysBody(opt *DeployKeyOptions) (string, error) {
3790
body := map[string]interface{}{}
3891
body["label"] = opt.Label
@@ -74,3 +127,13 @@ func (dk *DeployKeys) Delete(opt *DeployKeyOptions) (interface{}, error) {
74127
urlStr := dk.c.requestUrl("/repositories/%s/%s/deploy-keys/%d", opt.Owner, opt.RepoSlug, opt.Id)
75128
return dk.c.execute("DELETE", urlStr, "")
76129
}
130+
131+
func (dk *DeployKeys) List(opt *DeployKeyOptions) (*DeployKeysRes, error) {
132+
urlStr := dk.c.requestUrl("/repositories/%s/%s/deploy-keys", opt.Owner, opt.RepoSlug)
133+
response, err := dk.c.execute("GET", urlStr, "")
134+
if err != nil {
135+
return nil, err
136+
}
137+
138+
return decodeDeployKeys(response)
139+
}

go.mod

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ require (
66
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect
77
github.com/k0kubun/pp v3.0.1+incompatible
88
github.com/kr/pretty v0.3.0 // indirect
9-
github.com/mattn/go-colorable v0.0.9 // indirect
10-
github.com/mattn/go-isatty v0.0.16 // indirect
9+
github.com/mattn/go-colorable v0.1.13 // indirect
1110
github.com/mitchellh/mapstructure v1.5.0
1211
github.com/rogpeppe/go-internal v1.9.0 // indirect
1312
github.com/stretchr/testify v1.9.0

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
1717
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
1818
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
1919
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
20-
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
21-
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
20+
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
21+
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
2222
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
2323
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
2424
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=

repositories.go

+11-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package bitbucket
33
import (
44
"errors"
55
"fmt"
6+
"net/url"
67
)
78

89
//"github.com/k0kubun/pp"
@@ -35,16 +36,21 @@ func (r *Repositories) ListForAccount(ro *RepositoriesOptions) (*RepositoriesRes
3536
urlPath += fmt.Sprintf("/%s", ro.Owner)
3637
}
3738
urlStr := r.c.requestUrl(urlPath)
39+
urlAsUrl, err := url.Parse(urlStr)
40+
if err != nil {
41+
return nil, err
42+
}
43+
q := urlAsUrl.Query()
3844
if ro.Role != "" {
39-
urlStr += "?role=" + ro.Role
45+
q.Set("role", ro.Role)
4046
}
4147
if ro.Keyword != nil && *ro.Keyword != "" {
42-
if ro.Role == "" {
43-
urlStr += "?"
44-
}
4548
// https://developer.atlassian.com/cloud/bitbucket/rest/intro/#operators
46-
urlStr += fmt.Sprintf("q=full_name ~ \"%s\"", *ro.Keyword)
49+
query := fmt.Sprintf("full_name ~ \"%s\"", *ro.Keyword)
50+
q.Set("q", query)
4751
}
52+
urlAsUrl.RawQuery = q.Encode()
53+
urlStr = urlAsUrl.String()
4854
repos, err := r.c.executePaginated("GET", urlStr, "", ro.Page)
4955
if err != nil {
5056
return nil, err

ssh.go

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package bitbucket
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
7+
"github.com/mitchellh/mapstructure"
8+
)
9+
10+
type SSHKeys struct {
11+
c *Client
12+
}
13+
14+
type SSHKey struct {
15+
Uuid string `json:"uuid"`
16+
Label string `json:"label"`
17+
Key string `json:"key"`
18+
Comment string `json:"comment"`
19+
CreatedOm string `json:"created_on"`
20+
}
21+
22+
type SSHKeyRes struct {
23+
Page int32
24+
Pagelen int32
25+
MaxDepth int32
26+
Size int32
27+
Items []SSHKey
28+
}
29+
30+
func decodeSSHKey(response interface{}) (*SSHKey, error) {
31+
respMap := response.(map[string]interface{})
32+
33+
if respMap["type"] == "error" {
34+
return nil, DecodeError(respMap)
35+
}
36+
37+
var sshKey = new(SSHKey)
38+
err := mapstructure.Decode(respMap, sshKey)
39+
if err != nil {
40+
return nil, err
41+
}
42+
43+
return sshKey, nil
44+
}
45+
46+
func buildSSHKeysBody(opt *SSHKeyOptions) (string, error) {
47+
body := map[string]interface{}{}
48+
body["label"] = opt.Label
49+
body["key"] = opt.Key
50+
51+
data, err := json.Marshal(body)
52+
if err != nil {
53+
return "", err
54+
}
55+
56+
return string(data), nil
57+
}
58+
59+
func decodeSSHKeys(keysResponse interface{}) (*SSHKeyRes, error) {
60+
keysResponseMap, ok := keysResponse.(map[string]interface{})
61+
if !ok {
62+
return nil, errors.New("Not a valid format")
63+
}
64+
65+
keyArray := keysResponseMap["values"].([]interface{})
66+
var keys []SSHKey
67+
for _, keyEntry := range keyArray {
68+
var key SSHKey
69+
err := mapstructure.Decode(keyEntry, &key)
70+
if err == nil {
71+
keys = append(keys, key)
72+
}
73+
}
74+
75+
page, ok := keysResponseMap["page"].(float64)
76+
if !ok {
77+
page = 0
78+
}
79+
80+
pagelen, ok := keysResponseMap["pagelen"].(float64)
81+
if !ok {
82+
pagelen = 0
83+
}
84+
max_depth, ok := keysResponseMap["max_width"].(float64)
85+
if !ok {
86+
max_depth = 0
87+
}
88+
size, ok := keysResponseMap["size"].(float64)
89+
if !ok {
90+
size = 0
91+
}
92+
93+
keysResp := &SSHKeyRes{
94+
Page: int32(page),
95+
Pagelen: int32(pagelen),
96+
MaxDepth: int32(max_depth),
97+
Size: int32(size),
98+
Items: keys,
99+
}
100+
return keysResp, nil
101+
}
102+
103+
func (sk *SSHKeys) Create(ro *SSHKeyOptions) (*SSHKey, error) {
104+
data, err := buildSSHKeysBody(ro)
105+
if err != nil {
106+
return nil, err
107+
}
108+
urlStr := sk.c.requestUrl("/users/%s/ssh-keys", ro.Owner)
109+
response, err := sk.c.execute("POST", urlStr, data)
110+
if err != nil {
111+
return nil, err
112+
}
113+
114+
return decodeSSHKey(response)
115+
}
116+
117+
func (sk *SSHKeys) Get(ro *SSHKeyOptions) (*SSHKey, error) {
118+
urlStr := sk.c.requestUrl("/users/%s/ssh-keys/%s", ro.Owner, ro.Uuid)
119+
response, err := sk.c.execute("GET", urlStr, "")
120+
if err != nil {
121+
return nil, err
122+
}
123+
124+
return decodeSSHKey(response)
125+
}
126+
127+
func (sk *SSHKeys) Delete(ro *SSHKeyOptions) (interface{}, error) {
128+
urlStr := sk.c.requestUrl("/users/%s/ssh-keys/%s", ro.Owner, ro.Uuid)
129+
return sk.c.execute("DELETE", urlStr, "")
130+
}

0 commit comments

Comments
 (0)