Repository: cloudflare/cloudflare-go
Pull request #1368 (2023-08-09 16:51:23)

add support for tags to CreateWorkerParams
## Description

Our API supports tagging scripts (primarily for Workers for Platforms, but they also work for non-WFP scripts), and `cloudflare-go` support for them has been requested.

## Has your change been tested?

Yes, in the unit tests, but no against the API. It's still using the same endpoints it was before, so at least it won't have the same bug my last PR did :)

## Types of changes

What sort of change does your code introduce/modify?

- [❌] Bug fix (non-breaking change which fixes an issue)
- [✅] New feature (non-breaking change which adds functionality)
- [❌] Breaking change (fix or feature that would cause existing functionality to change)

## Checklist:

- [✅] My code follows the code style of this project.
- [✅] My change requires a change to the documentation.
- [❌] I have updated the documentation accordingly.
  - I will ensure this field is documented in our API schema after opening the PR.
- [✅] I have added tests to cover my changes.
- [✅] All new and existing tests passed.
- [❌, ✅] 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/1368.txt b/.changelog/1368.txt
new file mode 100644
index 000000000..c58d12c33
--- /dev/null
+++ b/.changelog/1368.txt
@@ -0,0 +1,3 @@
+```release-note:enhancement
+workers: add support for tagging Worker scripts
+```
diff --git a/workers.go b/workers.go
index 814f42517..7f5b44c27 100644
--- a/workers.go
+++ b/workers.go
@@ -58,6 +58,33 @@ type CreateWorkerParams struct {
 	CompatibilityFlags []string
 
 	Placement *Placement
+
+	// Tags are used to better manage CRUD operations at scale.
+	//  https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/platform/tags/
+	Tags []string
+}
+
+func (p CreateWorkerParams) RequiresMultipart() bool {
+	switch {
+	case p.Module:
+		return true
+	case p.Logpush != nil:
+		return true
+	case p.Placement != nil:
+		return true
+	case len(p.Bindings) > 0:
+		return true
+	case p.CompatibilityDate != "":
+		return true
+	case len(p.CompatibilityFlags) > 0:
+		return true
+	case p.TailConsumers != nil:
+		return true
+	case len(p.Tags) > 0:
+		return true
+	}
+
+	return false
 }
 
 // WorkerScriptParams provides a worker script and the associated bindings.
@@ -269,7 +296,7 @@ func (api *API) UploadWorker(ctx context.Context, rc *ResourceContainer, params
 		err         error
 	)
 
-	if params.Module || params.Logpush != nil || params.Placement != nil || len(params.Bindings) > 0 || params.CompatibilityDate != "" || len(params.CompatibilityFlags) > 0 || params.TailConsumers != nil {
+	if params.RequiresMultipart() {
 		contentType, body, err = formatMultipartBody(params)
 		if err != nil {
 			return WorkerScriptResponse{}, err
@@ -315,6 +342,7 @@ func formatMultipartBody(params CreateWorkerParams) (string, []byte, error) {
 		CompatibilityDate  string                 `json:"compatibility_date,omitempty"`
 		CompatibilityFlags []string               `json:"compatibility_flags,omitempty"`
 		Placement          *Placement             `json:"placement,omitempty"`
+		Tags               []string               `json:"tags"`
 	}{
 		Bindings:           make([]workerBindingMeta, 0, len(params.Bindings)),
 		Logpush:            params.Logpush,
@@ -322,6 +350,7 @@ func formatMultipartBody(params CreateWorkerParams) (string, []byte, error) {
 		CompatibilityDate:  params.CompatibilityDate,
 		CompatibilityFlags: params.CompatibilityFlags,
 		Placement:          params.Placement,
+		Tags:               params.Tags,
 	}
 
 	if params.Module {


Test patch:
diff --git a/workers_test.go b/workers_test.go
index 1a95019c5..40a2eac3f 100644
--- a/workers_test.go
+++ b/workers_test.go
@@ -380,6 +380,7 @@ type multipartUpload struct {
 	CompatibilityDate  string
 	CompatibilityFlags []string
 	Placement          *Placement
+	Tags               []string
 }
 
 func parseMultipartUpload(r *http.Request) (multipartUpload, error) {
@@ -397,6 +398,7 @@ func parseMultipartUpload(r *http.Request) (multipartUpload, error) {
 		CompatibilityDate  string              `json:"compatibility_date,omitempty"`
 		CompatibilityFlags []string            `json:"compatibility_flags,omitempty"`
 		Placement          *Placement          `json:"placement,omitempty"`
+		Tags               []string            `json:"tags"`
 	}
 	err = json.Unmarshal(mdBytes, &metadata)
 	if err != nil {
@@ -429,6 +431,7 @@ func parseMultipartUpload(r *http.Request) (multipartUpload, error) {
 		CompatibilityDate:  metadata.CompatibilityDate,
 		CompatibilityFlags: metadata.CompatibilityFlags,
 		Placement:          metadata.Placement,
+		Tags:               metadata.Tags,
 	}, nil
 }
 
@@ -1235,6 +1238,40 @@ func TestUploadWorker_ToDispatchNamespace(t *testing.T) {
 	assert.NoError(t, err)
 }
 
+func TestUploadWorker_ToDispatchNamespace_Tags(t *testing.T) {
+	setup()
+	defer teardown()
+
+	namespaceName := "n1"
+	tags := []string{"hello=there", "another-tag"}
+
+	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)
+		require.NoError(t, err)
+
+		assert.Equal(t, workerScript, mpUpload.Script)
+
+		assert.EqualValues(t, tags, mpUpload.Tags)
+
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprint(w, workersScriptResponse(t))
+	}
+	mux.HandleFunc(
+		fmt.Sprintf("/accounts/"+testAccountID+"/workers/dispatch/namespaces/%s/scripts/bar", namespaceName),
+		handler,
+	)
+
+	_, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{
+		ScriptName:            "bar",
+		Script:                workerScript,
+		DispatchNamespaceName: &namespaceName,
+		Tags:                  tags,
+	})
+	assert.NoError(t, err)
+}
+
 func TestUploadWorker_UnsafeBinding(t *testing.T) {
 	setup()
 	defer teardown()