Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

workers_kv: modernise all methods #1115

Merged
merged 4 commits into from Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
47 changes: 47 additions & 0 deletions .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.
```
202 changes: 102 additions & 100 deletions workers_kv.go
Expand Up @@ -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"`
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
Expand All @@ -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
Expand All @@ -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
}
Expand All @@ -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
Expand All @@ -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
Expand All @@ -205,25 +237,24 @@ 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
}
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
Expand All @@ -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
Expand All @@ -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
Expand Down