Skip to content

Commit

Permalink
workers: modernise all method signatures and conventions
Browse files Browse the repository at this point in the history
Updates all the available Workers endpoints to follow the new method signatures
and conventions for calling the API.
  • Loading branch information
jacobbednarz committed Nov 30, 2022
1 parent 03a78d1 commit 45fb88f
Show file tree
Hide file tree
Showing 16 changed files with 473 additions and 703 deletions.
5 changes: 4 additions & 1 deletion errors.go
Expand Up @@ -29,7 +29,8 @@ const (
errAPIKeysAndTokensAreMutuallyExclusive = "API keys and tokens are mutually exclusive" //nolint:gosec
errMissingCredentials = "no credentials provided"

errInvalidResourceContainerAccess = "requested resource container (%q) is not supported for this endpoint"
errInvalidResourceContainerAccess = "requested resource container (%q) is not supported for this endpoint"
errRequiredAccountLevelResourceContainer = "this endpoint requires using an account level resource container and identifiers"
)

var (
Expand All @@ -40,6 +41,8 @@ var (
ErrAccountIDOrZoneIDAreRequired = errors.New(errMissingAccountOrZoneID)
ErrAccountIDAndZoneIDAreMutuallyExclusive = errors.New(errAccountIDAndZoneIDAreMutuallyExclusive)
ErrMissingResourceIdentifier = errors.New(errMissingResourceIdentifier)

ErrRequiredAccountLevelResourceContainer = errors.New(errRequiredAccountLevelResourceContainer)
)

type ErrorType string
Expand Down
78 changes: 35 additions & 43 deletions workers.go
Expand Up @@ -21,8 +21,8 @@ type WorkerRequestParams struct {
}

type CreateWorkerParams struct {
Name string
Script string
ScriptName string
Script string

// Module changes the Content-Type header to specify the script is an
// ES Module syntax script.
Expand All @@ -35,7 +35,7 @@ type CreateWorkerParams struct {

// WorkerScriptParams provides a worker script and the associated bindings.
type WorkerScriptParams struct {
Script string
ScriptName string

// Module changes the Content-Type header to specify the script is an
// ES Module syntax script.
Expand All @@ -50,8 +50,9 @@ type WorkerScriptParams struct {
//
// API reference: https://api.cloudflare.com/#worker-routes-properties
type WorkerRoute struct {
Pattern string `json:"pattern"`
Script string `json:"script,omitempty"`
ID string `json:"id,omitempty"`
Pattern string `json:"pattern"`
ScriptName string `json:"script,omitempty"`
}

// WorkerRoutesResponse embeds Response struct and slice of WorkerRoutes.
Expand Down Expand Up @@ -85,6 +86,7 @@ type WorkerMetaData struct {
// WorkerListResponse wrapper struct for API response to worker script list API call.
type WorkerListResponse struct {
Response
ResultInfo
WorkerList []WorkerMetaData `json:"result"`
}

Expand All @@ -105,6 +107,14 @@ type DeleteWorkerParams struct {
//
// API reference: https://api.cloudflare.com/#worker-script-delete-worker
func (api *API) DeleteWorker(ctx context.Context, rc *ResourceContainer, params DeleteWorkerParams) error {
if rc.Level != AccountRouteLevel {
return ErrRequiredAccountLevelResourceContainer
}

if rc.Identifier == "" {
return ErrMissingAccountID
}

uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s", rc.Identifier, params.ScriptName)
res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)

Expand All @@ -127,11 +137,11 @@ func (api *API) DeleteWorker(ctx context.Context, rc *ResourceContainer, params
// API reference: https://developers.cloudflare.com/workers/tooling/api/scripts/
func (api *API) GetWorker(ctx context.Context, rc *ResourceContainer, scriptName string) (WorkerScriptResponse, error) {
if rc.Level != AccountRouteLevel {
return WorkerScriptResponse{}, fmt.Errorf(errInvalidResourceContainerAccess, ZoneRouteLevel)
return WorkerScriptResponse{}, ErrRequiredAccountLevelResourceContainer
}

if rc.Identifier == "" {
return WorkerScriptResponse{}, ErrMissingIdentifier
return WorkerScriptResponse{}, ErrMissingAccountID
}

uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s", rc.Identifier, scriptName)
Expand Down Expand Up @@ -168,30 +178,42 @@ func (api *API) GetWorker(ctx context.Context, rc *ResourceContainer, scriptName
// ListWorkers returns list of Workers for given account.
//
// API reference: https://developers.cloudflare.com/workers/tooling/api/scripts/
func (api *API) ListWorkers(ctx context.Context, rc *ResourceContainer, params ListWorkersParams) (WorkerListResponse, error) {
func (api *API) ListWorkers(ctx context.Context, rc *ResourceContainer, params ListWorkersParams) (WorkerListResponse, *ResultInfo, error) {
if rc.Level != AccountRouteLevel {
return WorkerListResponse{}, &ResultInfo{}, ErrRequiredAccountLevelResourceContainer
}

if rc.Identifier == "" {
return WorkerListResponse{}, ErrMissingAccountID
return WorkerListResponse{}, &ResultInfo{}, ErrMissingAccountID
}

uri := fmt.Sprintf("/accounts/%s/workers/scripts", rc.Identifier)
res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
if err != nil {
return WorkerListResponse{}, err
return WorkerListResponse{}, &ResultInfo{}, err
}

var r WorkerListResponse
err = json.Unmarshal(res, &r)
if err != nil {
return WorkerListResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
return WorkerListResponse{}, &ResultInfo{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}

return r, nil
return r, &r.ResultInfo, nil
}

// UploadWorker pushes raw script content for your Worker.
//
// API reference: https://api.cloudflare.com/#worker-script-upload-worker
func (api *API) UploadWorker(ctx context.Context, rc *ResourceContainer, params CreateWorkerParams) (WorkerScriptResponse, error) {
if rc.Level != AccountRouteLevel {
return WorkerScriptResponse{}, ErrRequiredAccountLevelResourceContainer
}

if rc.Identifier == "" {
return WorkerScriptResponse{}, ErrMissingAccountID
}

var (
contentType = "application/javascript"
err error
Expand All @@ -205,37 +227,7 @@ func (api *API) UploadWorker(ctx context.Context, rc *ResourceContainer, params
}
}

if rc.Level == AccountRouteLevel {
return api.uploadWorkerWithName(ctx, rc, params.Name, contentType, []byte(body))
} else {
return api.uploadWorkerForZone(ctx, rc, contentType, []byte(body))
}
}

func (api *API) uploadWorkerForZone(ctx context.Context, rc *ResourceContainer, contentType string, body []byte) (WorkerScriptResponse, error) {
uri := fmt.Sprintf("/zones/%s/workers/script", rc.Identifier)
headers := make(http.Header)
headers.Set("Content-Type", contentType)

res, err := api.makeRequestContextWithHeaders(ctx, http.MethodPut, uri, body, headers)
var r WorkerScriptResponse
if err != nil {
return r, err
}

err = json.Unmarshal(res, &r)
if err != nil {
return r, fmt.Errorf("%s: %w", errUnmarshalError, err)
}

return r, nil
}

func (api *API) uploadWorkerWithName(ctx context.Context, rc *ResourceContainer, scriptName, contentType string, body []byte) (WorkerScriptResponse, error) {
if rc.Identifier == "" {
return WorkerScriptResponse{}, ErrMissingAccountID
}
uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s", rc.Identifier, scriptName)
uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s", rc.Identifier, params.ScriptName)
headers := make(http.Header)
headers.Set("Content-Type", contentType)
res, err := api.makeRequestContextWithHeaders(ctx, http.MethodPut, uri, body, headers)
Expand Down
8 changes: 8 additions & 0 deletions workers_account_settings.go
Expand Up @@ -37,6 +37,10 @@ func (api *API) CreateWorkersAccountSettings(ctx context.Context, rc *ResourceCo
return WorkersAccountSettings{}, ErrMissingAccountID
}

if rc.Level != AccountRouteLevel {
return WorkersAccountSettings{}, ErrRequiredAccountLevelResourceContainer
}

uri := fmt.Sprintf("/accounts/%s/workers/account-settings", rc.Identifier)
res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params)
if err != nil {
Expand All @@ -59,6 +63,10 @@ func (api *API) WorkersAccountSettings(ctx context.Context, rc *ResourceContaine
return WorkersAccountSettings{}, ErrMissingAccountID
}

if rc.Level != AccountRouteLevel {
return WorkersAccountSettings{}, ErrRequiredAccountLevelResourceContainer
}

uri := fmt.Sprintf("/accounts/%s/workers/account-settings", rc.Identifier)
res, err := api.makeRequestContext(ctx, http.MethodGet, uri, params)
if err != nil {
Expand Down
43 changes: 27 additions & 16 deletions workers_bindings.go
Expand Up @@ -39,6 +39,10 @@ const (
WorkerR2BucketBindingType WorkerBindingType = "r2_bucket"
)

type ListWorkerBindingsParams struct {
ScriptName string
}

// WorkerBindingListItem a struct representing an individual binding in a list of bindings.
type WorkerBindingListItem struct {
Name string `json:"name"`
Expand Down Expand Up @@ -290,15 +294,20 @@ func getRandomPartName() string {
}

// ListWorkerBindings returns all the bindings for a particular worker.
func (api *API) ListWorkerBindings(ctx context.Context, requestParams *WorkerRequestParams) (WorkerBindingListResponse, error) {
if requestParams.ScriptName == "" {
func (api *API) ListWorkerBindings(ctx context.Context, rc *ResourceContainer, params ListWorkerBindingsParams) (WorkerBindingListResponse, error) {
if params.ScriptName == "" {
return WorkerBindingListResponse{}, errors.New("ScriptName is required")
}
if api.AccountID == "" {
return WorkerBindingListResponse{}, errors.New("account ID required")

if rc.Level != AccountRouteLevel {
return WorkerBindingListResponse{}, ErrRequiredAccountLevelResourceContainer
}

if rc.Identifier == "" {
return WorkerBindingListResponse{}, ErrMissingAccountID
}

uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/bindings", api.AccountID, requestParams.ScriptName)
uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/bindings", rc.Identifier, params.ScriptName)

var jsonRes struct {
Response
Expand Down Expand Up @@ -347,10 +356,11 @@ func (api *API) ListWorkerBindings(ctx context.Context, requestParams *WorkerReq
case WorkerWebAssemblyBindingType:
bindingListItem.Binding = WorkerWebAssemblyBinding{
Module: &bindingContentReader{
ctx: ctx,
api: api,
requestParams: requestParams,
bindingName: name,
api: api,
ctx: ctx,
accountID: rc.Identifier,
params: &params,
bindingName: name,
},
}
case WorkerPlainTextBindingType:
Expand Down Expand Up @@ -386,18 +396,19 @@ func (api *API) ListWorkerBindings(ctx context.Context, requestParams *WorkerReq
// is first called. This is only useful for binding types
// that store raw bytes, like WebAssembly modules.
type bindingContentReader struct {
api *API
requestParams *WorkerRequestParams
ctx context.Context
bindingName string
content []byte
position int
api *API
accountID string
params *ListWorkerBindingsParams
ctx context.Context
bindingName string
content []byte
position int
}

func (b *bindingContentReader) Read(p []byte) (n int, err error) {
// Lazily load the content when Read() is first called
if b.content == nil {
uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/bindings/%s/content", b.api.AccountID, b.requestParams.ScriptName, b.bindingName)
uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/bindings/%s/content", b.accountID, b.params.ScriptName, b.bindingName)
res, err := b.api.makeRequestContext(b.ctx, http.MethodGet, uri, nil)
if err != nil {
return 0, err
Expand Down
89 changes: 89 additions & 0 deletions workers_bindings_test.go
@@ -0,0 +1,89 @@
package cloudflare

import (
"context"
"fmt"
"io"
"net/http"
"testing"

"github.com/stretchr/testify/assert"
)

func TestListWorkerBindings(t *testing.T) {
setup()
defer teardown()

mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/my-script/bindings", 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/json")
fmt.Fprintf(w, listBindingsResponseData) //nolint
})

mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/my-script/bindings/MY_WASM/content", 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/wasm")
_, _ = w.Write([]byte("mock multi-script wasm"))
})

res, err := client.ListWorkerBindings(context.Background(), AccountIdentifier(testAccountID), ListWorkerBindingsParams{
ScriptName: "my-script",
})
assert.NoError(t, err)

assert.Equal(t, successResponse, res.Response)
assert.Equal(t, 7, len(res.BindingList))

assert.Equal(t, res.BindingList[0], WorkerBindingListItem{
Name: "MY_KV",
Binding: WorkerKvNamespaceBinding{
NamespaceID: "89f5f8fd93f94cb98473f6f421aa3b65",
},
})
assert.Equal(t, WorkerKvNamespaceBindingType, res.BindingList[0].Binding.Type())

assert.Equal(t, "MY_WASM", res.BindingList[1].Name)
wasmBinding := res.BindingList[1].Binding.(WorkerWebAssemblyBinding)
wasmModuleContent, err := io.ReadAll(wasmBinding.Module)
assert.NoError(t, err)
assert.Equal(t, []byte("mock multi-script wasm"), wasmModuleContent)
assert.Equal(t, WorkerWebAssemblyBindingType, res.BindingList[1].Binding.Type())

assert.Equal(t, res.BindingList[2], WorkerBindingListItem{
Name: "MY_PLAIN_TEXT",
Binding: WorkerPlainTextBinding{
Text: "text",
},
})
assert.Equal(t, WorkerPlainTextBindingType, res.BindingList[2].Binding.Type())

assert.Equal(t, res.BindingList[3], WorkerBindingListItem{
Name: "MY_SECRET_TEXT",
Binding: WorkerSecretTextBinding{},
})
assert.Equal(t, WorkerSecretTextBindingType, res.BindingList[3].Binding.Type())

environment := "MY_ENVIRONMENT"
assert.Equal(t, res.BindingList[4], WorkerBindingListItem{
Name: "MY_SERVICE_BINDING",
Binding: WorkerServiceBinding{
Service: "MY_SERVICE",
Environment: &environment,
},
})
assert.Equal(t, WorkerServiceBindingType, res.BindingList[4].Binding.Type())

assert.Equal(t, res.BindingList[5], WorkerBindingListItem{
Name: "MY_NEW_BINDING",
Binding: WorkerInheritBinding{},
})
assert.Equal(t, WorkerInheritBindingType, res.BindingList[5].Binding.Type())

assert.Equal(t, res.BindingList[6], WorkerBindingListItem{
Name: "MY_BUCKET",
Binding: WorkerR2BucketBinding{
BucketName: "bucket",
},
})
assert.Equal(t, WorkerR2BucketBindingType, res.BindingList[6].Binding.Type())
}

0 comments on commit 45fb88f

Please sign in to comment.