Skip to content
This repository was archived by the owner on Jan 23, 2022. It is now read-only.

Commit b514328

Browse files
author
TP Honey
committed
(feat) normalise sha usage, fix updates, bitbucket cloud update/create
1 parent 8301645 commit b514328

28 files changed

+1350
-49
lines changed

go.sum

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
22
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
3-
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
43
github.com/h2non/gock v1.0.9 h1:17gCehSo8ZOgEsFKpQgqHiR7VLyjxdAG3lkhVvO9QZU=
5-
github.com/h2non/gock v1.0.9/go.mod h1:CZMcB0Lg5IWnr9bF79pPMg9WeV6WumxQiUJ1UvdO1iE=
4+
github.com/h2non/gock v1.0.9/go.mod h1:CZMcB0Lg5IWnr9bF79pPMg9WeV6WumxQiUJ1UvdO1iE=

scm/content.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,10 @@ import "context"
99
type (
1010
// Content stores the contents of a repository file.
1111
Content struct {
12-
Path string
13-
Data []byte
14-
15-
// the hash of the blob, sometimes referred to
16-
// as the blob id or blob sha. this is the equivalent
17-
// to running the git hash-object command.
18-
Hash string
12+
Path string
13+
Data []byte
14+
Sha string
15+
BlobID string
1916
}
2017

2118
// ContentParams provide parameters for creating and
@@ -26,6 +23,7 @@ type (
2623
Message string
2724
Data []byte
2825
Sha string
26+
BlobID string
2927
Signature Signature
3028
}
3129

scm/driver/bitbucket/bitbucket.go

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"context"
1111
"encoding/json"
1212
"io"
13+
"mime/multipart"
1314
"net/url"
1415
"strings"
1516

@@ -65,12 +66,44 @@ func (c *wrapper) do(ctx context.Context, method, path string, in, out interface
6566
// if we are posting or putting data, we need to
6667
// write it to the body of the request.
6768
if in != nil {
68-
buf := new(bytes.Buffer)
69-
json.NewEncoder(buf).Encode(in)
70-
req.Header = map[string][]string{
71-
"Content-Type": {"application/json"},
69+
// create or update content
70+
switch content := in.(type) {
71+
case *contentCreateUpdate:
72+
// add the content to the multipart
73+
myReader := strings.NewReader(string(content.Content))
74+
var b bytes.Buffer
75+
w := multipart.NewWriter(&b)
76+
var fw io.Writer
77+
fw, _ = w.CreateFormFile(content.Files, "")
78+
_, _ = io.Copy(fw, myReader)
79+
// add the other fields
80+
if content.Message != "" {
81+
_ = w.WriteField("message", content.Message)
82+
}
83+
if content.Branch != "" {
84+
_ = w.WriteField("branch", content.Branch)
85+
}
86+
if content.Sha != "" {
87+
_ = w.WriteField("parents", content.Sha)
88+
}
89+
if content.Author != "" {
90+
_ = w.WriteField("author", content.Author)
91+
}
92+
w.Close()
93+
// write the multipart response to the body
94+
req.Body = &b
95+
// write the content type that contains the length of the multipart
96+
req.Header = map[string][]string{
97+
"Content-Type": {w.FormDataContentType()},
98+
}
99+
default:
100+
buf := new(bytes.Buffer)
101+
json.NewEncoder(buf).Encode(in)
102+
req.Header = map[string][]string{
103+
"Content-Type": {"application/json"},
104+
}
105+
req.Body = buf
72106
}
73-
req.Body = buf
74107
}
75108

76109
// execute the http request

scm/driver/bitbucket/content.go

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,51 @@ func (s *contentService) Find(ctx context.Context, repo, path, ref string) (*scm
2020
endpoint := fmt.Sprintf("/2.0/repositories/%s/src/%s/%s", repo, ref, path)
2121
out := new(bytes.Buffer)
2222
res, err := s.client.do(ctx, "GET", endpoint, nil, out)
23-
return &scm.Content{
23+
content := &scm.Content{
2424
Path: path,
2525
Data: out.Bytes(),
26-
}, res, err
26+
}
27+
if err != nil {
28+
return content, res, err
29+
}
30+
metaEndpoint := fmt.Sprintf("/2.0/repositories/%s/src/%s/%s?format=meta", repo, ref, path)
31+
metaOut := new(metaContent)
32+
metaRes, metaErr := s.client.do(ctx, "GET", metaEndpoint, nil, metaOut)
33+
if metaErr == nil {
34+
content.Sha = metaOut.Commit.Hash
35+
return content, metaRes, metaErr
36+
} else {
37+
// do not risk that returning an error if getting the meta fails.
38+
return content, res, err
39+
}
2740
}
2841

2942
func (s *contentService) Create(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
30-
return nil, scm.ErrNotSupported
43+
endpoint := fmt.Sprintf("/2.0/repositories/%s/src", repo)
44+
in := &contentCreateUpdate{
45+
Files: path,
46+
Message: params.Message,
47+
Branch: params.Branch,
48+
Content: params.Data,
49+
Author: fmt.Sprintf("%s <%s>", params.Signature.Name, params.Signature.Email),
50+
}
51+
res, err := s.client.do(ctx, "POST", endpoint, in, nil)
52+
return res, err
3153
}
3254

3355
func (s *contentService) Update(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
34-
return nil, scm.ErrNotSupported
56+
// https://jira.atlassian.com/browse/BCLOUD-20424?error=login_required&error_description=Login+required&state=196d85f7-a181-4b63-babe-0b567858d8f5 ugh :(
57+
endpoint := fmt.Sprintf("/2.0/repositories/%s/src", repo)
58+
in := &contentCreateUpdate{
59+
Files: path,
60+
Message: params.Message,
61+
Branch: params.Branch,
62+
Content: params.Data,
63+
Sha: params.Sha,
64+
Author: fmt.Sprintf("%s <%s>", params.Signature.Name, params.Signature.Email),
65+
}
66+
res, err := s.client.do(ctx, "POST", endpoint, in, nil)
67+
return res, err
3568
}
3669

3770
func (s *contentService) Delete(ctx context.Context, repo, path, ref string) (*scm.Response, error) {
@@ -57,6 +90,22 @@ type content struct {
5790
Attributes []string `json:"attributes"`
5891
}
5992

93+
type metaContent struct {
94+
Path string `json:"path"`
95+
Commit struct {
96+
Hash string `json:"hash"`
97+
} `json:"commit"`
98+
}
99+
100+
type contentCreateUpdate struct {
101+
Files string `json:"files"`
102+
Branch string `json:"branch"`
103+
Message string `json:"message"`
104+
Content []byte `json:"content"`
105+
Sha string `json:"sha"`
106+
Author string `json:"author"`
107+
}
108+
60109
func convertContentInfoList(from *contents) []*scm.ContentInfo {
61110
to := []*scm.ContentInfo{}
62111
for _, v := range from.Values {

scm/driver/bitbucket/content_test.go

Lines changed: 133 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ func TestContentFind(t *testing.T) {
2525
Type("text/plain").
2626
File("testdata/content.txt")
2727

28+
gock.New("https://api.bitbucket.org").
29+
MatchParam("format", "meta").
30+
Get("/2.0/repositories/atlassian/atlaskit/src/425863f9dbe56d70c8dcdbf2e4e0805e85591fcc/README").
31+
Reply(200).
32+
Type("application/json").
33+
File("testdata/content.json")
34+
2835
client, _ := New("https://api.bitbucket.org")
2936
got, _, err := client.Contents.Find(context.Background(), "atlassian/atlaskit", "README", "425863f9dbe56d70c8dcdbf2e4e0805e85591fcc")
3037
if err != nil {
@@ -41,19 +48,137 @@ func TestContentFind(t *testing.T) {
4148
}
4249
}
4350

51+
func TestContentFindNoMeta(t *testing.T) {
52+
defer gock.Off()
53+
54+
gock.New("https://api.bitbucket.org").
55+
Get("/2.0/repositories/atlassian/atlaskit/src/425863f9dbe56d70c8dcdbf2e4e0805e85591fcc/README").
56+
Reply(200).
57+
Type("text/plain").
58+
File("testdata/content.txt")
59+
60+
gock.New("https://api.bitbucket.org").
61+
MatchParam("format", "meta").
62+
Get("/2.0/repositories/atlassian/atlaskit/src/425863f9dbe56d70c8dcdbf2e4e0805e85591fcc/README").
63+
Reply(404).
64+
Type("application/json").
65+
File("testdata/content_fail.json")
66+
67+
client, _ := New("https://api.bitbucket.org")
68+
got, _, err := client.Contents.Find(context.Background(), "atlassian/atlaskit", "README", "425863f9dbe56d70c8dcdbf2e4e0805e85591fcc")
69+
if err != nil {
70+
t.Error(err)
71+
}
72+
73+
want := new(scm.Content)
74+
raw, _ := ioutil.ReadFile("testdata/content.json.fail")
75+
json.Unmarshal(raw, want)
76+
77+
if diff := cmp.Diff(got, want); diff != "" {
78+
t.Errorf("Unexpected Results")
79+
t.Log(diff)
80+
}
81+
}
82+
4483
func TestContentCreate(t *testing.T) {
45-
content := new(contentService)
46-
_, err := content.Create(context.Background(), "atlassian/atlaskit", "README", nil)
47-
if err != scm.ErrNotSupported {
48-
t.Errorf("Expect Not Supported error")
84+
defer gock.Off()
85+
86+
gock.New("https://api.bitbucket.org").
87+
Post("/2.0/repositories/atlassian/atlaskit/src").
88+
Reply(201).
89+
Type("application/json")
90+
91+
params := &scm.ContentParams{
92+
Message: "my commit message",
93+
Data: []byte("bXkgbmV3IGZpbGUgY29udGVudHM="),
94+
Signature: scm.Signature{
95+
Name: "Monalisa Octocat",
96+
97+
},
98+
}
99+
100+
client := NewDefault()
101+
res, err := client.Contents.Create(
102+
context.Background(),
103+
"atlassian/atlaskit",
104+
"test/hello",
105+
params,
106+
)
107+
108+
if err != nil {
109+
t.Error(err)
110+
return
111+
}
112+
113+
if res.Status != 201 {
114+
t.Errorf("Unexpected Results")
49115
}
116+
50117
}
51118

52119
func TestContentUpdate(t *testing.T) {
53-
content := new(contentService)
54-
_, err := content.Update(context.Background(), "atlassian/atlaskit", "README", nil)
55-
if err != scm.ErrNotSupported {
56-
t.Errorf("Expect Not Supported error")
120+
defer gock.Off()
121+
122+
gock.New("https://api.bitbucket.org").
123+
Post("/2.0/repositories/atlassian/atlaskit/src").
124+
Reply(201).
125+
Type("application/json")
126+
127+
params := &scm.ContentParams{
128+
Message: "my commit message",
129+
Data: []byte("bXkgbmV3IGZpbGUgY29udGVudHM="),
130+
Signature: scm.Signature{
131+
Name: "Monalisa Octocat",
132+
133+
},
134+
}
135+
136+
client := NewDefault()
137+
res, err := client.Contents.Update(
138+
context.Background(),
139+
"atlassian/atlaskit",
140+
"test/hello",
141+
params,
142+
)
143+
144+
if err != nil {
145+
t.Error(err)
146+
return
147+
}
148+
149+
if res.Status != 201 {
150+
t.Errorf("Unexpected Results")
151+
}
152+
}
153+
154+
func TestContentUpdateBadCommitID(t *testing.T) {
155+
defer gock.Off()
156+
157+
gock.New("https://api.bitbucket.org").
158+
Post("/2.0/repositories/atlassian/atlaskit/src").
159+
Reply(400).
160+
Type("application/json").
161+
File("testdata/content_update.json.fail")
162+
163+
params := &scm.ContentParams{
164+
Message: "my commit message",
165+
Data: []byte("bXkgbmV3IGZpbGUgY29udGVudHM="),
166+
Sha: "bad commit",
167+
Signature: scm.Signature{
168+
Name: "Monalisa Octocat",
169+
170+
},
171+
}
172+
173+
client := NewDefault()
174+
_, err := client.Contents.Update(
175+
context.Background(),
176+
"atlassian/atlaskit",
177+
"test/hello",
178+
params,
179+
)
180+
if err.Error() != "parents: Commit not found: 1a7eba6c-d4fe-47b7-b767-859abc660efc" {
181+
t.Errorf("Expecting 'parents: Commit not found: 1a7eba6c-d4fe-47b7-b767-859abc660efc'")
57182
}
58183
}
59184

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"mimetype": null,
3+
"links": {
4+
"self": {
5+
"href": "https://api.bitbucket.org/2.0/repositories/tphoney/scm-test/src/0846a192175701903ddd9264ece31922d8437c1a/README.md"
6+
},
7+
"meta": {
8+
"href": "https://api.bitbucket.org/2.0/repositories/tphoney/scm-test/src/0846a192175701903ddd9264ece31922d8437c1a/README.md?format=meta"
9+
},
10+
"history": {
11+
"href": "https://api.bitbucket.org/2.0/repositories/tphoney/scm-test/filehistory/0846a192175701903ddd9264ece31922d8437c1a/README.md"
12+
}
13+
},
14+
"escaped_path": "README.md",
15+
"path": "README.md",
16+
"commit": {
17+
"type": "commit",
18+
"hash": "0846a192175701903ddd9264ece31922d8437c1a",
19+
"links": {
20+
"self": {
21+
"href": "https://api.bitbucket.org/2.0/repositories/tphoney/scm-test/commit/0846a192175701903ddd9264ece31922d8437c1a"
22+
},
23+
"html": {
24+
"href": "https://bitbucket.org/tphoney/scm-test/commits/0846a192175701903ddd9264ece31922d8437c1a"
25+
}
26+
}
27+
},
28+
"attributes": [],
29+
"type": "commit_file",
30+
"size": 39
31+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"Path": "README",
3+
"Data": "SEVMTE8gV09STEQK"
4+
}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
22
"Path": "README",
3-
"Data": "SEVMTE8gV09STEQK"
4-
}
3+
"Data": "SEVMTE8gV09STEQK",
4+
"Sha": "0846a192175701903ddd9264ece31922d8437c1a"
5+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "error",
3+
"error": {
4+
"message": "No such file or directory: README.md.asdas"
5+
}
6+
}

0 commit comments

Comments
 (0)