diff --git a/errors.go b/errors.go index 994707978e..723c18bae3 100644 --- a/errors.go +++ b/errors.go @@ -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 ( @@ -40,6 +41,8 @@ var ( ErrAccountIDOrZoneIDAreRequired = errors.New(errMissingAccountOrZoneID) ErrAccountIDAndZoneIDAreMutuallyExclusive = errors.New(errAccountIDAndZoneIDAreMutuallyExclusive) ErrMissingResourceIdentifier = errors.New(errMissingResourceIdentifier) + + ErrRequiredAccountLevelResourceContainer = errors.New(errRequiredAccountLevelResourceContainer) ) type ErrorType string diff --git a/workers.go b/workers.go index b762acad8f..ae2bf7c5df 100644 --- a/workers.go +++ b/workers.go @@ -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. @@ -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. @@ -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. @@ -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"` } @@ -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) @@ -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) @@ -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 @@ -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) diff --git a/workers_account_settings.go b/workers_account_settings.go index d8e58516a0..6b3b65d6df 100644 --- a/workers_account_settings.go +++ b/workers_account_settings.go @@ -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 { @@ -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 { diff --git a/workers_bindings.go b/workers_bindings.go index d64717c4d9..e7a1f1f65b 100644 --- a/workers_bindings.go +++ b/workers_bindings.go @@ -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"` @@ -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 @@ -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: ¶ms, + bindingName: name, }, } case WorkerPlainTextBindingType: @@ -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 diff --git a/workers_bindings_test.go b/workers_bindings_test.go new file mode 100644 index 0000000000..ca0c5166e9 --- /dev/null +++ b/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()) +} diff --git a/workers_cron_triggers.go b/workers_cron_triggers.go index 8938862aef..2322a5d682 100644 --- a/workers_cron_triggers.go +++ b/workers_cron_triggers.go @@ -27,12 +27,29 @@ type WorkerCronTrigger struct { ModifiedOn *time.Time `json:"modified_on,omitempty"` } +type ListWorkerCronTriggersParams struct { + ScriptName string +} + +type UpdateWorkerCronTriggersParams struct { + ScriptName string + Crons []WorkerCronTrigger +} + // ListWorkerCronTriggers fetches all available cron triggers for a single Worker // script. // // API reference: https://api.cloudflare.com/#worker-cron-trigger-get-cron-triggers -func (api *API) ListWorkerCronTriggers(ctx context.Context, accountID, scriptName string) ([]WorkerCronTrigger, error) { - uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/schedules", accountID, scriptName) +func (api *API) ListWorkerCronTriggers(ctx context.Context, rc *ResourceContainer, params ListWorkerCronTriggersParams) ([]WorkerCronTrigger, error) { + if rc.Level != AccountRouteLevel { + return []WorkerCronTrigger{}, ErrRequiredAccountLevelResourceContainer + } + + if rc.Identifier == "" { + return []WorkerCronTrigger{}, ErrMissingIdentifier + } + + uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/schedules", rc.Identifier, params.ScriptName) res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil) if err != nil { return []WorkerCronTrigger{}, err @@ -49,9 +66,17 @@ func (api *API) ListWorkerCronTriggers(ctx context.Context, accountID, scriptNam // UpdateWorkerCronTriggers updates a single schedule for a Worker cron trigger. // // API reference: https://api.cloudflare.com/#worker-cron-trigger-update-cron-triggers -func (api *API) UpdateWorkerCronTriggers(ctx context.Context, accountID, scriptName string, crons []WorkerCronTrigger) ([]WorkerCronTrigger, error) { - uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/schedules", accountID, scriptName) - res, err := api.makeRequestContext(ctx, http.MethodPut, uri, crons) +func (api *API) UpdateWorkerCronTriggers(ctx context.Context, rc *ResourceContainer, params UpdateWorkerCronTriggersParams) ([]WorkerCronTrigger, error) { + if rc.Level != AccountRouteLevel { + return []WorkerCronTrigger{}, ErrRequiredAccountLevelResourceContainer + } + + if rc.Identifier == "" { + return []WorkerCronTrigger{}, ErrMissingIdentifier + } + + uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/schedules", rc.Identifier, params.ScriptName) + res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params.Crons) if err != nil { return []WorkerCronTrigger{}, err } diff --git a/workers_cron_triggers_test.go b/workers_cron_triggers_test.go index d2acba1e25..0194d75ef3 100644 --- a/workers_cron_triggers_test.go +++ b/workers_cron_triggers_test.go @@ -11,7 +11,7 @@ import ( ) func TestListWorkerCronTriggers(t *testing.T) { - setup(UsingAccount("9a7806061c88ada191ed06f989cc3dac")) + setup() defer teardown() handler := func(w http.ResponseWriter, r *http.Request) { @@ -42,14 +42,14 @@ func TestListWorkerCronTriggers(t *testing.T) { CreatedOn: &createdOn, }} - actual, err := client.ListWorkerCronTriggers(context.Background(), testAccountID, "example-script") + actual, err := client.ListWorkerCronTriggers(context.Background(), AccountIdentifier(testAccountID), ListWorkerCronTriggersParams{ScriptName: "example-script"}) if assert.NoError(t, err) { assert.Equal(t, want, actual) } } func TestUpdateWorkerCronTriggers(t *testing.T) { - setup(UsingAccount("9a7806061c88ada191ed06f989cc3dac")) + setup() defer teardown() handler := func(w http.ResponseWriter, r *http.Request) { @@ -80,7 +80,7 @@ func TestUpdateWorkerCronTriggers(t *testing.T) { CreatedOn: &createdOn, }} - actual, err := client.UpdateWorkerCronTriggers(context.Background(), testAccountID, "example-script", want) + actual, err := client.UpdateWorkerCronTriggers(context.Background(), AccountIdentifier(testAccountID), UpdateWorkerCronTriggersParams{ScriptName: "example-script", Crons: want}) if assert.NoError(t, err) { assert.Equal(t, want, actual) } diff --git a/workers_domains.go b/workers_domains.go deleted file mode 100644 index e8caea9793..0000000000 --- a/workers_domains.go +++ /dev/null @@ -1,48 +0,0 @@ -package cloudflare - -import ( - "context" - "encoding/json" - "fmt" - "net/http" -) - -type WorkerDomainParams struct { - ZoneID string `json:"zone_id"` - Hostname string `json:"hostname"` - Service string `json:"service"` - Environment string `json:"environment,omitempty"` -} - -type WorkerDomainResult struct { - ID string `json:"id"` - ZoneID string `json:"zone_id"` - ZoneName string `json:"zone_name"` - Hostname string `json:"hostname"` - Service string `json:"service"` - Environment string `json:"environment"` -} - -type WorkerDomainResponse struct { - Response - WorkerDomainResult `json:"result"` -} - -// AttachWorkerToDomain attaches a worker to a zone and hostname -// -// API reference: https://api.cloudflare.com/#worker-domain-attach-to-domain -func (api *API) AttachWorkerToDomain(ctx context.Context, rc *ResourceContainer, params *WorkerDomainParams) (WorkerDomainResponse, error) { - uri := fmt.Sprintf("/accounts/%s/workers/domains", rc.Identifier) - res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params) - if err != nil { - return WorkerDomainResponse{}, err - } - - var r WorkerDomainResponse - err = json.Unmarshal(res, &r) - if err != nil { - return WorkerDomainResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err) - } - - return r, nil -} diff --git a/workers_example_test.go b/workers_example_test.go deleted file mode 100644 index f70c7b93a7..0000000000 --- a/workers_example_test.go +++ /dev/null @@ -1,198 +0,0 @@ -package cloudflare_test - -// var ( -// workerScript = "addEventListener('fetch', event => {\n event.passThroughOnException()\nevent.respondWith(handleRequest(event.request))\n})\n\nasync function handleRequest(request) {\n return fetch(request)\n}" -// ) - -// func ExampleAPI_UploadWorker() { -// api, err := cloudflare.New(apiKey, user) -// if err != nil { -// log.Fatal(err) -// } - -// zoneID, err := api.ZoneIDByName(domain) -// if err != nil { -// log.Fatal(err) -// } - -// res, err := api.UploadWorker(context.Background(), &cloudflare.WorkerRequestParams{ZoneID: zoneID}, &cloudflare.WorkerScriptParams{Script: workerScript}) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("%+v", res) - -// UploadWorkerWithName() -// } - -// func UploadWorkerWithName() { -// api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount("foo")) -// if err != nil { -// log.Fatal(err) -// } - -// res, err := api.UploadWorker(context.Background(), &cloudflare.WorkerRequestParams{ScriptName: "baz"}, &cloudflare.WorkerScriptParams{Script: workerScript}) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("%+v", res) -// } - -// func ExampleAPI_DownloadWorker() { -// api, err := cloudflare.New(apiKey, user) -// if err != nil { -// log.Fatal(err) -// } - -// zoneID, err := api.ZoneIDByName(domain) -// if err != nil { -// log.Fatal(err) -// } - -// res, err := api.DownloadWorker(context.Background(), &cloudflare.WorkerRequestParams{ZoneID: zoneID}) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("%+v", res) - -// DownloadWorkerWithName() -// } - -// func DownloadWorkerWithName() { -// api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount("foo")) -// if err != nil { -// log.Fatal(err) -// } - -// res, err := api.DownloadWorker(context.Background(), &cloudflare.WorkerRequestParams{ScriptName: "baz"}) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("%+v", res) -// } - -// func ExampleAPI_DeleteWorker() { -// api, err := cloudflare.New(apiKey, user) -// if err != nil { -// log.Fatal(err) -// } - -// zoneID, err := api.ZoneIDByName(domain) -// if err != nil { -// log.Fatal(err) -// } -// res, err := api.DeleteWorker(context.Background(), &cloudflare.WorkerRequestParams{ZoneID: zoneID}) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("%+v", res) - -// DeleteWorkerWithName() -// } - -// func DeleteWorkerWithName() { -// api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount("foo")) -// if err != nil { -// log.Fatal(err) -// } - -// res, err := api.DeleteWorker(context.Background(), &cloudflare.WorkerRequestParams{ScriptName: "baz"}) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("%+v", res) -// } - -// func ExampleAPI_ListWorkerScripts() { -// api, err := cloudflare.New(apiKey, user, cloudflare.UsingAccount("foo")) -// if err != nil { -// log.Fatal(err) -// } - -// res, err := api.ListWorkerScripts(context.Background()) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("%+v", res.WorkerList) -// } - -// func ExampleAPI_CreateWorkerRoute() { -// api, err := cloudflare.New(apiKey, user) -// if err != nil { -// log.Fatal(err) -// } - -// zoneID, err := api.ZoneIDByName(domain) -// if err != nil { -// log.Fatal(err) -// } -// route := cloudflare.WorkerRoute{Pattern: "app1.example.com/*", Enabled: true} -// res, err := api.CreateWorkerRoute(context.Background(), zoneID, route) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("%+v", res) -// } - -// func ExampleAPI_UpdateWorkerRoute() { -// api, err := cloudflare.New(apiKey, user) -// if err != nil { -// log.Fatal(err) -// } - -// zoneID, err := api.ZoneIDByName(domain) -// if err != nil { -// log.Fatal(err) -// } -// // pull from existing list of routes to perform update on -// routesResponse, err := api.ListWorkerRoutes(context.Background(), zoneID) -// if err != nil { -// log.Fatal(err) -// } -// route := cloudflare.WorkerRoute{Pattern: "app2.example.com/*", Enabled: true} -// // update first route retrieved from the listWorkerRoutes call with details above -// res, err := api.UpdateWorkerRoute(context.Background(), zoneID, routesResponse.Routes[0].ID, route) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("%+v", res) -// } - -// func ExampleAPI_ListWorkerRoutes() { -// api, err := cloudflare.New(apiKey, user) -// if err != nil { -// log.Fatal(err) -// } - -// zoneID, err := api.ZoneIDByName(domain) -// if err != nil { -// log.Fatal(err) -// } -// res, err := api.ListWorkerRoutes(context.Background(), zoneID) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("%+v", res) -// } - -// func ExampleAPI_DeleteWorkerRoute() { -// api, err := cloudflare.New(apiKey, user) -// if err != nil { -// log.Fatal(err) -// } - -// zoneID, err := api.ZoneIDByName(domain) -// if err != nil { -// log.Fatal(err) -// } -// // pull from existing list of routes to perform delete on -// routesResponse, err := api.ListWorkerRoutes(context.Background(), zoneID) -// if err != nil { -// log.Fatal(err) -// } -// // delete first route retrieved from the listWorkerRoutes call -// res, err := api.DeleteWorkerRoute(context.Background(), zoneID, routesResponse.Routes[0].ID) -// if err != nil { -// log.Fatal(err) -// } -// fmt.Printf("%+v", res) -// } diff --git a/workers_kv.go b/workers_kv.go index f7a01bc062..7cdf30e3c6 100644 --- a/workers_kv.go +++ b/workers_kv.go @@ -106,6 +106,13 @@ type ListWorkersKVsParams struct { // // API reference: https://api.cloudflare.com/#workers-kv-namespace-create-a-namespace func (api *API) CreateWorkersKVNamespace(ctx context.Context, rc *ResourceContainer, params CreateWorkersKVNamespaceParams) (WorkersKVNamespaceResponse, error) { + if rc.Level != AccountRouteLevel { + return WorkersKVNamespaceResponse{}, ErrRequiredAccountLevelResourceContainer + } + + if rc.Identifier == "" { + return WorkersKVNamespaceResponse{}, ErrMissingIdentifier + } uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces", rc.Identifier) res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params) if err != nil { @@ -124,6 +131,14 @@ func (api *API) CreateWorkersKVNamespace(ctx context.Context, rc *ResourceContai // // API reference: https://api.cloudflare.com/#workers-kv-namespace-list-namespaces func (api *API) ListWorkersKVNamespaces(ctx context.Context, rc *ResourceContainer, params ListWorkersKVNamespacesParams) ([]WorkersKVNamespace, *ResultInfo, error) { + if rc.Level != AccountRouteLevel { + return []WorkersKVNamespace{}, &ResultInfo{}, ErrRequiredAccountLevelResourceContainer + } + + if rc.Identifier == "" { + return []WorkersKVNamespace{}, &ResultInfo{}, ErrMissingIdentifier + } + autoPaginate := true if params.PerPage >= 1 || params.Page >= 1 { autoPaginate = false @@ -183,6 +198,14 @@ func (api *API) DeleteWorkersKVNamespace(ctx context.Context, rc *ResourceContai // // API reference: https://api.cloudflare.com/#workers-kv-namespace-rename-a-namespace func (api *API) UpdateWorkersKVNamespace(ctx context.Context, rc *ResourceContainer, params UpdateWorkersKVNamespaceParams) (Response, error) { + if rc.Level != AccountRouteLevel { + return Response{}, ErrRequiredAccountLevelResourceContainer + } + + if rc.Identifier == "" { + return Response{}, ErrMissingIdentifier + } + 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 { @@ -201,6 +224,14 @@ func (api *API) UpdateWorkersKVNamespace(ctx context.Context, rc *ResourceContai // // API reference: https://api.cloudflare.com/#workers-kv-namespace-write-key-value-pair func (api *API) WriteWorkersKVEntry(ctx context.Context, rc *ResourceContainer, params WriteWorkersKVEntryParams) (Response, error) { + if rc.Level != AccountRouteLevel { + return Response{}, ErrRequiredAccountLevelResourceContainer + } + + if rc.Identifier == "" { + return Response{}, ErrMissingIdentifier + } + 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, params.Value, http.Header{"Content-Type": []string{"application/octet-stream"}}, @@ -221,6 +252,14 @@ func (api *API) WriteWorkersKVEntry(ctx context.Context, rc *ResourceContainer, // // API reference: https://api.cloudflare.com/#workers-kv-namespace-write-multiple-key-value-pairs func (api *API) WriteWorkersKVEntries(ctx context.Context, rc *ResourceContainer, params WriteWorkersKVEntriesParams) (Response, error) { + if rc.Level != AccountRouteLevel { + return Response{}, ErrRequiredAccountLevelResourceContainer + } + + if rc.Identifier == "" { + return Response{}, ErrMissingIdentifier + } + uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/bulk", rc.Identifier, params.NamespaceID) res, err := api.makeRequestContextWithHeaders( ctx, http.MethodPut, uri, params.KVs, http.Header{"Content-Type": []string{"application/json"}}, @@ -242,6 +281,13 @@ func (api *API) WriteWorkersKVEntries(ctx context.Context, rc *ResourceContainer // // API reference: https://api.cloudflare.com/#workers-kv-namespace-read-key-value-pair func (api API) GetWorkersKV(ctx context.Context, rc *ResourceContainer, params GetWorkersKVParams) ([]byte, error) { + if rc.Level != AccountRouteLevel { + return []byte(``), ErrRequiredAccountLevelResourceContainer + } + + if rc.Identifier == "" { + return []byte(``), ErrMissingIdentifier + } 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 { @@ -254,6 +300,13 @@ func (api API) GetWorkersKV(ctx context.Context, rc *ResourceContainer, params G // // API reference: https://api.cloudflare.com/#workers-kv-namespace-delete-key-value-pair func (api API) DeleteWorkersKVEntry(ctx context.Context, rc *ResourceContainer, params DeleteWorkersKVEntryParams) (Response, error) { + if rc.Level != AccountRouteLevel { + return Response{}, ErrRequiredAccountLevelResourceContainer + } + + if rc.Identifier == "" { + return Response{}, ErrMissingIdentifier + } 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 { @@ -271,6 +324,13 @@ func (api API) DeleteWorkersKVEntry(ctx context.Context, rc *ResourceContainer, // // API reference: https://api.cloudflare.com/#workers-kv-namespace-delete-multiple-key-value-pairs func (api *API) DeleteWorkersKVEntries(ctx context.Context, rc *ResourceContainer, params DeleteWorkersKVEntriesParams) (Response, error) { + if rc.Level != AccountRouteLevel { + return Response{}, ErrRequiredAccountLevelResourceContainer + } + + if rc.Identifier == "" { + return Response{}, ErrMissingIdentifier + } uri := fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/bulk", rc.Identifier, params.NamespaceID) res, err := api.makeRequestContextWithHeaders( ctx, http.MethodDelete, uri, params.Keys, http.Header{"Content-Type": []string{"application/json"}}, @@ -291,6 +351,14 @@ func (api *API) DeleteWorkersKVEntries(ctx context.Context, rc *ResourceContaine // // API Reference: https://api.cloudflare.com/#workers-kv-namespace-list-a-namespace-s-keys func (api API) ListWorkersKVKeys(ctx context.Context, rc *ResourceContainer, params ListWorkersKVsParams) (ListStorageKeysResponse, error) { + if rc.Level != AccountRouteLevel { + return ListStorageKeysResponse{}, ErrRequiredAccountLevelResourceContainer + } + + if rc.Identifier == "" { + return ListStorageKeysResponse{}, ErrMissingIdentifier + } + uri := buildURI( fmt.Sprintf("/accounts/%s/storage/kv/namespaces/%s/keys", rc.Identifier, params.NamespaceID), params, diff --git a/workers_routes.go b/workers_routes.go index 31ec7937bf..ec43d9e8bb 100644 --- a/workers_routes.go +++ b/workers_routes.go @@ -8,6 +8,8 @@ import ( "net/http" ) +var ErrMissingWorkerRouteID = errors.New("missing required route ID") + type ListWorkerRoutes struct{} type CreateWorkerRouteParams struct { @@ -18,6 +20,7 @@ type CreateWorkerRouteParams struct { type ListWorkerRoutesParams struct{} type UpdateWorkerRouteParams struct { + ID string `json:"id,omitempty"` Pattern string `json:"pattern"` Script string `json:"script,omitempty"` } @@ -140,7 +143,11 @@ func (api *API) UpdateWorkerRoute(ctx context.Context, rc *ResourceContainer, pa return WorkerRouteResponse{}, ErrMissingIdentifier } - uri := fmt.Sprintf("/zones/%s/workers/routes/%s", rc.Identifier, params.Script) + if params.ID == "" { + return WorkerRouteResponse{}, ErrMissingWorkerRouteID + } + + uri := fmt.Sprintf("/zones/%s/workers/routes/%s", rc.Identifier, params.ID) res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params) if err != nil { return WorkerRouteResponse{}, err diff --git a/workers_routes_test.go b/workers_routes_test.go index 7182c1571a..f0070a913a 100644 --- a/workers_routes_test.go +++ b/workers_routes_test.go @@ -1,348 +1,121 @@ package cloudflare -// func TestWorkers_CreateWorkerRoute(t *testing.T) { -// setup() -// defer teardown() - -// mux.HandleFunc("/zones"+testAccountID+"/workers/filters", 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/json") -// fmt.Fprintf(w, createWorkerRouteResponse) //nolint -// }) -// route := WorkerRoute{Pattern: "app1.example.com/*", Enabled: true} -// res, err := client.CreateWorkerRoute(context.Background(), "foo", route) -// want := WorkerRouteResponse{successResponse, WorkerRoute{ID: "e7a57d8746e74ae49c25994dadb421b1"}} -// if assert.NoError(t, err) { -// assert.Equal(t, want, res) -// } -// } - -// func TestWorkers_CreateWorkerRouteEnt(t *testing.T) { -// setup() -// defer teardown() - -// mux.HandleFunc("/zones"+testAccountID+"/workers/routes", 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/json") -// fmt.Fprintf(w, createWorkerRouteResponse) //nolint -// }) -// route := WorkerRoute{Pattern: "app1.example.com/*", Script: "test_script"} -// res, err := client.CreateWorkerRoute(context.Background(), "foo", route) -// want := WorkerRouteResponse{successResponse, WorkerRoute{ID: "e7a57d8746e74ae49c25994dadb421b1"}} -// if assert.NoError(t, err) { -// assert.Equal(t, want, res) -// } -// } - -// func TestWorkers_CreateWorkerRouteSingleScriptWithAccount(t *testing.T) { -// setup() -// defer teardown() - -// mux.HandleFunc("/zones"+testAccountID+"/workers/filters", 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/json") -// fmt.Fprintf(w, createWorkerRouteResponse) //nolint -// }) -// route := WorkerRoute{Pattern: "app1.example.com/*", Enabled: true} -// res, err := client.CreateWorkerRoute(context.Background(), "foo", route) -// want := WorkerRouteResponse{successResponse, WorkerRoute{ID: "e7a57d8746e74ae49c25994dadb421b1"}} -// if assert.NoError(t, err) { -// assert.Equal(t, want, res) -// } -// } - -// func TestWorkers_CreateWorkerRouteErrorsWhenMixingSingleAndMultiScriptProperties(t *testing.T) { -// setup() -// defer teardown() - -// route := WorkerRoute{Pattern: "app1.example.com/*", Script: "test_script", Enabled: true} -// _, err := client.CreateWorkerRoute(context.Background(), "foo", route) -// assert.EqualError(t, err, "Only `Script` or `Enabled` may be specified for a WorkerRoute, not both") -// } - -// func TestWorkers_CreateWorkerRouteWithNoScript(t *testing.T) { -// setup() - -// mux.HandleFunc("/zones"+testAccountID+"/workers/routes", 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/json") -// fmt.Fprintf(w, createWorkerRouteResponse) //nolint -// }) - -// route := WorkerRoute{Pattern: "app1.example.com/*"} -// _, err := client.CreateWorkerRoute(context.Background(), "foo", route) -// assert.NoError(t, err) -// } - -// func TestWorkers_DeleteWorkerRoute(t *testing.T) { -// setup() -// defer teardown() - -// mux.HandleFunc("/zones"+testAccountID+"/workers/routes/e7a57d8746e74ae49c25994dadb421b1", 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, deleteWorkerRouteResponseData) //nolint -// }) -// res, err := client.DeleteWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1") -// want := WorkerRouteResponse{successResponse, -// WorkerRoute{ -// ID: "e7a57d8746e74ae49c25994dadb421b1", -// }} -// if assert.NoError(t, err) { -// assert.Equal(t, want, res) -// } -// } - -// func TestWorkers_DeleteWorkerRouteEnt(t *testing.T) { -// setup() -// defer teardown() - -// mux.HandleFunc("/zones"+testAccountID+"/workers/routes/e7a57d8746e74ae49c25994dadb421b1", 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, deleteWorkerRouteResponseData) //nolint -// }) -// res, err := client.DeleteWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1") -// want := WorkerRouteResponse{successResponse, -// WorkerRoute{ -// ID: "e7a57d8746e74ae49c25994dadb421b1", -// }} -// if assert.NoError(t, err) { -// assert.Equal(t, want, res) -// } -// } - -// func TestWorkers_ListWorkerRoutes(t *testing.T) { -// setup() -// defer teardown() - -// mux.HandleFunc("/zones"+testAccountID+"/workers/filters", 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, listRouteResponseData) //nolint -// }) - -// res, err := client.ListWorkerRoutes(context.Background(), "foo") -// want := WorkerRoutesResponse{successResponse, -// []WorkerRoute{ -// {ID: "e7a57d8746e74ae49c25994dadb421b1", Pattern: "app1.example.com/*", Enabled: true}, -// {ID: "f8b68e9857f85bf59c25994dadb421b1", Pattern: "app2.example.com/*", Enabled: false}, -// }, -// } -// if assert.NoError(t, err) { -// assert.Equal(t, want, res) -// } -// } - -// func TestWorkers_ListWorkerRoutesEnt(t *testing.T) { -// setup() -// defer teardown() - -// mux.HandleFunc("/zones"+testAccountID+"/workers/routes", 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, listRouteEntResponseData) //nolint -// }) - -// res, err := client.ListWorkerRoutes(context.Background(), "foo") -// want := WorkerRoutesResponse{successResponse, -// []WorkerRoute{ -// {ID: "e7a57d8746e74ae49c25994dadb421b1", Pattern: "app1.example.com/*", Script: "test_script_1", Enabled: true}, -// {ID: "f8b68e9857f85bf59c25994dadb421b1", Pattern: "app2.example.com/*", Script: "test_script_2", Enabled: true}, -// {ID: "2b5bf4240cd34c77852fac70b1bf745a", Pattern: "app3.example.com/*", Script: "", Enabled: false}, -// }, -// } -// if assert.NoError(t, err) { -// assert.Equal(t, want, res) -// } -// } - -// func TestWorkers_GetWorkerRoute(t *testing.T) { -// setup() -// defer teardown() - -// mux.HandleFunc("/zones"+testAccountID+"/workers/routes/1234", 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, getRouteResponseData) //nolint -// }) - -// res, err := client.GetWorkerRoute(context.Background(), "foo", "1234") -// want := WorkerRouteResponse{successResponse, -// WorkerRoute{ -// ID: "e7a57d8746e74ae49c25994dadb421b1", -// Pattern: "app1.example.com/*", -// Script: "script-name"}, -// } -// if assert.NoError(t, err) { -// assert.Equal(t, want, res) -// } -// } - -// func TestWorkers_UpdateWorkerRoute(t *testing.T) { -// setup() -// defer teardown() - -// mux.HandleFunc("/zones"+testAccountID+"/workers/filters/e7a57d8746e74ae49c25994dadb421b1", 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, updateWorkerRouteResponse) //nolint -// }) -// route := WorkerRoute{Pattern: "app3.example.com/*", Enabled: true} -// res, err := client.UpdateWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1", route) -// want := WorkerRouteResponse{successResponse, -// WorkerRoute{ -// ID: "e7a57d8746e74ae49c25994dadb421b1", -// Pattern: "app3.example.com/*", -// Enabled: true, -// }} -// if assert.NoError(t, err) { -// assert.Equal(t, want, res) -// } -// } - -// func TestWorkers_UpdateWorkerRouteEnt(t *testing.T) { -// setup() -// defer teardown() - -// mux.HandleFunc("/zones"+testAccountID+"/workers/routes/e7a57d8746e74ae49c25994dadb421b1", 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, updateWorkerRouteEntResponse) //nolint -// }) -// route := WorkerRoute{Pattern: "app3.example.com/*", Script: "test_script_1"} -// res, err := client.UpdateWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1", route) -// want := WorkerRouteResponse{successResponse, -// WorkerRoute{ -// ID: "e7a57d8746e74ae49c25994dadb421b1", -// Pattern: "app3.example.com/*", -// Script: "test_script_1", -// }} -// if assert.NoError(t, err) { -// assert.Equal(t, want, res) -// } -// } - -// func TestWorkers_UpdateWorkerRouteSingleScriptWithAccount(t *testing.T) { -// setup() -// defer teardown() - -// mux.HandleFunc("/zones"+testAccountID+"/workers/filters/e7a57d8746e74ae49c25994dadb421b1", 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, updateWorkerRouteEntResponse) //nolint -// }) -// route := WorkerRoute{Pattern: "app3.example.com/*", Enabled: true} -// res, err := client.UpdateWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1", route) -// want := WorkerRouteResponse{successResponse, -// WorkerRoute{ -// ID: "e7a57d8746e74ae49c25994dadb421b1", -// Pattern: "app3.example.com/*", -// Script: "test_script_1", -// }} -// if assert.NoError(t, err) { -// assert.Equal(t, want, res) -// } -// } - -// func TestWorkers_ListWorkerBindingsMultiScript(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(), &WorkerRequestParams{ -// 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()) -// } - -// func TestWorkers_UpdateWorkerRouteErrorsWhenMixingSingleAndMultiScriptProperties(t *testing.T) { -// setup() -// defer teardown() - -// route := WorkerRoute{Pattern: "app1.example.com/*", Script: "test_script", Enabled: true} -// _, err := client.UpdateWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1", route) -// assert.EqualError(t, err, "Only `Script` or `Enabled` may be specified for a WorkerRoute, not both") -// } - -// func TestWorkers_UpdateWorkerRouteWithNoScript(t *testing.T) { -// setup() -// defer teardown() - -// mux.HandleFunc("/zones"+testAccountID+"/workers/routes/e7a57d8746e74ae49c25994dadb421b1", 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, updateWorkerRouteEntResponse) //nolint -// }) - -// route := WorkerRoute{Pattern: "app1.example.com/*"} -// _, err := client.UpdateWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1", route) -// assert.NoError(t, err) -// } +import ( + "context" + "fmt" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCreateWorkersRoute(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/zones/"+testZoneID+"/workers/routes", 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/json") + fmt.Fprintf(w, createWorkerRouteResponse) + }) + + res, err := client.CreateWorkerRoute(context.Background(), ZoneIdentifier(testZoneID), CreateWorkerRouteParams{ + Pattern: "app1.example.com/*", + Script: "example", + }) + + want := WorkerRouteResponse{successResponse, WorkerRoute{ID: "e7a57d8746e74ae49c25994dadb421b1"}} + if assert.NoError(t, err) { + assert.Equal(t, want, res) + } +} + +func TestDeleteWorkersRoute(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/zones/"+testZoneID+"/workers/routes/e7a57d8746e74ae49c25994dadb421b1", 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, deleteWorkerRouteResponseData) + }) + res, err := client.DeleteWorkerRoute(context.Background(), ZoneIdentifier(testZoneID), "e7a57d8746e74ae49c25994dadb421b1") + want := WorkerRouteResponse{successResponse, + WorkerRoute{ + ID: "e7a57d8746e74ae49c25994dadb421b1", + }} + if assert.NoError(t, err) { + assert.Equal(t, want, res) + } +} + +func TestListWorkersRoute(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/zones/"+testZoneID+"/workers/routes", 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, listWorkerRouteResponse) + }) + + res, err := client.ListWorkerRoutes(context.Background(), ZoneIdentifier(testZoneID), ListWorkerRoutesParams{}) + want := WorkerRoutesResponse{successResponse, + []WorkerRoute{ + {ID: "e7a57d8746e74ae49c25994dadb421b1", Pattern: "app1.example.com/*", ScriptName: "test_script_1"}, + {ID: "f8b68e9857f85bf59c25994dadb421b1", Pattern: "app2.example.com/*", ScriptName: "test_script_2"}, + {ID: "2b5bf4240cd34c77852fac70b1bf745a", Pattern: "app3.example.com/*", ScriptName: "test_script_3"}, + }, + } + if assert.NoError(t, err) { + assert.Equal(t, want, res) + } +} + +func TestGetWorkersRoute(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/zones/"+testZoneID+"/workers/routes/1234", 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, getRouteResponseData) + }) + + res, err := client.GetWorkerRoute(context.Background(), ZoneIdentifier(testZoneID), "1234") + want := WorkerRouteResponse{successResponse, + WorkerRoute{ + ID: "e7a57d8746e74ae49c25994dadb421b1", + Pattern: "app1.example.com/*", + ScriptName: "script-name"}, + } + if assert.NoError(t, err) { + assert.Equal(t, want, res) + } +} + +func TestUpdateWorkersRoute(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/zones/"+testZoneID+"/workers/routes/e7a57d8746e74ae49c25994dadb421b1", 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, updateWorkerRouteEntResponse) + }) + + res, err := client.UpdateWorkerRoute(context.Background(), ZoneIdentifier(testZoneID), UpdateWorkerRouteParams{ + ID: "e7a57d8746e74ae49c25994dadb421b1", + Pattern: "app3.example.com/*", + Script: "test_script_1", + }) + want := WorkerRouteResponse{successResponse, + WorkerRoute{ + ID: "e7a57d8746e74ae49c25994dadb421b1", + Pattern: "app3.example.com/*", + ScriptName: "test_script_1", + }} + if assert.NoError(t, err) { + assert.Equal(t, want, res) + } +} diff --git a/workers_secrets.go b/workers_secrets.go index 09d8ba337e..20d30fe54d 100644 --- a/workers_secrets.go +++ b/workers_secrets.go @@ -32,11 +32,33 @@ type WorkersListSecretsResponse struct { Result []WorkersSecret `json:"result"` } +type SetWorkersSecretParams struct { + ScriptName string + Secret *WorkersPutSecretRequest +} + +type DeleteWorkersSecretParams struct { + ScriptName string + SecretName string +} + +type ListWorkersSecretsParams struct { + ScriptName string +} + // SetWorkersSecret creates or updates a secret // API reference: https://api.cloudflare.com/ -func (api *API) SetWorkersSecret(ctx context.Context, script string, req *WorkersPutSecretRequest) (WorkersPutSecretResponse, error) { - uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/secrets", api.AccountID, script) - res, err := api.makeRequestContext(ctx, http.MethodPut, uri, req) +func (api *API) SetWorkersSecret(ctx context.Context, rc *ResourceContainer, params SetWorkersSecretParams) (WorkersPutSecretResponse, error) { + if rc.Level != AccountRouteLevel { + return WorkersPutSecretResponse{}, ErrRequiredAccountLevelResourceContainer + } + + if rc.Identifier == "" { + return WorkersPutSecretResponse{}, ErrMissingAccountID + } + + uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/secrets", rc.Identifier, params.ScriptName) + res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params.Secret) if err != nil { return WorkersPutSecretResponse{}, err } @@ -51,8 +73,16 @@ func (api *API) SetWorkersSecret(ctx context.Context, script string, req *Worker // DeleteWorkersSecret deletes a secret // API reference: https://api.cloudflare.com/ -func (api *API) DeleteWorkersSecret(ctx context.Context, script, secretName string) (Response, error) { - uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/secrets/%s", api.AccountID, script, secretName) +func (api *API) DeleteWorkersSecret(ctx context.Context, rc *ResourceContainer, params DeleteWorkersSecretParams) (Response, error) { + if rc.Level != AccountRouteLevel { + return Response{}, ErrRequiredAccountLevelResourceContainer + } + + if rc.Identifier == "" { + return Response{}, ErrMissingAccountID + } + + uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/secrets/%s", rc.Identifier, params.ScriptName, params.SecretName) res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil) if err != nil { return Response{}, err @@ -68,8 +98,16 @@ func (api *API) DeleteWorkersSecret(ctx context.Context, script, secretName stri // ListWorkersSecrets lists secrets for a given worker // API reference: https://api.cloudflare.com/ -func (api *API) ListWorkersSecrets(ctx context.Context, script string) (WorkersListSecretsResponse, error) { - uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/secrets", api.AccountID, script) +func (api *API) ListWorkersSecrets(ctx context.Context, rc *ResourceContainer, params ListWorkersSecretsParams) (WorkersListSecretsResponse, error) { + if rc.Level != AccountRouteLevel { + return WorkersListSecretsResponse{}, ErrRequiredAccountLevelResourceContainer + } + + if rc.Identifier == "" { + return WorkersListSecretsResponse{}, ErrMissingAccountID + } + + uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/secrets", rc.Identifier, params.ScriptName) res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil) if err != nil { return WorkersListSecretsResponse{}, err diff --git a/workers_secrets_test.go b/workers_secrets_test.go index c08ef71dd2..55af3d9f13 100644 --- a/workers_secrets_test.go +++ b/workers_secrets_test.go @@ -9,8 +9,8 @@ import ( "github.com/stretchr/testify/assert" ) -func TestWorkers_SetWorkersSecret(t *testing.T) { - setup(UsingAccount("foo")) +func TestSetWorkersSecret(t *testing.T) { + setup() defer teardown() response := `{ @@ -23,7 +23,7 @@ func TestWorkers_SetWorkersSecret(t *testing.T) { "messages": [] }` - mux.HandleFunc("/accounts/foo/workers/scripts/test-script/secrets", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/test-script/secrets", 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 @@ -32,7 +32,7 @@ func TestWorkers_SetWorkersSecret(t *testing.T) { Name: "my-secret", Text: "super-secret", } - res, err := client.SetWorkersSecret(context.Background(), "test-script", req) + res, err := client.SetWorkersSecret(context.Background(), AccountIdentifier(testAccountID), SetWorkersSecretParams{ScriptName: "test-script", Secret: req}) want := WorkersPutSecretResponse{ successResponse, WorkersSecret{ @@ -46,8 +46,8 @@ func TestWorkers_SetWorkersSecret(t *testing.T) { } } -func TestWorkers_DeleteWorkersSecret(t *testing.T) { - setup(UsingAccount("foo")) +func TestDeleteWorkersSecret(t *testing.T) { + setup() defer teardown() response := `{ @@ -60,13 +60,13 @@ func TestWorkers_DeleteWorkersSecret(t *testing.T) { "messages": [] }` - mux.HandleFunc("/accounts/foo/workers/scripts/test-script/secrets/my-secret", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/test-script/secrets/my-secret", 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.DeleteWorkersSecret(context.Background(), "test-script", "my-secret") + res, err := client.DeleteWorkersSecret(context.Background(), AccountIdentifier(testAccountID), DeleteWorkersSecretParams{ScriptName: "test-script", SecretName: "my-secret"}) want := successResponse if assert.NoError(t, err) { @@ -74,8 +74,8 @@ func TestWorkers_DeleteWorkersSecret(t *testing.T) { } } -func TestWorkers_ListWorkersSecret(t *testing.T) { - setup(UsingAccount("foo")) +func TestListWorkersSecret(t *testing.T) { + setup() defer teardown() response := `{ @@ -88,13 +88,13 @@ func TestWorkers_ListWorkersSecret(t *testing.T) { "messages": [] }` - mux.HandleFunc("/accounts/foo/workers/scripts/test-script/secrets", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/test-script/secrets", 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.ListWorkersSecrets(context.Background(), "test-script") + res, err := client.ListWorkersSecrets(context.Background(), AccountIdentifier(testAccountID), ListWorkersSecretsParams{ScriptName: "test-script"}) want := WorkersListSecretsResponse{ successResponse, []WorkersSecret{ diff --git a/workers_tail.go b/workers_tail.go index 478112b4a0..09d7f2fd1a 100644 --- a/workers_tail.go +++ b/workers_tail.go @@ -42,6 +42,7 @@ func (api *API) StartWorkersTail(ctx context.Context, rc *ResourceContainer, scr if rc.Identifier == "" { return WorkersTail{}, ErrMissingAccountID } + if scriptName == "" { return WorkersTail{}, ErrMissingScriptName } diff --git a/workers_test.go b/workers_test.go index 9890b35dba..0fae20b2a4 100644 --- a/workers_test.go +++ b/workers_test.go @@ -89,7 +89,7 @@ const ( "errors": [], "messages": [] }` - listRouteEntResponseData = `{ + listWorkerRouteResponse = `{ "result": [ { "id": "e7a57d8746e74ae49c25994dadb421b1", @@ -103,7 +103,8 @@ const ( }, { "id": "2b5bf4240cd34c77852fac70b1bf745a", - "pattern": "app3.example.com/*" + "pattern": "app3.example.com/*", + "script": "test_script_3" } ], "success": true, @@ -384,7 +385,7 @@ func TestListWorkers(t *testing.T) { fmt.Fprintf(w, listWorkersResponseData) }) - res, err := client.ListWorkers(context.Background(), AccountIdentifier(testAccountID), ListWorkersParams{}) + res, _, err := client.ListWorkers(context.Background(), AccountIdentifier(testAccountID), ListWorkersParams{}) sampleDate, _ := time.Parse(time.RFC3339Nano, "2018-04-22T17:10:48.938097Z") want := []WorkerMetaData{ { @@ -416,7 +417,7 @@ func TestUploadWorker_Basic(t *testing.T) { w.Header().Set("content-type", "application/json") fmt.Fprintf(w, uploadWorkerResponseData) //nolint }) - res, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{Name: "foo", Script: workerScript}) + res, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{ScriptName: "foo", Script: workerScript}) formattedTime, _ := time.Parse(time.RFC3339Nano, "2018-06-09T15:17:01.989141Z") want := WorkerScriptResponse{ successResponse, @@ -455,7 +456,7 @@ func TestUploadWorker_Module(t *testing.T) { w.Header().Set("content-type", "application/json") fmt.Fprintf(w, uploadWorkerModuleResponseData) }) - res, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{Name: "foo", Script: workerModuleScript, Module: true}) + res, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{ScriptName: "foo", Script: workerModuleScript, Module: true}) formattedTime, _ := time.Parse(time.RFC3339Nano, "2018-06-09T15:17:01.989141Z") want := WorkerScriptResponse{ successResponse, @@ -501,8 +502,8 @@ func TestUploadWorker_WithDurableObjectBinding(t *testing.T) { mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/bar", handler) _, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{ - Name: "bar", - Script: workerScript, + ScriptName: "bar", + Script: workerScript, Bindings: map[string]WorkerBinding{ "b1": WorkerDurableObjectBinding{ ClassName: "TheClass", @@ -558,8 +559,8 @@ func TestUploadWorker_WithInheritBinding(t *testing.T) { }} res, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{ - Name: "bar", - Script: workerScript, + ScriptName: "bar", + Script: workerScript, Bindings: map[string]WorkerBinding{ "b1": WorkerInheritBinding{}, "b2": WorkerInheritBinding{ @@ -597,8 +598,8 @@ func TestUploadWorker_WithKVBinding(t *testing.T) { mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/bar", handler) _, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{ - Name: "bar", - Script: workerScript, + ScriptName: "bar", + Script: workerScript, Bindings: map[string]WorkerBinding{ "b1": WorkerKvNamespaceBinding{ NamespaceID: "test-namespace", @@ -638,8 +639,8 @@ func TestUploadWorker_WithWasmBinding(t *testing.T) { mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/bar", handler) _, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{ - Name: "bar", - Script: workerScript, + ScriptName: "bar", + Script: workerScript, Bindings: map[string]WorkerBinding{ "b1": WorkerWebAssemblyBinding{ Module: strings.NewReader("fake-wasm"), @@ -676,8 +677,8 @@ func TestUploadWorker_WithPlainTextBinding(t *testing.T) { mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/bar", handler) _, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{ - Name: "bar", - Script: workerScript, + ScriptName: "bar", + Script: workerScript, Bindings: map[string]WorkerBinding{ "b1": WorkerPlainTextBinding{ Text: "plain text value", @@ -721,9 +722,9 @@ func TestUploadWorker_ModuleWithPlainTextBinding(t *testing.T) { }) _, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{ - Name: "bar", - Script: workerModuleScript, - Module: true, + ScriptName: "bar", + Script: workerModuleScript, + Module: true, Bindings: map[string]WorkerBinding{ "b1": WorkerPlainTextBinding{ Text: "plain text value", @@ -760,8 +761,8 @@ func TestUploadWorker_WithSecretTextBinding(t *testing.T) { mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/bar", handler) _, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{ - Name: "bar", - Script: workerScript, + ScriptName: "bar", + Script: workerScript, Bindings: map[string]WorkerBinding{ "b1": WorkerSecretTextBinding{ Text: "secret text value", @@ -803,8 +804,8 @@ func TestUploadWorker_WithServiceBinding(t *testing.T) { mux.HandleFunc("/accounts/"+testAccountID+"/workers/scripts/bar", handler) _, err := client.UploadWorker(context.Background(), AccountIdentifier(testAccountID), CreateWorkerParams{ - Name: "bar", - Script: workerScript, + ScriptName: "bar", + Script: workerScript, Bindings: map[string]WorkerBinding{ "b1": WorkerServiceBinding{ Service: "the_service",