Repository: cloudflare/cloudflare-go
Pull request #964 (2022-06-30 21:24:13)

Add R2 bucket binding
## Description
Added the ability to binding an R2 bucket to workers.  Cloudflare-go currently supports binding of text, kv namespaces, secrets, etc. but not R2 buckets.  This changes adds support for R2 bucket bindings.

## Has your change been tested?
Enhanced TestWorkers_ListWorkerBindingsMultiScript with an additional test for R2 bucket bindings.  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] I have added tests to cover my changes.
- [X] All new and existing tests passed.
- [X] This change is using publicly documented (api.cloudflare.com or developers.cloudflare.com) and stable APIs.



Patch:
diff --git a/workers.go b/workers.go
index 8bfb88685..342251185 100644
--- a/workers.go
+++ b/workers.go
@@ -103,6 +103,8 @@ const (
 	WorkerPlainTextBindingType WorkerBindingType = "plain_text"
 	// WorkerServiceBindingType is the type for service bindings.
 	WorkerServiceBindingType WorkerBindingType = "service"
+	// WorkerR2BucketBindingType is the type for R2 bucket bindings.
+	WorkerR2BucketBindingType WorkerBindingType = "r2_bucket"
 )
 
 // WorkerBindingListItem a struct representing an individual binding in a list of bindings.
@@ -325,6 +327,28 @@ func (b WorkerServiceBinding) serialize(bindingName string) (workerBindingMeta,
 	return meta, nil, nil
 }
 
+// WorkerR2BucketBinding is a binding to an R2 bucket.
+type WorkerR2BucketBinding struct {
+	BucketName string
+}
+
+// Type returns the type of the binding.
+func (b WorkerR2BucketBinding) Type() WorkerBindingType {
+	return WorkerR2BucketBindingType
+}
+
+func (b WorkerR2BucketBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
+	if b.BucketName == "" {
+		return nil, nil, errors.Errorf(`BucketName for binding "%s" cannot be empty`, bindingName)
+	}
+
+	return workerBindingMeta{
+		"name":        bindingName,
+		"type":        b.Type(),
+		"bucket_name": b.BucketName,
+	}, nil, nil
+}
+
 // Each binding that adds a part to the multipart form body will need
 // a unique part name so we just generate a random 128bit hex string.
 func getRandomPartName() string {
@@ -488,6 +512,11 @@ func (api *API) ListWorkerBindings(ctx context.Context, requestParams *WorkerReq
 			}
 		case WorkerSecretTextBindingType:
 			bindingListItem.Binding = WorkerSecretTextBinding{}
+		case WorkerR2BucketBindingType:
+			bucketName := jsonBinding["bucket_name"].(string)
+			bindingListItem.Binding = WorkerR2BucketBinding{
+				BucketName: bucketName,
+			}
 		default:
 			bindingListItem.Binding = WorkerInheritBinding{}
 		}


Test patch:
diff --git a/workers_test.go b/workers_test.go
index 35e8a8a01..becb32a63 100644
--- a/workers_test.go
+++ b/workers_test.go
@@ -136,6 +136,11 @@ const (
 			{
 				"name": "MY_NEW_BINDING",
 				"type": "some_imaginary_new_binding_type"
+			},
+			{
+				"name": "MY_BUCKET",
+				"type": "r2_bucket",
+				"bucket_name": "bucket"
 			}
 		],
 		"success": true,
@@ -1029,7 +1034,7 @@ func TestWorkers_ListWorkerBindingsMultiScript(t *testing.T) {
 	assert.NoError(t, err)
 
 	assert.Equal(t, successResponse, res.Response)
-	assert.Equal(t, 6, len(res.BindingList))
+	assert.Equal(t, 7, len(res.BindingList))
 
 	assert.Equal(t, res.BindingList[0], WorkerBindingListItem{
 		Name: "MY_KV",
@@ -1075,6 +1080,14 @@ func TestWorkers_ListWorkerBindingsMultiScript(t *testing.T) {
 		Binding: WorkerInheritBinding{},
 	})
 	assert.Equal(t, WorkerInheritBindingType, res.BindingList[5].Binding.Type())
+
+	assert.Equal(t, res.BindingList[6], WorkerBindingListItem{
+		Name: "MY_BUCKET",
+		Binding: WorkerR2BucketBinding{
+			BucketName: "bucket",
+		},
+	})
+	assert.Equal(t, WorkerR2BucketBindingType, res.BindingList[6].Binding.Type())
 }
 
 func TestWorkers_UpdateWorkerRouteErrorsWhenMixingSingleAndMultiScriptProperties(t *testing.T) {