Repository: cloudflare/cloudflare-go
Pull request #1063 (2022-08-26 21:05:50)

Add support for listing R2 buckets
## Description

Added the ability to list R2 buckets. A recent change (#1028) added support for creating and deleting R2 buckets but was missing the ability to list R2 buckets. This change adds support for listing R2 buckets.

## Has your change been tested?

Enhanced r2_bucket_test.go with an additional test (TestR2_ListBuckets) to list R2 buckets. Also verified this against an active cloudflare account.

## Types of changes

What sort of change does your code introduce/modify?

- [X] New feature (non-breaking change which adds functionality)

## Checklist:

- [X] My code follows the code style of this project.
- [X] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [X] I have added tests to cover my changes.
- [X] All new and existing tests passed.
- [ ] This change is using publicly documented (api.cloudflare.com or developers.cloudflare.com) and stable APIs.

[1]: https://help.github.com/articles/closing-issues-using-keywords/



Patch:
diff --git a/.changelog/1063.txt b/.changelog/1063.txt
new file mode 100644
index 000000000..e27d99dd1
--- /dev/null
+++ b/.changelog/1063.txt
@@ -0,0 +1,3 @@
+```release-note:enhancement
+r2: Add support for listing R2 buckets
+```
diff --git a/r2_bucket.go b/r2_bucket.go
index d946b633d..08de630e1 100644
--- a/r2_bucket.go
+++ b/r2_bucket.go
@@ -2,6 +2,7 @@ package cloudflare
 
 import (
 	"context"
+	"encoding/json"
 	"errors"
 	"fmt"
 	"net/http"
@@ -11,6 +12,55 @@ var (
 	ErrMissingBucketName = errors.New("require bucket name missing")
 )
 
+// R2Bucket defines a container for objects stored in R2 Storage.
+type R2Bucket struct {
+	Name         string `json:"name"`
+	CreationDate string `json:"creation_date,omitempty"`
+}
+
+// R2Buckets represents the map of buckets response from
+// the R2 buckets endpoint.
+type R2Buckets struct {
+	Buckets []R2Bucket `json:"buckets"`
+}
+
+// R2BucketListResponse represents the response from the list
+// R2 buckets endpoint.
+type R2BucketListResponse struct {
+	Result R2Buckets `json:"result"`
+	Response
+}
+
+type ListR2BucketsParams struct {
+	Name       string `url:"name_contains,omitempty"`
+	StartAfter string `url:"start_after,omitempty"`
+	PerPage    int64  `url:"per_page,omitempty"`
+	Order      string `url:"order,omitempty"`
+	Direction  string `url:"direction,omitempty"`
+	Cursor     string `url:"cursor,omitempty"`
+}
+
+// ListR2Buckets Lists R2 buckets.
+func (api *API) ListR2Buckets(ctx context.Context, rc *ResourceContainer, params ListR2BucketsParams) ([]R2Bucket, error) {
+	if rc.Identifier == "" {
+		return []R2Bucket{}, ErrMissingAccountID
+	}
+
+	uri := buildURI(fmt.Sprintf("/accounts/%s/r2/buckets", rc.Identifier), params)
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return []R2Bucket{}, err
+	}
+
+	var r2BucketListResponse R2BucketListResponse
+	err = json.Unmarshal(res, &r2BucketListResponse)
+	if err != nil {
+		return []R2Bucket{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return r2BucketListResponse.Result.Buckets, nil
+}
+
 type CreateR2BucketParameters struct {
 	Name string `json:"name,omitempty"`
 }


Test patch:
diff --git a/r2_bucket_test.go b/r2_bucket_test.go
index 92926d5e9..42168838c 100644
--- a/r2_bucket_test.go
+++ b/r2_bucket_test.go
@@ -11,6 +11,40 @@ import (
 
 const testBucketName = "example-bucket"
 
+func TestR2_ListBuckets(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc(fmt.Sprintf("/accounts/%s/r2/buckets", testAccountID), func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprintf(w, `{
+  "success": true,
+  "errors": [],
+  "messages": [],
+  "result": {
+	"buckets": [
+		{
+			"name": "example-bucket",
+			"creation_date": "2022-06-24T19:58:49.477Z"
+		}
+	]
+  }
+}`)
+	})
+
+	want := []R2Bucket{
+		{
+			Name:         "example-bucket",
+			CreationDate: "2022-06-24T19:58:49.477Z",
+		},
+	}
+	actual, err := client.ListR2Buckets(context.Background(), AccountIdentifier(testAccountID), ListR2BucketsParams{})
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, actual)
+	}
+}
+
 func TestR2_CreateBucket(t *testing.T) {
 	setup()
 	defer teardown()