Repository: cloudflare/cloudflare-go
Pull request #1115 (2022-10-21 05:14:54)

workers_kv: modernise all methods
- Updates the methods to make the experimental client method signatures for consistency
- Renames some methods to be clearer about what they do
- Automatically paginate List* requests (except for keys due to potential load issues on larger result sets)

Todo:

- [x] Add changelog for all method renames/moves


Patch:
diff --git a/.changelog/1115.txt b/.changelog/1115.txt
new file mode 100644
index 000000000..b77fa0e55
--- /dev/null
+++ b/.changelog/1115.txt
@@ -0,0 +1,47 @@
+```release-note:breaking-change
+workers_kv: `CreateWorkersKVNamespace` has been updated to match the experimental client method signatures (https://github.com/cloudflare/cloudflare-go/blob/master/docs/experimental.md).
+```
+
+```release-note:breaking-change
+workers_kv: `ListWorkersKVNamespaces` has been updated to match the experimental client method signatures (https://github.com/cloudflare/cloudflare-go/blob/master/docs/experimental.md).
+```
+
+```release-note:enhancement
+workers_kv: `ListWorkersKVNamespaces` automatically paginates all results unless `PerPage` is defined.
+```
+
+```release-note:breaking-change
+workers_kv: `DeleteWorkersKVNamespace` has been updated to match the experimental client method signatures (https://github.com/cloudflare/cloudflare-go/blob/master/docs/experimental.md).
+```
+
+```release-note:breaking-change
+workers_kv: `UpdateWorkersKVNamespace` has been updated to match the experimental client method signatures (https://github.com/cloudflare/cloudflare-go/blob/master/docs/experimental.md).
+```
+
+```release-note:breaking-change
+workers_kv: `WriteWorkersKV` has been renamed to `WriteWorkersKVEntry`.
+```
+
+```release-note:breaking-change
+workers_kv: `WriteWorkersKVBulk` has been renamed to `WriteWorkersKVEntries`.
+```
+
+```release-note:breaking-change
+workers_kv: `ReadWorkersKV` has been renamed to `GetWorkersKV`.
+```
+
+```release-note:breaking-change
+workers_kv: `DeleteWorkersKV` has been renamed to `DeleteWorkersKVEntry`.
+```
+
+```release-note:breaking-change
+workers_kv: `DeleteWorkersKVBulk` has been renamed to `DeleteWorkersKVEntries`.
+```
+
+```release-note:breaking-change
+workers_kv: `ListWorkersKVs` has been renamed to `ListWorkersKVKeys`.
+```
+
+```release-note:breaking-change
+workers_kv: `ListWorkersKVsWithOptions` has been removed. Use `ListWorkersKVKeys` instead and pass in the options.
+```
diff --git a/workers_kv.go b/workers_kv.go
index 39611f8fe..f7a01bc06 100644
--- a/workers_kv.go
+++ b/workers_kv.go
@@ -3,18 +3,21 @@ package cloudflare
 import (
 	"context"
 	"encoding/json"
-	"errors"
 	"fmt"
 	"net/http"
 	"net/url"
-	"strconv"
 )
 
-// WorkersKVNamespaceRequest provides parameters for creating and updating storage namespaces.
-type WorkersKVNamespaceRequest struct {
+// CreateWorkersKVNamespaceParams provides parameters for creating and updating storage namespaces.
+type CreateWorkersKVNamespaceParams struct {
 	Title string `json:"title"`
 }
 
+type UpdateWorkersKVNamespaceParams struct {
+	NamespaceID string `json:"-"`
+	Title       string `json:"title"`
+}
+
 // WorkersKVPair is used in an array in the request to the bulk KV api.
 type WorkersKVPair struct {
 	Key           string      `json:"key"`
@@ -25,9 +28,6 @@ type WorkersKVPair struct {
 	Base64        bool        `json:"base64,omitempty"`
 }
 
-// WorkersKVBulkWriteRequest is the request to the bulk KV api.
-type WorkersKVBulkWriteRequest []*WorkersKVPair
-
 // WorkersKVNamespaceResponse is the response received when creating storage namespaces.
 type WorkersKVNamespaceResponse struct {
 	Response
@@ -55,13 +55,6 @@ type StorageKey struct {
 	Metadata   interface{} `json:"metadata"`
 }
 
-// ListWorkersKVsOptions contains optional parameters for listing a namespace's keys.
-type ListWorkersKVsOptions struct {
-	Limit  *int
-	Cursor *string
-	Prefix *string
-}
-
 // ListStorageKeysResponse contains a slice of keys belonging to a storage namespace,
 // pagination information, and an embedded response struct.
 type ListStorageKeysResponse struct {
@@ -70,14 +63,51 @@ type ListStorageKeysResponse struct {
 	ResultInfo `json:"result_info"`
 }
 
+type ListWorkersKVNamespacesParams struct {
+	ResultInfo
+}
+
+type WriteWorkersKVEntryParams struct {
+	NamespaceID string
+	Key         string
+	Value       []byte
+}
+
+type WriteWorkersKVEntriesParams struct {
+	NamespaceID string
+	KVs         []*WorkersKVPair
+}
+
+type GetWorkersKVParams struct {
+	NamespaceID string
+	Key         string
+}
+
+type DeleteWorkersKVEntryParams struct {
+	NamespaceID string
+	Key         string
+}
+
+type DeleteWorkersKVEntriesParams struct {
+	NamespaceID string
+	Keys        []string
+}
+
+type ListWorkersKVsParams struct {
+	NamespaceID string `url:"-"`
+	Limit       int    `url:"limit,omitempty"`
+	Cursor      string `url:"cursor,omitempty"`
+	Prefix      string `url:"prefix,omitempty"`
+}
+
 // CreateWorkersKVNamespace creates a namespace under the given title.
 // A 400 is returned if the account already owns a namespace with this title.
 // A namespace must be explicitly deleted to be replaced.
 //
 // API reference: https://api.cloudflare.com/#workers-kv-namespace-create-a-namespace
-func (api *API) CreateWorkersKVNamespace(ctx context.Context, req *WorkersKVNamespaceRequest) (WorkersKVNamespaceResponse, error) {
-	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces", api.AccountID)
-	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, req)
+func (api *API) CreateWorkersKVNamespace(ctx context.Context, rc *ResourceContainer, params CreateWorkersKVNamespaceParams) (WorkersKVNamespaceResponse, error) {
+	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces", rc.Identifier)
+	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params)
 	if err != nil {
 		return WorkersKVNamespaceResponse{}, err
 	}
@@ -90,49 +120,52 @@ func (api *API) CreateWorkersKVNamespace(ctx context.Context, req *WorkersKVName
 	return result, err
 }
 
-// ListWorkersKVNamespaces lists storage namespaces
+// ListWorkersKVNamespaces lists storage namespaces.
 //
 // API reference: https://api.cloudflare.com/#workers-kv-namespace-list-namespaces
-func (api *API) ListWorkersKVNamespaces(ctx context.Context) ([]WorkersKVNamespace, error) {
-	v := url.Values{}
-	v.Set("per_page", "100")
+func (api *API) ListWorkersKVNamespaces(ctx context.Context, rc *ResourceContainer, params ListWorkersKVNamespacesParams) ([]WorkersKVNamespace, *ResultInfo, error) {
+	autoPaginate := true
+	if params.PerPage >= 1 || params.Page >= 1 {
+		autoPaginate = false
+	}
+	if params.PerPage < 1 {
+		params.PerPage = 50
+	}
+	if params.Page < 1 {
+		params.Page = 1
+	}
 
 	var namespaces []WorkersKVNamespace
-	page := 1
-
+	var nsResponse ListWorkersKVNamespacesResponse
 	for {
-		v.Set("page", strconv.Itoa(page))
-		uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces?%s", api.AccountID, v.Encode())
+		uri := buildURI(fmt.Sprintf("/accounts/%s/storage/kv/namespaces", rc.Identifier), params)
+
 		res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
 		if err != nil {
-			return []WorkersKVNamespace{}, err
+			return []WorkersKVNamespace{}, &ResultInfo{}, err
 		}
 
-		var p ListWorkersKVNamespacesResponse
-		if err := json.Unmarshal(res, &p); err != nil {
-			return []WorkersKVNamespace{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+		err = json.Unmarshal(res, &nsResponse)
+		if err != nil {
+			return []WorkersKVNamespace{}, &ResultInfo{}, fmt.Errorf("failed to unmarshal workers KV namespaces JSON data: %w", err)
 		}
 
-		if !p.Success {
-			return []WorkersKVNamespace{}, errors.New(errRequestNotSuccessful)
-		}
+		namespaces = append(namespaces, nsResponse.Result...)
+		params.ResultInfo = nsResponse.ResultInfo.Next()
 
-		namespaces = append(namespaces, p.Result...)
-		if p.ResultInfo.Page >= p.ResultInfo.TotalPages {
+		if params.ResultInfo.Done() || !autoPaginate {
 			break
 		}
-
-		page++
 	}
 
-	return namespaces, nil
+	return namespaces, &nsResponse.ResultInfo, nil
 }
 
-// DeleteWorkersKVNamespace deletes the namespace corresponding to the given ID
+// DeleteWorkersKVNamespace deletes the namespace corresponding to the given ID.
 //
 // API reference: https://api.cloudflare.com/#workers-kv-namespace-remove-a-namespace
-func (api *API) DeleteWorkersKVNamespace(ctx context.Context, namespaceID string) (Response, error) {
-	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s", api.AccountID, namespaceID)
+func (api *API) DeleteWorkersKVNamespace(ctx context.Context, rc *ResourceContainer, namespaceID string) (Response, error) {
+	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s", rc.Identifier, namespaceID)
 	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
 	if err != nil {
 		return Response{}, err
@@ -146,12 +179,12 @@ func (api *API) DeleteWorkersKVNamespace(ctx context.Context, namespaceID string
 	return result, err
 }
 
-// UpdateWorkersKVNamespace modifies a namespace's title
+// UpdateWorkersKVNamespace modifies a KV namespace based on the ID.
 //
 // API reference: https://api.cloudflare.com/#workers-kv-namespace-rename-a-namespace
-func (api *API) UpdateWorkersKVNamespace(ctx context.Context, namespaceID string, req *WorkersKVNamespaceRequest) (Response, error) {
-	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s", api.AccountID, namespaceID)
-	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, req)
+func (api *API) UpdateWorkersKVNamespace(ctx context.Context, rc *ResourceContainer, params UpdateWorkersKVNamespaceParams) (Response, error) {
+	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s", rc.Identifier, params.NamespaceID)
+	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params)
 	if err != nil {
 		return Response{}, err
 	}
@@ -164,14 +197,13 @@ func (api *API) UpdateWorkersKVNamespace(ctx context.Context, namespaceID string
 	return result, err
 }
 
-// WriteWorkersKV writes a value identified by a key.
+// WriteWorkersKVEntry writes a single KV value based on the key.
 //
 // API reference: https://api.cloudflare.com/#workers-kv-namespace-write-key-value-pair
-func (api *API) WriteWorkersKV(ctx context.Context, namespaceID, key string, value []byte) (Response, error) {
-	key = url.PathEscape(key)
-	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/values/%s", api.AccountID, namespaceID, key)
+func (api *API) WriteWorkersKVEntry(ctx context.Context, rc *ResourceContainer, params WriteWorkersKVEntryParams) (Response, error) {
+	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/values/%s", rc.Identifier, params.NamespaceID, url.PathEscape(params.Key))
 	res, err := api.makeRequestContextWithHeaders(
-		ctx, http.MethodPut, uri, value, http.Header{"Content-Type": []string{"application/octet-stream"}},
+		ctx, http.MethodPut, uri, params.Value, http.Header{"Content-Type": []string{"application/octet-stream"}},
 	)
 	if err != nil {
 		return Response{}, err
@@ -185,13 +217,13 @@ func (api *API) WriteWorkersKV(ctx context.Context, namespaceID, key string, val
 	return result, err
 }
 
-// WriteWorkersKVBulk writes multiple KVs at once.
+// WriteWorkersKVEntries writes multiple KVs at once.
 //
 // API reference: https://api.cloudflare.com/#workers-kv-namespace-write-multiple-key-value-pairs
-func (api *API) WriteWorkersKVBulk(ctx context.Context, namespaceID string, kvs WorkersKVBulkWriteRequest) (Response, error) {
-	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/bulk", api.AccountID, namespaceID)
+func (api *API) WriteWorkersKVEntries(ctx context.Context, rc *ResourceContainer, params WriteWorkersKVEntriesParams) (Response, error) {
+	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/bulk", rc.Identifier, params.NamespaceID)
 	res, err := api.makeRequestContextWithHeaders(
-		ctx, http.MethodPut, uri, kvs, http.Header{"Content-Type": []string{"application/json"}},
+		ctx, http.MethodPut, uri, params.KVs, http.Header{"Content-Type": []string{"application/json"}},
 	)
 	if err != nil {
 		return Response{}, err
@@ -205,12 +237,12 @@ func (api *API) WriteWorkersKVBulk(ctx context.Context, namespaceID string, kvs
 	return result, err
 }
 
-// ReadWorkersKV returns the value associated with the given key in the given namespace
+// GetWorkersKV returns the value associated with the given key in the
+// given namespace.
 //
 // API reference: https://api.cloudflare.com/#workers-kv-namespace-read-key-value-pair
-func (api API) ReadWorkersKV(ctx context.Context, namespaceID, key string) ([]byte, error) {
-	key = url.PathEscape(key)
-	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/values/%s", api.AccountID, namespaceID, key)
+func (api API) GetWorkersKV(ctx context.Context, rc *ResourceContainer, params GetWorkersKVParams) ([]byte, error) {
+	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/values/%s", rc.Identifier, params.NamespaceID, url.PathEscape(params.Key))
 	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
 	if err != nil {
 		return nil, err
@@ -218,12 +250,11 @@ func (api API) ReadWorkersKV(ctx context.Context, namespaceID, key string) ([]by
 	return res, nil
 }
 
-// DeleteWorkersKV deletes a key and value for a provided storage namespace
+// DeleteWorkersKVEntry deletes a key and value for a provided storage namespace.
 //
 // API reference: https://api.cloudflare.com/#workers-kv-namespace-delete-key-value-pair
-func (api API) DeleteWorkersKV(ctx context.Context, namespaceID, key string) (Response, error) {
-	key = url.PathEscape(key)
-	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/values/%s", api.AccountID, namespaceID, key)
+func (api API) DeleteWorkersKVEntry(ctx context.Context, rc *ResourceContainer, params DeleteWorkersKVEntryParams) (Response, error) {
+	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/values/%s", rc.Identifier, params.NamespaceID, url.PathEscape(params.Key))
 	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
 	if err != nil {
 		return Response{}, err
@@ -236,13 +267,13 @@ func (api API) DeleteWorkersKV(ctx context.Context, namespaceID, key string) (Re
 	return result, err
 }
 
-// DeleteWorkersKVBulk deletes multiple KVs at once.
+// DeleteWorkersKVEntries deletes multiple KVs at once.
 //
 // API reference: https://api.cloudflare.com/#workers-kv-namespace-delete-multiple-key-value-pairs
-func (api *API) DeleteWorkersKVBulk(ctx context.Context, namespaceID string, keys []string) (Response, error) {
-	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/bulk", api.AccountID, namespaceID)
+func (api *API) DeleteWorkersKVEntries(ctx context.Context, rc *ResourceContainer, params DeleteWorkersKVEntriesParams) (Response, error) {
+	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/bulk", rc.Identifier, params.NamespaceID)
 	res, err := api.makeRequestContextWithHeaders(
-		ctx, http.MethodDelete, uri, keys, http.Header{"Content-Type": []string{"application/json"}},
+		ctx, http.MethodDelete, uri, params.Keys, http.Header{"Content-Type": []string{"application/json"}},
 	)
 	if err != nil {
 		return Response{}, err
@@ -256,43 +287,14 @@ func (api *API) DeleteWorkersKVBulk(ctx context.Context, namespaceID string, key
 	return result, err
 }
 
-// ListWorkersKVs lists a namespace's keys
+// ListWorkersKVKeys lists a namespace's keys.
 //
 // API Reference: https://api.cloudflare.com/#workers-kv-namespace-list-a-namespace-s-keys
-func (api API) ListWorkersKVs(ctx context.Context, namespaceID string) (ListStorageKeysResponse, error) {
-	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/keys", api.AccountID, namespaceID)
-	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
-	if err != nil {
-		return ListStorageKeysResponse{}, err
-	}
-
-	result := ListStorageKeysResponse{}
-	if err := json.Unmarshal(res, &result); err != nil {
-		return result, fmt.Errorf("%s: %w", errUnmarshalError, err)
-	}
-	return result, err
-}
-
-// encode encodes non-nil fields into URL encoded form.
-func (o ListWorkersKVsOptions) encode() string {
-	v := url.Values{}
-	if o.Limit != nil {
-		v.Set("limit", strconv.Itoa(*o.Limit))
-	}
-	if o.Cursor != nil {
-		v.Set("cursor", *o.Cursor)
-	}
-	if o.Prefix != nil {
-		v.Set("prefix", *o.Prefix)
-	}
-	return v.Encode()
-}
-
-// ListWorkersKVsWithOptions lists a namespace's keys with optional parameters
-//
-// API Reference: https://api.cloudflare.com/#workers-kv-namespace-list-a-namespace-s-keys
-func (api API) ListWorkersKVsWithOptions(ctx context.Context, namespaceID string, o ListWorkersKVsOptions) (ListStorageKeysResponse, error) {
-	uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/keys?%s", api.AccountID, namespaceID, o.encode())
+func (api API) ListWorkersKVKeys(ctx context.Context, rc *ResourceContainer, params ListWorkersKVsParams) (ListStorageKeysResponse, error) {
+	uri := buildURI(
+		fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/keys", rc.Identifier, params.NamespaceID),
+		params,
+	)
 	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
 	if err != nil {
 		return ListStorageKeysResponse{}, err


Test patch:
diff --git a/workers_kv_example_test.go b/workers_kv_example_test.go
index 23b4a7807..233adbc66 100644
--- a/workers_kv_example_test.go
+++ b/workers_kv_example_test.go
@@ -15,13 +15,13 @@ const (
 )
 
 func ExampleAPI_CreateWorkersKVNamespace() {
-	api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount(accountID))
+	api, err := cloudflare.New(apiKey, user)
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	req := &cloudflare.WorkersKVNamespaceRequest{Title: "test_namespace2"}
-	response, err := api.CreateWorkersKVNamespace(context.Background(), req)
+	req := cloudflare.CreateWorkersKVNamespaceParams{Title: "test_namespace2"}
+	response, err := api.CreateWorkersKVNamespace(context.Background(), cloudflare.AccountIdentifier(accountID), req)
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -30,26 +30,35 @@ func ExampleAPI_CreateWorkersKVNamespace() {
 }
 
 func ExampleAPI_ListWorkersKVNamespaces() {
-	api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount(accountID))
+	api, err := cloudflare.New(apiKey, user)
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	lsr, err := api.ListWorkersKVNamespaces(context.Background())
+	lsr, _, err := api.ListWorkersKVNamespaces(context.Background(), cloudflare.AccountIdentifier(accountID), cloudflare.ListWorkersKVNamespacesParams{})
 	if err != nil {
 		log.Fatal(err)
 	}
 
 	fmt.Println(lsr)
+
+	resp, _, err := api.ListWorkersKVNamespaces(context.Background(), cloudflare.AccountIdentifier(accountID), cloudflare.ListWorkersKVNamespacesParams{ResultInfo: cloudflare.ResultInfo{
+		PerPage: 10,
+	}})
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	fmt.Println(resp)
 }
 
 func ExampleAPI_DeleteWorkersKVNamespace() {
-	api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount(accountID))
+	api, err := cloudflare.New(apiKey, user)
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	response, err := api.DeleteWorkersKVNamespace(context.Background(), namespace)
+	response, err := api.DeleteWorkersKVNamespace(context.Background(), cloudflare.AccountIdentifier(accountID), namespace)
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -58,12 +67,15 @@ func ExampleAPI_DeleteWorkersKVNamespace() {
 }
 
 func ExampleAPI_UpdateWorkersKVNamespace() {
-	api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount(accountID))
+	api, err := cloudflare.New(apiKey, user)
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	resp, err := api.UpdateWorkersKVNamespace(context.Background(), namespace, &cloudflare.WorkersKVNamespaceRequest{Title: "test_title"})
+	resp, err := api.UpdateWorkersKVNamespace(context.Background(), cloudflare.AccountIdentifier(accountID), cloudflare.UpdateWorkersKVNamespaceParams{
+		NamespaceID: namespace,
+		Title:       "test_title",
+	})
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -71,8 +83,8 @@ func ExampleAPI_UpdateWorkersKVNamespace() {
 	fmt.Println(resp)
 }
 
-func ExampleAPI_WriteWorkersKV() {
-	api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount(accountID))
+func ExampleAPI_WriteWorkersKVEntry() {
+	api, err := cloudflare.New(apiKey, user)
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -80,7 +92,11 @@ func ExampleAPI_WriteWorkersKV() {
 	payload := []byte("test payload")
 	key := "test_key"
 
-	resp, err := api.WriteWorkersKV(context.Background(), namespace, key, payload)
+	resp, err := api.WriteWorkersKVEntry(context.Background(), cloudflare.AccountIdentifier(accountID), cloudflare.WriteWorkersKVEntryParams{
+		NamespaceID: namespace,
+		Key:         key,
+		Value:       payload,
+	})
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -88,13 +104,13 @@ func ExampleAPI_WriteWorkersKV() {
 	fmt.Println(resp)
 }
 
-func ExampleAPI_WriteWorkersKVBulk() {
-	api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount(accountID))
+func ExampleAPI_WriteWorkersKVEntries() {
+	api, err := cloudflare.New(apiKey, user)
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	payload := cloudflare.WorkersKVBulkWriteRequest{
+	payload := []*cloudflare.WorkersKVPair{
 		{
 			Key:   "key1",
 			Value: "value1",
@@ -107,7 +123,10 @@ func ExampleAPI_WriteWorkersKVBulk() {
 		},
 	}
 
-	resp, err := api.WriteWorkersKVBulk(context.Background(), namespace, payload)
+	resp, err := api.WriteWorkersKVEntries(context.Background(), cloudflare.AccountIdentifier(accountID), cloudflare.WriteWorkersKVEntriesParams{
+		NamespaceID: namespace,
+		KVs:         payload,
+	})
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -115,14 +134,14 @@ func ExampleAPI_WriteWorkersKVBulk() {
 	fmt.Println(resp)
 }
 
-func ExampleAPI_ReadWorkersKV() {
-	api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount(accountID))
+func ExampleAPI_GetWorkersKV() {
+	api, err := cloudflare.New(apiKey, user)
 	if err != nil {
 		log.Fatal(err)
 	}
 
 	key := "test_key"
-	resp, err := api.ReadWorkersKV(context.Background(), namespace, key)
+	resp, err := api.GetWorkersKV(context.Background(), cloudflare.AccountIdentifier(accountID), cloudflare.GetWorkersKVParams{NamespaceID: namespace, Key: key})
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -130,14 +149,17 @@ func ExampleAPI_ReadWorkersKV() {
 	fmt.Printf("%s\n", resp)
 }
 
-func ExampleAPI_DeleteWorkersKV() {
-	api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount(accountID))
+func ExampleAPI_DeleteWorkersKVEntry() {
+	api, err := cloudflare.New(apiKey, user)
 	if err != nil {
 		log.Fatal(err)
 	}
 
 	key := "test_key"
-	resp, err := api.DeleteWorkersKV(context.Background(), namespace, key)
+	resp, err := api.DeleteWorkersKVEntry(context.Background(), cloudflare.AccountIdentifier(accountID), cloudflare.DeleteWorkersKVEntryParams{
+		NamespaceID: namespace,
+		Key:         key,
+	})
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -145,15 +167,18 @@ func ExampleAPI_DeleteWorkersKV() {
 	fmt.Printf("%+v\n", resp)
 }
 
-func ExampleAPI_DeleteWorkersKVBulk() {
-	api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount(accountID))
+func ExampleAPI_DeleteWorkersKVEntries() {
+	api, err := cloudflare.New(apiKey, user)
 	if err != nil {
 		log.Fatal(err)
 	}
 
 	keys := []string{"key1", "key2", "key3"}
 
-	resp, err := api.DeleteWorkersKVBulk(context.Background(), namespace, keys)
+	resp, err := api.DeleteWorkersKVEntries(context.Background(), cloudflare.AccountIdentifier(accountID), cloudflare.DeleteWorkersKVEntriesParams{
+		NamespaceID: namespace,
+		Keys:        keys,
+	})
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -161,22 +186,8 @@ func ExampleAPI_DeleteWorkersKVBulk() {
 	fmt.Println(resp)
 }
 
-func ExampleAPI_ListWorkersKVs() {
-	api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount(accountID))
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	resp, err := api.ListWorkersKVs(context.Background(), namespace)
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	fmt.Println(resp)
-}
-
-func ExampleAPI_ListWorkersKVsWithOptions() {
-	api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount(accountID))
+func ExampleAPI_ListWorkersKVKeys() {
+	api, err := cloudflare.New(apiKey, user)
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -185,13 +196,12 @@ func ExampleAPI_ListWorkersKVsWithOptions() {
 	prefix := "my-prefix"
 	cursor := "AArAbNSOuYcr4HmzGH02-cfDN8Ck9ejOwkn_Ai5rsn7S9NEqVJBenU9-gYRlrsziyjKLx48hNDLvtYzBAmkPsLGdye8ECr5PqFYcIOfUITdhkyTc1x6bV8nmyjz5DO-XaZH4kYY1KfqT8NRBIe5sic6yYt3FUDttGjafy0ivi-Up-TkVdRB0OxCf3O3OB-svG6DXheV5XTdDNrNx1o_CVqy2l2j0F4iKV1qFe_KhdkjC7Y6QjhUZ1MOb3J_uznNYVCoxZ-bVAAsJmXA"
 
-	options := cloudflare.ListWorkersKVsOptions{
-		Limit:  &limit,
-		Prefix: &prefix,
-		Cursor: &cursor,
-	}
-
-	resp, err := api.ListWorkersKVsWithOptions(context.Background(), namespace, options)
+	resp, err := api.ListWorkersKVKeys(context.Background(), cloudflare.AccountIdentifier(accountID), cloudflare.ListWorkersKVsParams{
+		NamespaceID: namespace,
+		Prefix:      prefix,
+		Limit:       limit,
+		Cursor:      cursor,
+	})
 	if err != nil {
 		log.Fatal(err)
 	}
diff --git a/workers_kv_test.go b/workers_kv_test.go
index ee64fb273..1ad7ae091 100644
--- a/workers_kv_test.go
+++ b/workers_kv_test.go
@@ -13,7 +13,7 @@ import (
 )
 
 func TestWorkersKV_CreateWorkersKVNamespace(t *testing.T) {
-	setup(UsingAccount("foo"))
+	setup()
 	defer teardown()
 
 	response := `{
@@ -26,13 +26,13 @@ func TestWorkersKV_CreateWorkersKVNamespace(t *testing.T) {
 		"messages": []
 	}`
 
-	mux.HandleFunc("/accounts/foo/storage/kv/namespaces", func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc("/accounts/"+testAccountID+"/storage/kv/namespaces", func(w http.ResponseWriter, r *http.Request) {
 		assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method)
 		w.Header().Set("content-type", "application/javascript")
 		fmt.Fprintf(w, response) //nolint
 	})
 
-	res, err := client.CreateWorkersKVNamespace(context.Background(), &WorkersKVNamespaceRequest{Title: "Namespace"})
+	res, err := client.CreateWorkersKVNamespace(context.Background(), AccountIdentifier(testAccountID), CreateWorkersKVNamespaceParams{Title: "Namespace"})
 	want := WorkersKVNamespaceResponse{
 		successResponse,
 		WorkersKVNamespace{
@@ -47,7 +47,7 @@ func TestWorkersKV_CreateWorkersKVNamespace(t *testing.T) {
 }
 
 func TestWorkersKV_DeleteWorkersKVNamespace(t *testing.T) {
-	setup(UsingAccount("foo"))
+	setup()
 	defer teardown()
 
 	namespace := "3aeaxxxxee014exxxx4cf66xxxxc0448"
@@ -57,13 +57,13 @@ func TestWorkersKV_DeleteWorkersKVNamespace(t *testing.T) {
 		"messages": []
 	}`
 
-	mux.HandleFunc(fmt.Sprintf("/accounts/foo/storage/kv/namespaces/%s", namespace), func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc(fmt.Sprintf("/accounts/"+testAccountID+"/storage/kv/namespaces/%s", namespace), func(w http.ResponseWriter, r *http.Request) {
 		assert.Equal(t, http.MethodDelete, r.Method, "Expected method 'DELETE', got %s", r.Method)
 		w.Header().Set("content-type", "application/javascript")
 		fmt.Fprintf(w, response) //nolint
 	})
 
-	res, err := client.DeleteWorkersKVNamespace(context.Background(), namespace)
+	res, err := client.DeleteWorkersKVNamespace(context.Background(), AccountIdentifier(testAccountID), namespace)
 	want := successResponse
 
 	if assert.NoError(t, err) {
@@ -71,8 +71,8 @@ func TestWorkersKV_DeleteWorkersKVNamespace(t *testing.T) {
 	}
 }
 
-func TestWorkersKV_ListWorkersKVNamespace(t *testing.T) {
-	setup(UsingAccount("foo"))
+func TestWorkersKV_ListWorkersKVNamespaces(t *testing.T) {
+	setup()
 	defer teardown()
 
 	response := `{
@@ -96,13 +96,13 @@ func TestWorkersKV_ListWorkersKVNamespace(t *testing.T) {
 		}
 	}`
 
-	mux.HandleFunc("/accounts/foo/storage/kv/namespaces", func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc("/accounts/"+testAccountID+"/storage/kv/namespaces", 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/javascript")
 		fmt.Fprintf(w, response) //nolint
 	})
 
-	res, err := client.ListWorkersKVNamespaces(context.Background())
+	res, _, err := client.ListWorkersKVNamespaces(context.Background(), AccountIdentifier(testAccountID), ListWorkersKVNamespacesParams{})
 	want := []WorkersKVNamespace{
 		{
 			ID:    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
@@ -126,7 +126,7 @@ func TestWorkersKV_ListWorkersKVNamespace(t *testing.T) {
 }
 
 func TestWorkersKV_ListWorkersKVNamespaceMultiplePages(t *testing.T) {
-	setup(UsingAccount("foo"))
+	setup()
 	defer teardown()
 
 	response1 := `{
@@ -165,7 +165,7 @@ func TestWorkersKV_ListWorkersKVNamespaceMultiplePages(t *testing.T) {
 		}
 	}`
 
-	mux.HandleFunc("/accounts/foo/storage/kv/namespaces", func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc("/accounts/"+testAccountID+"/storage/kv/namespaces", 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/javascript")
 
@@ -180,7 +180,7 @@ func TestWorkersKV_ListWorkersKVNamespaceMultiplePages(t *testing.T) {
 		}
 	})
 
-	res, err := client.ListWorkersKVNamespaces(context.Background())
+	res, _, err := client.ListWorkersKVNamespaces(context.Background(), AccountIdentifier(testAccountID), ListWorkersKVNamespacesParams{})
 	want := []WorkersKVNamespace{
 		{
 			ID:    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
@@ -204,7 +204,7 @@ func TestWorkersKV_ListWorkersKVNamespaceMultiplePages(t *testing.T) {
 }
 
 func TestWorkersKV_UpdateWorkersKVNamespace(t *testing.T) {
-	setup(UsingAccount("foo"))
+	setup()
 	defer teardown()
 
 	namespace := "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
@@ -215,13 +215,13 @@ func TestWorkersKV_UpdateWorkersKVNamespace(t *testing.T) {
 		"messages": []
 	}`
 
-	mux.HandleFunc(fmt.Sprintf("/accounts/foo/storage/kv/namespaces/%s", namespace), func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc(fmt.Sprintf("/accounts/"+testAccountID+"/storage/kv/namespaces/%s", namespace), func(w http.ResponseWriter, r *http.Request) {
 		assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method)
 		w.Header().Set("content-type", "application/javascript")
 		fmt.Fprintf(w, response) //nolint
 	})
 
-	res, err := client.UpdateWorkersKVNamespace(context.Background(), namespace, &WorkersKVNamespaceRequest{Title: "Namespace"})
+	res, err := client.UpdateWorkersKVNamespace(context.Background(), AccountIdentifier(testAccountID), UpdateWorkersKVNamespaceParams{Title: "Namespace", NamespaceID: namespace})
 	want := successResponse
 
 	if assert.NoError(t, err) {
@@ -229,8 +229,8 @@ func TestWorkersKV_UpdateWorkersKVNamespace(t *testing.T) {
 	}
 }
 
-func TestWorkersKV_WriteWorkersKV(t *testing.T) {
-	setup(UsingAccount("foo"))
+func TestWorkersKV_WriteWorkersKVEntry(t *testing.T) {
+	setup()
 	defer teardown()
 
 	key := "test_key"
@@ -243,25 +243,29 @@ func TestWorkersKV_WriteWorkersKV(t *testing.T) {
 		"messages": []
 	}`
 
-	mux.HandleFunc(fmt.Sprintf("/accounts/foo/storage/kv/namespaces/%s/values/%s", namespace, key), func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc(fmt.Sprintf("/accounts/"+testAccountID+"/storage/kv/namespaces/%s/values/%s", namespace, key), func(w http.ResponseWriter, r *http.Request) {
 		assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method)
 		w.Header().Set("content-type", "application/octet-stream")
 		fmt.Fprintf(w, response) //nolint
 	})
 
 	want := successResponse
-	res, err := client.WriteWorkersKV(context.Background(), namespace, key, value)
+	res, err := client.WriteWorkersKVEntry(context.Background(), AccountIdentifier(testAccountID), WriteWorkersKVEntryParams{NamespaceID: namespace, Key: key, Value: value})
 
 	if assert.NoError(t, err) {
 		assert.Equal(t, want, res)
 	}
 }
 
-func TestWorkersKV_WriteWorkersKVBulk(t *testing.T) {
-	setup(UsingAccount("foo"))
+func TestWorkersKV_WriteWorkersKVEntries(t *testing.T) {
+	setup()
 	defer teardown()
 
-	kvs := WorkersKVBulkWriteRequest{{Key: "key1", Value: "value1"}, {Key: "key2", Value: "value2"}, {Key: "key3", Value: "value3", Metadata: "meta3", Base64: true}}
+	kvs := []*WorkersKVPair{
+		{Key: "key1", Value: "value1"},
+		{Key: "key2", Value: "value2"},
+		{Key: "key3", Value: "value3", Metadata: "meta3", Base64: true},
+	}
 
 	namespace := "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
 	response := `{
@@ -271,32 +275,32 @@ func TestWorkersKV_WriteWorkersKVBulk(t *testing.T) {
 		"messages": []
 	}`
 
-	mux.HandleFunc(fmt.Sprintf("/accounts/foo/storage/kv/namespaces/%s/bulk", namespace), func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc(fmt.Sprintf("/accounts/"+testAccountID+"/storage/kv/namespaces/%s/bulk", namespace), func(w http.ResponseWriter, r *http.Request) {
 		assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method)
 		w.Header().Set("content-type", "application/json")
 		fmt.Fprintf(w, response) //nolint
 	})
 
 	want := successResponse
-	res, err := client.WriteWorkersKVBulk(context.Background(), namespace, kvs)
+	res, err := client.WriteWorkersKVEntries(context.Background(), AccountIdentifier(testAccountID), WriteWorkersKVEntriesParams{NamespaceID: namespace, KVs: kvs})
 	require.NoError(t, err)
 	assert.Equal(t, want, res)
 }
 
 func TestWorkersKV_ReadWorkersKV(t *testing.T) {
-	setup(UsingAccount("foo"))
+	setup()
 	defer teardown()
 
 	key := "test_key"
 	namespace := "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
 
-	mux.HandleFunc(fmt.Sprintf("/accounts/foo/storage/kv/namespaces/%s/values/%s", namespace, key), func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc(fmt.Sprintf("/accounts/"+testAccountID+"/storage/kv/namespaces/%s/values/%s", namespace, key), 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", "text/plain")
 		fmt.Fprintf(w, "test_value")
 	})
 
-	res, err := client.ReadWorkersKV(context.Background(), namespace, key)
+	res, err := client.GetWorkersKV(context.Background(), AccountIdentifier(testAccountID), GetWorkersKVParams{NamespaceID: namespace, Key: key})
 	want := []byte("test_value")
 
 	if assert.NoError(t, err) {
@@ -304,8 +308,8 @@ func TestWorkersKV_ReadWorkersKV(t *testing.T) {
 	}
 }
 
-func TestWorkersKV_DeleteWorkersKV(t *testing.T) {
-	setup(UsingAccount("foo"))
+func TestWorkersKV_DeleteWorkersKVEntry(t *testing.T) {
+	setup()
 	defer teardown()
 
 	key := "test_key"
@@ -317,13 +321,13 @@ func TestWorkersKV_DeleteWorkersKV(t *testing.T) {
 		"messages": []
 	}`
 
-	mux.HandleFunc(fmt.Sprintf("/accounts/foo/storage/kv/namespaces/%s/values/%s", namespace, key), func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc(fmt.Sprintf("/accounts/"+testAccountID+"/storage/kv/namespaces/%s/values/%s", namespace, key), func(w http.ResponseWriter, r *http.Request) {
 		assert.Equal(t, http.MethodDelete, r.Method, "Expected method 'DELETE', got %s", r.Method)
 		w.Header().Set("content-type", "application/javascript")
 		fmt.Fprintf(w, response) //nolint
 	})
 
-	res, err := client.DeleteWorkersKV(context.Background(), namespace, key)
+	res, err := client.DeleteWorkersKVEntry(context.Background(), AccountIdentifier(testAccountID), DeleteWorkersKVEntryParams{NamespaceID: namespace, Key: key})
 	want := successResponse
 
 	if assert.NoError(t, err) {
@@ -332,7 +336,7 @@ func TestWorkersKV_DeleteWorkersKV(t *testing.T) {
 }
 
 func TestWorkersKV_DeleteWorkersKVBulk(t *testing.T) {
-	setup(UsingAccount("foo"))
+	setup()
 	defer teardown()
 
 	keys := []string{"key1", "key2", "key3"}
@@ -345,20 +349,20 @@ func TestWorkersKV_DeleteWorkersKVBulk(t *testing.T) {
 		"messages": []
 	}`
 
-	mux.HandleFunc(fmt.Sprintf("/accounts/foo/storage/kv/namespaces/%s/bulk", namespace), func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc(fmt.Sprintf("/accounts/"+testAccountID+"/storage/kv/namespaces/%s/bulk", namespace), func(w http.ResponseWriter, r *http.Request) {
 		assert.Equal(t, http.MethodDelete, r.Method, "Expected method 'DELETE', got %s", r.Method)
 		w.Header().Set("content-type", "application/json")
 		fmt.Fprintf(w, response) //nolint
 	})
 
 	want := successResponse
-	res, err := client.DeleteWorkersKVBulk(context.Background(), namespace, keys)
+	res, err := client.DeleteWorkersKVEntries(context.Background(), AccountIdentifier(testAccountID), DeleteWorkersKVEntriesParams{NamespaceID: namespace, Keys: keys})
 	require.NoError(t, err)
 	assert.Equal(t, want, res)
 }
 
-func TestWorkersKV_ListStorageKeys(t *testing.T) {
-	setup(UsingAccount("foo"))
+func TestWorkersKV_ListKeys(t *testing.T) {
+	setup()
 	defer teardown()
 
 	namespace := "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
@@ -379,13 +383,13 @@ func TestWorkersKV_ListStorageKeys(t *testing.T) {
 		}
 	}`
 
-	mux.HandleFunc(fmt.Sprintf("/accounts/foo/storage/kv/namespaces/%s/keys", namespace), func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc(fmt.Sprintf("/accounts/"+testAccountID+"/storage/kv/namespaces/%s/keys", namespace), 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/javascript")
 		fmt.Fprintf(w, response) //nolint
 	})
 
-	res, err := client.ListWorkersKVs(context.Background(), namespace)
+	res, err := client.ListWorkersKVKeys(context.Background(), AccountIdentifier(testAccountID), ListWorkersKVsParams{NamespaceID: namespace})
 
 	want := ListStorageKeysResponse{
 		successResponse,
@@ -417,8 +421,8 @@ func TestWorkersKV_ListStorageKeys(t *testing.T) {
 	}
 }
 
-func TestWorkersKV_ListStorageKeysWithOptions(t *testing.T) {
-	setup(UsingAccount("foo"))
+func TestWorkersKV_ListKeysWithParameters(t *testing.T) {
+	setup()
 	defer teardown()
 
 	cursor := "AArAbNSOuYcr4HmzGH02-cfDN8Ck9ejOwkn_Ai5rsn7S9NEqVJBenU9-gYRlrsziyjKLx48hNDLvtYzBAmkPsLGdye8ECr5PqFYcIOfUITdhkyTc1x6bV8nmyjz5DO-XaZH4kYY1KfqT8NRBIe5sic6yYt3FUDttGjafy0ivi-Up-TkVdRB0OxCf3O3OB-svG6DXheV5XTdDNrNx1o_CVqy2l2j0F4iKV1qFe_KhdkjC7Y6QjhUZ1MOb3J_uznNYVCoxZ-bVAAsJmXA"
@@ -449,16 +453,17 @@ func TestWorkersKV_ListStorageKeysWithOptions(t *testing.T) {
 		}
 	}`
 
-	mux.HandleFunc(fmt.Sprintf("/accounts/foo/storage/kv/namespaces/%s/keys", namespace), func(w http.ResponseWriter, r *http.Request) {
+	mux.HandleFunc(fmt.Sprintf("/accounts/"+testAccountID+"/storage/kv/namespaces/%s/keys", namespace), 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/javascript")
 		fmt.Fprintf(w, response) //nolint
 	})
 
 	limit, prefix := 25, "test-prefix"
-	res, err := client.ListWorkersKVsWithOptions(context.Background(), namespace, ListWorkersKVsOptions{
-		Limit:  &limit,
-		Prefix: &prefix,
+	res, err := client.ListWorkersKVKeys(context.Background(), AccountIdentifier(testAccountID), ListWorkersKVsParams{
+		NamespaceID: namespace,
+		Limit:       limit,
+		Prefix:      prefix,
 	})
 
 	want := ListStorageKeysResponse{