Repository: cloudflare/cloudflare-go
Pull request #949 (2022-06-23 06:12:18)

Add Durable Object bindings to Workers upload
Revisit #849 for Durable Objects only.

## Description

Describe your changes in detail through motivation and context. Why is
this change required? What problem does it solve? If it fixes an open
issue, link to the issue using GitHub's closing issues keywords[1].

## Has your change been tested?

Explain how the change has been tested and what you ran to confirm your
change affects other parts of the code. Automated tests are generally
expected and changes without tests should explain why they aren't
required.

## Screenshots (if appropriate):

## Types of changes

What sort of change does your code introduce/modify?

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)

## Checklist:

- [x] My code follows the code style of this project.
- [ ] 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.
- [x] 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/workers.go b/workers.go
index 897741f5f..8bfb88685 100644
--- a/workers.go
+++ b/workers.go
@@ -89,6 +89,8 @@ func (b WorkerBindingType) String() string {
 }
 
 const (
+	// WorkerDurableObjectBindingType is the type for Durable Object bindings.
+	WorkerDurableObjectBindingType WorkerBindingType = "durable_object_namespace"
 	// WorkerInheritBindingType is the type for inherited bindings.
 	WorkerInheritBindingType WorkerBindingType = "inherit"
 	// WorkerKvNamespaceBindingType is the type for KV Namespace bindings.
@@ -188,6 +190,32 @@ func (b WorkerKvNamespaceBinding) serialize(bindingName string) (workerBindingMe
 	}, nil, nil
 }
 
+// WorkerDurableObjectBinding is a binding to a Workers Durable Object
+//
+// https://api.cloudflare.com/#durable-objects-namespace-properties
+type WorkerDurableObjectBinding struct {
+	ClassName  string
+	ScriptName string
+}
+
+// Type returns the type of the binding.
+func (b WorkerDurableObjectBinding) Type() WorkerBindingType {
+	return WorkerDurableObjectBindingType
+}
+
+func (b WorkerDurableObjectBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
+	if b.ClassName == "" {
+		return nil, nil, errors.Errorf(`ClassName for binding "%s" cannot be empty`, bindingName)
+	}
+
+	return workerBindingMeta{
+		"name":        bindingName,
+		"type":        b.Type(),
+		"class_name":  b.ClassName,
+		"script_name": b.ScriptName,
+	}, nil, nil
+}
+
 // WorkerWebAssemblyBinding is a binding to a WebAssembly module
 //
 // https://developers.cloudflare.com/workers/archive/api/resource-bindings/webassembly-modules/
@@ -426,6 +454,13 @@ func (api *API) ListWorkerBindings(ctx context.Context, requestParams *WorkerReq
 		}
 
 		switch WorkerBindingType(bType) {
+		case WorkerDurableObjectBindingType:
+			class_name := jsonBinding["class_name"].(string)
+			script_name := jsonBinding["script_name"].(string)
+			bindingListItem.Binding = WorkerDurableObjectBinding{
+				ClassName:  class_name,
+				ScriptName: script_name,
+			}
 		case WorkerKvNamespaceBindingType:
 			namespaceID := jsonBinding["namespace_id"].(string)
 			bindingListItem.Binding = WorkerKvNamespaceBinding{


Test patch:
diff --git a/workers_test.go b/workers_test.go
index 4238d948f..35e8a8a01 100644
--- a/workers_test.go
+++ b/workers_test.go
@@ -455,6 +455,45 @@ func TestWorkers_UploadWorkerWithNameErrorsWithoutAccountId(t *testing.T) {
 	assert.Error(t, err)
 }
 
+func TestWorkers_UploadWorkerWithDurableObjectBinding(t *testing.T) {
+	setup(UsingAccount("foo"))
+	defer teardown()
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method)
+
+		mpUpload, err := parseMultipartUpload(r)
+		assert.NoError(t, err)
+
+		expectedBindings := map[string]workerBindingMeta{
+			"b1": {
+				"name":        "b1",
+				"type":        "durable_object_namespace",
+				"class_name":  "TheClass",
+				"script_name": "the_script",
+			},
+		}
+		assert.Equal(t, workerScript, mpUpload.Script)
+		assert.Equal(t, expectedBindings, mpUpload.BindingMeta)
+
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprintf(w, uploadWorkerResponseData) //nolint
+	}
+	mux.HandleFunc("/accounts/foo/workers/scripts/bar", handler)
+
+	scriptParams := WorkerScriptParams{
+		Script: workerScript,
+		Bindings: map[string]WorkerBinding{
+			"b1": WorkerDurableObjectBinding{
+				ClassName:  "TheClass",
+				ScriptName: "the_script",
+			},
+		},
+	}
+	_, err := client.UploadWorkerWithBindings(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, &scriptParams)
+	assert.NoError(t, err)
+}
+
 func TestWorkers_UploadWorkerWithInheritBinding(t *testing.T) {
 	setup(UsingAccount("foo"))
 	defer teardown()