Skip to content

Commit f99b061

Browse files
authored
Allow paging to be optionally disabled (ktrysmt#192)
- Added a new option, `DisableAutoPaging`. This defaults to false for backwards compatibility, but allows the behaviour of automatically requesting all pages of a paged response to be disabled. - While doing this I split the `execute()` method into two versions - one that supports paging and another that doesn't. The reason I did this is that the previous version was adding the `pagelen` parameter for any paths containing `/repositories/`, but this breaks if you call `Repositories.ListForAccount()` with a role specified, in which case there's no trailing `/`. I figured it was better to be explicit about when to support paging or not. - Moved the `max_depth` support out of the client and into `Repository.ListFiles()`, which is where it's actually supported. I noticed that `Repository.ListBranches()` also specifies the max depth. It doesn't look like this is actually supported by the API to me, but I didn't want to risk breaking it in case I'm wrong. - Fixed a mistake in `GetCommitContents()` where it was using a `DELETE` instead of a `GET`.
1 parent 4d1e2d3 commit f99b061

13 files changed

+244
-70
lines changed

branchrestrictions.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ type BranchRestrictions struct {
1717

1818
func (b *BranchRestrictions) Gets(bo *BranchRestrictionsOptions) (interface{}, error) {
1919
urlStr := b.c.requestUrl("/repositories/%s/%s/branch-restrictions", bo.Owner, bo.RepoSlug)
20-
return b.c.execute("GET", urlStr, "")
20+
return b.c.executePaginated("GET", urlStr, "")
2121
}
2222

2323
func (b *BranchRestrictions) Create(bo *BranchRestrictionsOptions) (*BranchRestrictions, error) {

client.go

Lines changed: 69 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,12 @@ type Client struct {
4040
Teams teams
4141
Repositories *Repositories
4242
Workspaces *Workspace
43-
Pagelen uint64
44-
MaxDepth uint64
45-
apiBaseURL *url.URL
43+
Pagelen int
44+
MaxDepth int
45+
// DisableAutoPaging allows you to disable the default behavior of automatically requesting
46+
// all the pages for a paginated response.
47+
DisableAutoPaging bool
48+
apiBaseURL *url.URL
4649

4750
HttpClient *http.Client
4851
}
@@ -201,34 +204,37 @@ func (c *Client) executeRaw(method string, urlStr string, text string) (io.ReadC
201204
}
202205

203206
func (c *Client) execute(method string, urlStr string, text string) (interface{}, error) {
204-
// Use pagination if changed from default value
205-
const DEC_RADIX = 10
206-
if strings.Contains(urlStr, "/repositories/") {
207-
if c.Pagelen != DEFAULT_PAGE_LENGTH {
208-
urlObj, err := url.Parse(urlStr)
209-
if err != nil {
210-
return nil, err
211-
}
212-
q := urlObj.Query()
213-
q.Set("pagelen", strconv.FormatUint(c.Pagelen, DEC_RADIX))
214-
urlObj.RawQuery = q.Encode()
215-
urlStr = urlObj.String()
216-
}
207+
body := strings.NewReader(text)
208+
req, err := http.NewRequest(method, urlStr, body)
209+
if err != nil {
210+
return nil, err
211+
}
212+
if text != "" {
213+
req.Header.Set("Content-Type", "application/json")
214+
}
217215

218-
if c.MaxDepth != DEFAULT_MAX_DEPTH {
219-
urlObj, err := url.Parse(urlStr)
220-
if err != nil {
221-
return nil, err
222-
}
223-
q := urlObj.Query()
224-
q.Set("max_depth", strconv.FormatUint(c.MaxDepth, DEC_RADIX))
225-
urlObj.RawQuery = q.Encode()
226-
urlStr = urlObj.String()
216+
c.authenticateRequest(req)
217+
result, err := c.doRequest(req, false)
218+
if err != nil {
219+
return nil, err
220+
}
221+
222+
return result, nil
223+
}
224+
225+
func (c *Client) executePaginated(method string, urlStr string, text string) (interface{}, error) {
226+
if c.Pagelen != DEFAULT_PAGE_LENGTH {
227+
urlObj, err := url.Parse(urlStr)
228+
if err != nil {
229+
return nil, err
227230
}
231+
q := urlObj.Query()
232+
q.Set("pagelen", strconv.Itoa(c.Pagelen))
233+
urlObj.RawQuery = q.Encode()
234+
urlStr = urlObj.String()
228235
}
229236

230237
body := strings.NewReader(text)
231-
232238
req, err := http.NewRequest(method, urlStr, body)
233239
if err != nil {
234240
return nil, err
@@ -238,7 +244,7 @@ func (c *Client) execute(method string, urlStr string, text string) (interface{}
238244
}
239245

240246
c.authenticateRequest(req)
241-
result, err := c.doRequest(req, false)
247+
result, err := c.doPaginatedRequest(req, false)
242248
if err != nil {
243249
return nil, err
244250
}
@@ -318,13 +324,37 @@ func (c *Client) doRequest(req *http.Request, emptyResponse bool) (interface{},
318324
return resBody, err
319325
}
320326

327+
var result interface{}
328+
if err := json.Unmarshal(responseBytes, &result); err != nil {
329+
log.Println("Could not unmarshal JSON payload, returning raw response")
330+
return resBody, err
331+
}
332+
return result, nil
333+
}
334+
335+
func (c *Client) doPaginatedRequest(req *http.Request, emptyResponse bool) (interface{}, error) {
336+
resBody, err := c.doRawRequest(req, emptyResponse)
337+
if err != nil {
338+
return nil, err
339+
}
340+
if emptyResponse || resBody == nil {
341+
return nil, nil
342+
}
343+
344+
defer resBody.Close()
345+
346+
responseBytes, err := ioutil.ReadAll(resBody)
347+
if err != nil {
348+
return resBody, err
349+
}
350+
321351
responsePaginated := &Response{}
322352
err = json.Unmarshal(responseBytes, responsePaginated)
323353
if err == nil && len(responsePaginated.Values) > 0 {
324354
var values []interface{}
325355
for {
326356
values = append(values, responsePaginated.Values...)
327-
if len(responsePaginated.Next) == 0 {
357+
if c.DisableAutoPaging || len(responsePaginated.Next) == 0 {
328358
break
329359
}
330360
newReq, err := http.NewRequest(req.Method, responsePaginated.Next, nil)
@@ -407,3 +437,14 @@ func (c *Client) requestUrl(template string, args ...interface{}) string {
407437
}
408438
return c.GetApiBaseURL() + fmt.Sprintf(template, args...)
409439
}
440+
441+
func (c *Client) addMaxDepthParam(params *url.Values, customMaxDepth *int) {
442+
maxDepth := c.MaxDepth
443+
if customMaxDepth != nil && *customMaxDepth > 0 {
444+
maxDepth = *customMaxDepth
445+
}
446+
447+
if maxDepth != DEFAULT_MAX_DEPTH {
448+
params.Set("max_depth", strconv.Itoa(maxDepth))
449+
}
450+
}

commits.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package bitbucket
22

33
import (
4-
"net/url"
54
"encoding/json"
5+
"net/url"
66
)
77

88
type Commits struct {
@@ -12,7 +12,7 @@ type Commits struct {
1212
func (cm *Commits) GetCommits(cmo *CommitsOptions) (interface{}, error) {
1313
urlStr := cm.c.requestUrl("/repositories/%s/%s/commits/%s", cmo.Owner, cmo.RepoSlug, cmo.Branchortag)
1414
urlStr += cm.buildCommitsQuery(cmo.Include, cmo.Exclude)
15-
return cm.c.execute("GET", urlStr, "")
15+
return cm.c.executePaginated("GET", urlStr, "")
1616
}
1717

1818
func (cm *Commits) GetCommit(cmo *CommitsOptions) (interface{}, error) {
@@ -22,7 +22,7 @@ func (cm *Commits) GetCommit(cmo *CommitsOptions) (interface{}, error) {
2222

2323
func (cm *Commits) GetCommitComments(cmo *CommitsOptions) (interface{}, error) {
2424
urlStr := cm.c.requestUrl("/repositories/%s/%s/commit/%s/comments", cmo.Owner, cmo.RepoSlug, cmo.Revision)
25-
return cm.c.execute("DELETE", urlStr, "")
25+
return cm.c.executePaginated("GET", urlStr, "")
2626
}
2727

2828
func (cm *Commits) GetCommitComment(cmo *CommitsOptions) (interface{}, error) {
@@ -32,7 +32,7 @@ func (cm *Commits) GetCommitComment(cmo *CommitsOptions) (interface{}, error) {
3232

3333
func (cm *Commits) GetCommitStatuses(cmo *CommitsOptions) (interface{}, error) {
3434
urlStr := cm.c.requestUrl("/repositories/%s/%s/commit/%s/statuses", cmo.Owner, cmo.RepoSlug, cmo.Revision)
35-
return cm.c.execute("GET", urlStr, "")
35+
return cm.c.executePaginated("GET", urlStr, "")
3636
}
3737

3838
func (cm *Commits) GetCommitStatus(cmo *CommitsOptions, commitStatusKey string) (interface{}, error) {
@@ -50,7 +50,6 @@ func (cm *Commits) RemoveApprove(cmo *CommitsOptions) (interface{}, error) {
5050
return cm.c.execute("DELETE", urlStr, "")
5151
}
5252

53-
5453
func (cm *Commits) CreateCommitStatus(cmo *CommitsOptions, cso *CommitStatusOptions) (interface{}, error) {
5554
urlStr := cm.c.requestUrl("/repositories/%s/%s/commit/%s/statuses/build", cmo.Owner, cmo.RepoSlug, cmo.Revision)
5655
data, err := json.Marshal(cso)
@@ -60,7 +59,6 @@ func (cm *Commits) CreateCommitStatus(cmo *CommitsOptions, cso *CommitStatusOpti
6059
return cm.c.execute("POST", urlStr, string(data))
6160
}
6261

63-
6462
func (cm *Commits) buildCommitsQuery(include, exclude string) string {
6563

6664
p := url.Values{}

downloads.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ func (dl *Downloads) Create(do *DownloadsOptions) (interface{}, error) {
1111

1212
func (dl *Downloads) List(do *DownloadsOptions) (interface{}, error) {
1313
urlStr := dl.c.requestUrl("/repositories/%s/%s/downloads", do.Owner, do.RepoSlug)
14-
return dl.c.execute("GET", urlStr, "")
14+
return dl.c.executePaginated("GET", urlStr, "")
1515
}

issues.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func (p *Issues) Gets(io *IssuesOptions) (interface{}, error) {
3737
url.RawQuery = query.Encode()
3838
}
3939

40-
return p.c.execute("GET", url.String(), "")
40+
return p.c.executePaginated("GET", url.String(), "")
4141
}
4242

4343
func (p *Issues) Get(io *IssuesOptions) (interface{}, error) {

pipelines.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func (p *Pipelines) List(po *PipelinesOptions) (interface{}, error) {
4646
urlStr = parsed.String()
4747
}
4848

49-
return p.c.execute("GET", urlStr, "")
49+
return p.c.executePaginated("GET", urlStr, "")
5050
}
5151

5252
func (p *Pipelines) Get(po *PipelinesOptions) (interface{}, error) {
@@ -90,7 +90,7 @@ func (p *Pipelines) ListSteps(po *PipelinesOptions) (interface{}, error) {
9090
urlStr = parsed.String()
9191
}
9292

93-
return p.c.execute("GET", urlStr, "")
93+
return p.c.executePaginated("GET", urlStr, "")
9494
}
9595

9696
func (p *Pipelines) GetStep(po *PipelinesOptions) (interface{}, error) {

pullrequests.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func (p *PullRequests) Gets(po *PullRequestsOptions) (interface{}, error) {
6565
urlStr = parsed.String()
6666
}
6767

68-
return p.c.execute("GET", urlStr, "")
68+
return p.c.executePaginated("GET", urlStr, "")
6969
}
7070

7171
func (p *PullRequests) Get(po *PullRequestsOptions) (interface{}, error) {
@@ -75,7 +75,7 @@ func (p *PullRequests) Get(po *PullRequestsOptions) (interface{}, error) {
7575

7676
func (p *PullRequests) Activities(po *PullRequestsOptions) (interface{}, error) {
7777
urlStr := p.c.GetApiBaseURL() + "/repositories/" + po.Owner + "/" + po.RepoSlug + "/pullrequests/activity"
78-
return p.c.execute("GET", urlStr, "")
78+
return p.c.executePaginated("GET", urlStr, "")
7979
}
8080

8181
func (p *PullRequests) Activity(po *PullRequestsOptions) (interface{}, error) {
@@ -85,7 +85,7 @@ func (p *PullRequests) Activity(po *PullRequestsOptions) (interface{}, error) {
8585

8686
func (p *PullRequests) Commits(po *PullRequestsOptions) (interface{}, error) {
8787
urlStr := p.c.GetApiBaseURL() + "/repositories/" + po.Owner + "/" + po.RepoSlug + "/pullrequests/" + po.ID + "/commits"
88-
return p.c.execute("GET", urlStr, "")
88+
return p.c.executePaginated("GET", urlStr, "")
8989
}
9090

9191
func (p *PullRequests) Patch(po *PullRequestsOptions) (interface{}, error) {
@@ -158,7 +158,7 @@ func (p *PullRequests) UpdateComment(co *PullRequestCommentOptions) (interface{}
158158

159159
func (p *PullRequests) GetComments(po *PullRequestsOptions) (interface{}, error) {
160160
urlStr := p.c.GetApiBaseURL() + "/repositories/" + po.Owner + "/" + po.RepoSlug + "/pullrequests/" + po.ID + "/comments/"
161-
return p.c.execute("GET", urlStr, "")
161+
return p.c.executePaginated("GET", urlStr, "")
162162
}
163163

164164
func (p *PullRequests) GetComment(po *PullRequestsOptions) (interface{}, error) {
@@ -189,7 +189,7 @@ func (p *PullRequests) Statuses(po *PullRequestsOptions) (interface{}, error) {
189189
parsed.RawQuery = query.Encode()
190190
urlStr = parsed.String()
191191
}
192-
return p.c.execute("GET", urlStr, "")
192+
return p.c.executePaginated("GET", urlStr, "")
193193
}
194194

195195
func (p *PullRequests) buildPullRequestBody(po *PullRequestsOptions) (string, error) {

repositories.go

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,10 @@ type Repositories struct {
2525
}
2626

2727
type RepositoriesRes struct {
28-
Page int32
29-
Pagelen int32
30-
MaxDepth int32
31-
Size int32
32-
Items []Repository
28+
Page int32
29+
Pagelen int32
30+
Size int32
31+
Items []Repository
3332
}
3433

3534
func (r *Repositories) ListForAccount(ro *RepositoriesOptions) (*RepositoriesRes, error) {
@@ -41,7 +40,7 @@ func (r *Repositories) ListForAccount(ro *RepositoriesOptions) (*RepositoriesRes
4140
if ro.Role != "" {
4241
urlStr += "?role=" + ro.Role
4342
}
44-
repos, err := r.c.execute("GET", urlStr, "")
43+
repos, err := r.c.executePaginated("GET", urlStr, "")
4544
if err != nil {
4645
return nil, err
4746
}
@@ -55,7 +54,7 @@ func (r *Repositories) ListForTeam(ro *RepositoriesOptions) (*RepositoriesRes, e
5554

5655
func (r *Repositories) ListPublic() (*RepositoriesRes, error) {
5756
urlStr := r.c.requestUrl("/repositories/")
58-
repos, err := r.c.execute("GET", urlStr, "")
57+
repos, err := r.c.executePaginated("GET", urlStr, "")
5958
if err != nil {
6059
return nil, err
6160
}
@@ -87,21 +86,16 @@ func decodeRepositories(reposResponse interface{}) (*RepositoriesRes, error) {
8786
if !ok {
8887
pagelen = 0
8988
}
90-
max_depth, ok := reposResponseMap["max_width"].(float64)
91-
if !ok {
92-
max_depth = 0
93-
}
9489
size, ok := reposResponseMap["size"].(float64)
9590
if !ok {
9691
size = 0
9792
}
9893

9994
repositories := RepositoriesRes{
100-
Page: int32(page),
101-
Pagelen: int32(pagelen),
102-
MaxDepth: int32(max_depth),
103-
Size: int32(size),
104-
Items: repos,
95+
Page: int32(page),
96+
Pagelen: int32(pagelen),
97+
Size: int32(size),
98+
Items: repos,
10599
}
106100
return &repositories, nil
107101
}

repository.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,20 @@ func (r *Repository) Get(ro *RepositoryOptions) (*Repository, error) {
243243

244244
func (r *Repository) ListFiles(ro *RepositoryFilesOptions) ([]RepositoryFile, error) {
245245
filePath := path.Join("/repositories", ro.Owner, ro.RepoSlug, "src", ro.Ref, ro.Path) + "/"
246+
246247
urlStr := r.c.requestUrl(filePath)
247-
response, err := r.c.execute("GET", urlStr, "")
248+
url, err := url.Parse(urlStr)
249+
if err != nil {
250+
return nil, err
251+
}
252+
253+
query := url.Query()
254+
r.c.addMaxDepthParam(&query, nil)
255+
url.RawQuery = query.Encode()
256+
257+
urlStr = url.String()
258+
259+
response, err := r.c.executePaginated("GET", urlStr, "")
248260
if err != nil {
249261
return nil, err
250262
}
@@ -349,7 +361,7 @@ func (r *Repository) ListBranches(rbo *RepositoryBranchOptions) (*RepositoryBran
349361
}
350362

351363
if rbo.MaxDepth > 0 {
352-
params.Add("max_depth", strconv.Itoa(rbo.MaxDepth))
364+
r.c.addMaxDepthParam(&params, &rbo.MaxDepth)
353365
}
354366

355367
urlStr := r.c.requestUrl("/repositories/%s/%s/refs/branches?%s", rbo.Owner, rbo.RepoSlug, params.Encode())
@@ -503,18 +515,18 @@ func (r *Repository) Delete(ro *RepositoryOptions) (interface{}, error) {
503515

504516
func (r *Repository) ListWatchers(ro *RepositoryOptions) (interface{}, error) {
505517
urlStr := r.c.requestUrl("/repositories/%s/%s/watchers", ro.Owner, ro.RepoSlug)
506-
return r.c.execute("GET", urlStr, "")
518+
return r.c.executePaginated("GET", urlStr, "")
507519
}
508520

509521
func (r *Repository) ListForks(ro *RepositoryOptions) (interface{}, error) {
510522
urlStr := r.c.requestUrl("/repositories/%s/%s/forks", ro.Owner, ro.RepoSlug)
511-
return r.c.execute("GET", urlStr, "")
523+
return r.c.executePaginated("GET", urlStr, "")
512524
}
513525

514526
func (r *Repository) ListDefaultReviewers(ro *RepositoryOptions) (*DefaultReviewers, error) {
515527
urlStr := r.c.requestUrl("/repositories/%s/%s/default-reviewers?pagelen=1", ro.Owner, ro.RepoSlug)
516528

517-
res, err := r.c.execute("GET", urlStr, "")
529+
res, err := r.c.executePaginated("GET", urlStr, "")
518530
if err != nil {
519531
return nil, err
520532
}

0 commit comments

Comments
 (0)