Repository: cloudflare/cloudflare-go
Pull request #1446 (2023-11-22 18:04:55)

Adding d1 binding to worker_bindings
## Description

Adding D1 bindings to worker_bindings to support binding D1 databases through terraform

## Has your change been tested?

Changes passed using test script

## 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 in [cloudflare/api-schemas](https://github.com/cloudflare/api-schemas) 
      and relies on stable APIs.

[1]: https://help.github.com/articles/closing-issues-using-keywords/



Patch:
diff --git a/.changelog/1446.txt b/.changelog/1446.txt
new file mode 100644
index 000000000..a324d9ebb
--- /dev/null
+++ b/.changelog/1446.txt
@@ -0,0 +1,3 @@
+```release-note:enhancement
+worker_bindings: add support for `d1` bindings
+```
diff --git a/workers_bindings.go b/workers_bindings.go
index 05cdc1bdb..c32cc2715 100644
--- a/workers_bindings.go
+++ b/workers_bindings.go
@@ -44,6 +44,8 @@ const (
 	WorkerQueueBindingType WorkerBindingType = "queue"
 	// DispatchNamespaceBindingType is the type for WFP namespace bindings.
 	DispatchNamespaceBindingType WorkerBindingType = "dispatch_namespace"
+	// WorkerD1DataseBindingType is for D1 databases.
+	WorkerD1DataseBindingType WorkerBindingType = "d1"
 )
 
 type ListWorkerBindingsParams struct {
@@ -125,7 +127,7 @@ func (b WorkerKvNamespaceBinding) Type() WorkerBindingType {
 
 func (b WorkerKvNamespaceBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
 	if b.NamespaceID == "" {
-		return nil, nil, fmt.Errorf(`NamespaceID for binding "%s" cannot be empty`, bindingName)
+		return nil, nil, fmt.Errorf(`namespace ID for binding "%s" cannot be empty`, bindingName)
 	}
 
 	return workerBindingMeta{
@@ -209,7 +211,7 @@ func (b WorkerPlainTextBinding) Type() WorkerBindingType {
 
 func (b WorkerPlainTextBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
 	if b.Text == "" {
-		return nil, nil, fmt.Errorf(`Text for binding "%s" cannot be empty`, bindingName)
+		return nil, nil, fmt.Errorf(`text for binding "%s" cannot be empty`, bindingName)
 	}
 
 	return workerBindingMeta{
@@ -233,7 +235,7 @@ func (b WorkerSecretTextBinding) Type() WorkerBindingType {
 
 func (b WorkerSecretTextBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
 	if b.Text == "" {
-		return nil, nil, fmt.Errorf(`Text for binding "%s" cannot be empty`, bindingName)
+		return nil, nil, fmt.Errorf(`text for binding "%s" cannot be empty`, bindingName)
 	}
 
 	return workerBindingMeta{
@@ -254,7 +256,7 @@ func (b WorkerServiceBinding) Type() WorkerBindingType {
 
 func (b WorkerServiceBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
 	if b.Service == "" {
-		return nil, nil, fmt.Errorf(`Service for binding "%s" cannot be empty`, bindingName)
+		return nil, nil, fmt.Errorf(`service for binding "%s" cannot be empty`, bindingName)
 	}
 
 	meta := workerBindingMeta{
@@ -304,7 +306,7 @@ func (b WorkerAnalyticsEngineBinding) Type() WorkerBindingType {
 
 func (b WorkerAnalyticsEngineBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
 	if b.Dataset == "" {
-		return nil, nil, fmt.Errorf(`Dataset for binding "%s" cannot be empty`, bindingName)
+		return nil, nil, fmt.Errorf(`dataset for binding "%s" cannot be empty`, bindingName)
 	}
 
 	return workerBindingMeta{
@@ -329,10 +331,10 @@ func (b WorkerQueueBinding) Type() WorkerBindingType {
 
 func (b WorkerQueueBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
 	if b.Binding == "" {
-		return nil, nil, fmt.Errorf(`Binding name for binding "%s" cannot be empty`, bindingName)
+		return nil, nil, fmt.Errorf(`binding name for binding "%s" cannot be empty`, bindingName)
 	}
 	if b.Queue == "" {
-		return nil, nil, fmt.Errorf(`Queue name for binding "%s" cannot be empty`, bindingName)
+		return nil, nil, fmt.Errorf(`queue name for binding "%s" cannot be empty`, bindingName)
 	}
 
 	return workerBindingMeta{
@@ -372,10 +374,10 @@ func (b DispatchNamespaceBinding) Type() WorkerBindingType {
 
 func (b DispatchNamespaceBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
 	if b.Binding == "" {
-		return nil, nil, fmt.Errorf(`Binding name for binding "%s" cannot be empty`, bindingName)
+		return nil, nil, fmt.Errorf(`binding name for binding "%s" cannot be empty`, bindingName)
 	}
 	if b.Namespace == "" {
-		return nil, nil, fmt.Errorf(`Namespace name for binding "%s" cannot be empty`, bindingName)
+		return nil, nil, fmt.Errorf(`namespace name for binding "%s" cannot be empty`, bindingName)
 	}
 
 	meta := workerBindingMeta{
@@ -386,7 +388,7 @@ func (b DispatchNamespaceBinding) serialize(bindingName string) (workerBindingMe
 
 	if b.Outbound != nil {
 		if b.Outbound.Worker.Service == "" {
-			return nil, nil, fmt.Errorf(`Outbound options for binding "%s" must have a service name`, bindingName)
+			return nil, nil, fmt.Errorf(`outbound options for binding "%s" must have a service name`, bindingName)
 		}
 
 		var params []map[string]interface{}
@@ -408,6 +410,28 @@ func (b DispatchNamespaceBinding) serialize(bindingName string) (workerBindingMe
 	return meta, nil, nil
 }
 
+// WorkerD1DatabaseBinding is a binding to a D1 instance.
+type WorkerD1DatabaseBinding struct {
+	DatabaseID string
+}
+
+// Type returns the type of the binding.
+func (b WorkerD1DatabaseBinding) Type() WorkerBindingType {
+	return WorkerD1DataseBindingType
+}
+
+func (b WorkerD1DatabaseBinding) serialize(bindingName string) (workerBindingMeta, workerBindingBodyWriter, error) {
+	if b.DatabaseID == "" {
+		return nil, nil, fmt.Errorf(`database ID for binding "%s" cannot be empty`, bindingName)
+	}
+
+	return workerBindingMeta{
+		"name":        bindingName,
+		"type":        b.Type(),
+		"database_id": b.DatabaseID,
+	}, nil, nil
+}
+
 // UnsafeBinding is for experimental or deprecated bindings, and allows specifying any binding type or property.
 type UnsafeBinding map[string]interface{}
 
@@ -432,7 +456,7 @@ func getRandomPartName() string {
 // ListWorkerBindings returns all the bindings for a particular worker.
 func (api *API) ListWorkerBindings(ctx context.Context, rc *ResourceContainer, params ListWorkerBindingsParams) (WorkerBindingListResponse, error) {
 	if params.ScriptName == "" {
-		return WorkerBindingListResponse{}, errors.New("ScriptName is required")
+		return WorkerBindingListResponse{}, errors.New("script name is required")
 	}
 
 	if rc.Level != AccountRouteLevel {
@@ -466,11 +490,11 @@ func (api *API) ListWorkerBindings(ctx context.Context, rc *ResourceContainer, p
 	for _, jsonBinding := range jsonRes.Bindings {
 		name, ok := jsonBinding["name"].(string)
 		if !ok {
-			return r, fmt.Errorf("Binding missing name %v", jsonBinding)
+			return r, fmt.Errorf("binding missing name %v", jsonBinding)
 		}
 		bType, ok := jsonBinding["type"].(string)
 		if !ok {
-			return r, fmt.Errorf("Binding missing type %v", jsonBinding)
+			return r, fmt.Errorf("binding missing type %v", jsonBinding)
 		}
 		bindingListItem := WorkerBindingListItem{
 			Name: name,
@@ -529,6 +553,11 @@ func (api *API) ListWorkerBindings(ctx context.Context, rc *ResourceContainer, p
 			bindingListItem.Binding = WorkerAnalyticsEngineBinding{
 				Dataset: dataset,
 			}
+		case WorkerD1DataseBindingType:
+			database_id := jsonBinding["database_id"].(string)
+			bindingListItem.Binding = WorkerD1DatabaseBinding{
+				DatabaseID: database_id,
+			}
 		default:
 			bindingListItem.Binding = WorkerInheritBinding{}
 		}


Test patch:
diff --git a/workers_bindings_test.go b/workers_bindings_test.go
index 3b33f0623..677acb934 100644
--- a/workers_bindings_test.go
+++ b/workers_bindings_test.go
@@ -35,7 +35,7 @@ func TestListWorkerBindings(t *testing.T) {
 	assert.NoError(t, err)
 
 	assert.Equal(t, successResponse, res.Response)
-	assert.Equal(t, 8, len(res.BindingList))
+	assert.Equal(t, 9, len(res.BindingList))
 
 	assert.Equal(t, res.BindingList[0], WorkerBindingListItem{
 		Name: "MY_KV",
@@ -96,7 +96,16 @@ func TestListWorkerBindings(t *testing.T) {
 			Dataset: "my_dataset",
 		},
 	})
+
 	assert.Equal(t, WorkerAnalyticsEngineBindingType, res.BindingList[7].Binding.Type())
+
+	assert.Equal(t, res.BindingList[8], WorkerBindingListItem{
+		Name: "MY_DATABASE",
+		Binding: WorkerD1DatabaseBinding{
+			DatabaseID: "cef5331f-e5c7-4c8a-a415-7908ae45f92a",
+		},
+	})
+	assert.Equal(t, WorkerD1DataseBindingType, res.BindingList[8].Binding.Type())
 }
 
 func ExampleUnsafeBinding() {
diff --git a/workers_test.go b/workers_test.go
index 2349d8dd3..2beb16ff3 100644
--- a/workers_test.go
+++ b/workers_test.go
@@ -140,6 +140,11 @@ const (
 				"name": "MY_DATASET",
 				"type": "analytics_engine",
 				"dataset": "my_dataset"
+			},
+			{
+				"name": "MY_DATABASE",
+				"type": "d1",
+				"database_id": "cef5331f-e5c7-4c8a-a415-7908ae45f92a"
 			}
 		],
 		"success": true,