Repository: cloudflare/cloudflare-go
Pull request #1017 (2022-08-04 14:38:18)

zone-lockdown: paginating list response
## Description

I'm adding pagination for `ListZoneLockdowns` API. Additionally, I'm bringing Lockdown APIs to a common pattern. Instead of returning `ZoneLockdownResponse` they will return `ZoneLockdown` so they are easier to use.

## Has your change been tested?

Unit tests are working

## Screenshots (if appropriate):

## Types of changes

What sort of change does your code introduce/modify?

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)

## Checklist:

- [x] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [x] I have added tests to cover my changes.
- [x] All new and existing tests passed.
- [x] This change is using publicly documented (api.cloudflare.com or developers.cloudflare.com) and stable APIs.

[1]: https://help.github.com/articles/closing-issues-using-keywords/


github-actions[bot]: Oops! It looks like no changelog entry is attached to this PR. Please include a release note as described in https://github.com/cloudflare/cloudflare-go/blob/master/docs/changelog-process.md.

Example: 

~~~
```release-note:TYPE
Release note
```
~~~

If you do not require a release note to be included, please add the `workflow/skip-changelog-entry` label.
tamas-jozsa: > instead of introducing a change that takes us further away from the [experimental method signatures](https://github.com/cloudflare/cloudflare-go/blob/master/docs/experimental.md), let's update it to move towards it.
> 
> check out https://github.com/cloudflare/cloudflare-go/blob/master/tunnel.go for an example of where we should head.

That's very useful, thanks Jacob! I've opened an issue for further clarifications: [1023](https://github.com/cloudflare/cloudflare-go/issues/1023) 

Patch:
diff --git a/.changelog/1017.txt b/.changelog/1017.txt
new file mode 100644
index 000000000..56bd30386
--- /dev/null
+++ b/.changelog/1017.txt
@@ -0,0 +1,3 @@
+```release-note:enhancement
+lockdown: automatically paginate `List` results unless `Page` and `PerPage` are provided
+```
\ No newline at end of file
diff --git a/.changelog/1020.txt b/.changelog/1020.txt
deleted file mode 100644
index 051b9e79d..000000000
--- a/.changelog/1020.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-```release-note:dependency
-provider: bumps github.com/golangci/golangci-lint from 1.47.3 to 1.48.0
-```
diff --git a/.changelog/1026.txt b/.changelog/1026.txt
deleted file mode 100644
index 3a43ce2de..000000000
--- a/.changelog/1026.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-```release-note:enhancement
-workers-tail: Add in support for Workers tail API
-```
\ No newline at end of file
diff --git a/.changelog/1027.txt b/.changelog/1027.txt
deleted file mode 100644
index 4661228cd..000000000
--- a/.changelog/1027.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-```release-note:enhancement
-workers-account-settings: Add in support for Workers account settings API
-```
\ No newline at end of file
diff --git a/.changelog/1028.txt b/.changelog/1028.txt
deleted file mode 100644
index f76d16ff3..000000000
--- a/.changelog/1028.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-```release-note:enhancement
-r2: Add in support for creating and deleting R2 buckets
-```
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 73a2af949..64a93bc59 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,15 +1,5 @@
 ## 0.47.0 (Unreleased)
 
-ENHANCEMENTS:
-
-* r2: Add in support for creating and deleting R2 buckets ([#1028](https://github.com/cloudflare/cloudflare-go/issues/1028))
-* workers-account-settings: Add in support for Workers account settings API ([#1027](https://github.com/cloudflare/cloudflare-go/issues/1027))
-* workers-tail: Add in support for Workers tail API ([#1026](https://github.com/cloudflare/cloudflare-go/issues/1026))
-
-DEPENDENCIES:
-
-* provider: bumps github.com/golangci/golangci-lint from 1.47.3 to 1.48.0 ([#1020](https://github.com/cloudflare/cloudflare-go/issues/1020))
-
 ## 0.46.0 (3rd August, 2022)
 
 NOTES:
diff --git a/cmd/flarectl/zone.go b/cmd/flarectl/zone.go
index ad771b11c..5cd2c81a4 100644
--- a/cmd/flarectl/zone.go
+++ b/cmd/flarectl/zone.go
@@ -133,21 +133,25 @@ func zoneCreateLockdown(c *cli.Context) error {
 			Value:  c.StringSlice("values")[index],
 		})
 	}
-	lockdown := cloudflare.ZoneLockdown{
+	params := cloudflare.ZoneLockdownCreateParams{
 		Description:    c.String("description"),
 		URLs:           c.StringSlice("urls"),
 		Configurations: zonelockdownconfigs,
 	}
 
-	var resp *cloudflare.ZoneLockdownResponse
-
-	resp, err = api.CreateZoneLockdown(context.Background(), zoneID, lockdown)
+	resp, err := api.CreateZoneLockdown(context.Background(), cloudflare.ZoneIdentifier(zoneID), params)
 	if err != nil {
 		fmt.Fprintln(os.Stderr, "Error creating ZONE lock down: ", err)
 		return err
 	}
+
 	output := make([][]string, 0, 1)
-	output = append(output, formatLockdownResponse(resp))
+
+	format := []string{
+		resp.ID,
+	}
+
+	output = append(output, format)
 
 	writeTable(c, output, "ID")
 
@@ -335,12 +339,6 @@ func formatCacheResponse(resp cloudflare.PurgeCacheResponse) []string {
 	}
 }
 
-func formatLockdownResponse(resp *cloudflare.ZoneLockdownResponse) []string {
-	return []string{
-		resp.Result.ID,
-	}
-}
-
 func zoneExport(c *cli.Context) error {
 	var zone string
 	if c.NArg() > 0 {
diff --git a/go.mod b/go.mod
index d3a03d072..f5ea54fcb 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,7 @@ require (
 	github.com/client9/misspell v0.3.4
 	github.com/cweill/gotests v1.6.0
 	github.com/go-delve/delve v1.9.0
-	github.com/golangci/golangci-lint v1.48.0
+	github.com/golangci/golangci-lint v1.47.3
 	github.com/google/go-querystring v1.1.0
 	github.com/hashicorp/go-changelog v0.0.0-20220419201213-5edfc0d651d8
 	github.com/hashicorp/go-retryablehttp v0.7.1
@@ -54,7 +54,6 @@ require (
 	github.com/mitchellh/reflectwalk v1.0.2 // indirect
 	github.com/posener/complete v1.2.3 // indirect
 	github.com/russross/blackfriday v1.6.0 // indirect
-	github.com/sashamelentyev/usestdlibvars v1.8.0 // indirect
 	github.com/shopspring/decimal v1.3.1 // indirect
 	github.com/xanzy/ssh-agent v0.3.0 // indirect
 	github.com/zclconf/go-cty v1.10.0 // indirect
@@ -89,7 +88,7 @@ require (
 	github.com/cilium/ebpf v0.7.0 // indirect
 	github.com/cosiner/argv v0.1.0 // indirect
 	github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
-	github.com/daixiang0/gci v0.6.2 // indirect
+	github.com/daixiang0/gci v0.5.0 // indirect
 	github.com/dave/dst v0.26.2 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/denis-tingaikin/go-header v0.4.3 // indirect
@@ -121,7 +120,7 @@ require (
 	github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 // indirect
 	github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca // indirect
 	github.com/golangci/misspell v0.3.5 // indirect
-	github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 // indirect
+	github.com/golangci/revgrep v0.0.0-20210930125155-c22e5001d4f2 // indirect
 	github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 // indirect
 	github.com/google/go-cmp v0.5.8 // indirect
 	github.com/google/go-dap v0.6.0 // indirect
@@ -144,7 +143,7 @@ require (
 	github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af // indirect
 	github.com/julz/importas v0.1.0 // indirect
 	github.com/karrick/godirwalk v1.12.0 // indirect
-	github.com/kisielk/errcheck v1.6.2 // indirect
+	github.com/kisielk/errcheck v1.6.1 // indirect
 	github.com/kisielk/gotool v1.0.0 // indirect
 	github.com/kulti/thelper v0.6.3 // indirect
 	github.com/kunwardeep/paralleltest v1.0.6 // indirect
diff --git a/go.sum b/go.sum
index 6ae87bca1..3c5575203 100644
--- a/go.sum
+++ b/go.sum
@@ -237,8 +237,8 @@ github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/cweill/gotests v1.6.0 h1:KJx+/p4EweijYzqPb4Y/8umDCip1Cv6hEVyOx0mE9W8=
 github.com/cweill/gotests v1.6.0/go.mod h1:CaRYbxQZGQOxXDvM9l0XJVV2Tjb2E5H53vq+reR2GrA=
-github.com/daixiang0/gci v0.6.2 h1:TXCP5RqjE/UupXO+p33MEhqdv7QxjKGw5MVkt9ATiMs=
-github.com/daixiang0/gci v0.6.2/go.mod h1:EpVfrztufwVgQRXjnX4zuNinEpLj5OmMjtu/+MB0V0c=
+github.com/daixiang0/gci v0.5.0 h1:3+Z8nb/4dhJQYjpEbG4wt5na+KFJJTZ++PVEq/MVKX4=
+github.com/daixiang0/gci v0.5.0/go.mod h1:EpVfrztufwVgQRXjnX4zuNinEpLj5OmMjtu/+MB0V0c=
 github.com/dave/dst v0.26.2 h1:lnxLAKI3tx7MgLNVDirFCsDTlTG9nKTk7GcptKcWSwY=
 github.com/dave/dst v0.26.2/go.mod h1:UMDJuIRPfyUCC78eFuB+SV/WI8oDeyFDvM/JR6NI3IU=
 github.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e/go.mod h1:i00+b/gKdIDIxuLDFob7ustLAVqhsZRk2qVZrArELGQ=
@@ -409,16 +409,16 @@ github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe h1:6RGUuS7EGotKx6
 github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe/go.mod h1:gjqyPShc/m8pEMpk0a3SeagVb0kaqvhscv+i9jI5ZhQ=
 github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a h1:iR3fYXUjHCR97qWS8ch1y9zPNsgXThGwjKPrYfqMPks=
 github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
-github.com/golangci/golangci-lint v1.48.0 h1:hRiBNk9iRqdAKMa06ntfEiLyza1/3IE9rHLNJaek4a8=
-github.com/golangci/golangci-lint v1.48.0/go.mod h1:5N+oxduCho+7yuccW69upg/O7cxjfR/d+IQeiNxGmKM=
+github.com/golangci/golangci-lint v1.47.3 h1:ri7A2DgtFpxgqcMSsU3qIT0IBm/SCdYgXlvmJx4szUU=
+github.com/golangci/golangci-lint v1.47.3/go.mod h1:IvT5xyPX1W8JUJJrV60gcMzgQe1ttW/38yAzn6LuHOk=
 github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA=
 github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
 github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA=
 github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
 github.com/golangci/misspell v0.3.5 h1:pLzmVdl3VxTOncgzHcvLOKirdvcx/TydsClUQXTehjo=
 github.com/golangci/misspell v0.3.5/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
-github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 h1:DIPQnGy2Gv2FSA4B/hh8Q7xx3B7AIDk3DAMeHclH1vQ=
-github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6/go.mod h1:0AKcRCkMoKvUvlf89F6O7H2LYdhr1zBh736mBItOdRs=
+github.com/golangci/revgrep v0.0.0-20210930125155-c22e5001d4f2 h1:SgM7GDZTxtTTQPU84heOxy34iG5Du7F2jcoZnvp+fXI=
+github.com/golangci/revgrep v0.0.0-20210930125155-c22e5001d4f2/go.mod h1:LK+zW4MpyytAWQRz0M4xnzEk50lSvqDQKfx304apFkY=
 github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys=
 github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -691,8 +691,8 @@ github.com/keybase/go-crypto v0.0.0-20161004153544-93f5b35093ba/go.mod h1:ghbZsc
 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
-github.com/kisielk/errcheck v1.6.2 h1:uGQ9xI8/pgc9iOoCe7kWQgRE6SBTrCGmTSf0LrEtY7c=
-github.com/kisielk/errcheck v1.6.2/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw=
+github.com/kisielk/errcheck v1.6.1 h1:cErYo+J4SmEjdXZrVXGwLJCE2sB06s23LpkcyWNrT+s=
+github.com/kisielk/errcheck v1.6.1/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw=
 github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
@@ -978,8 +978,6 @@ github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43
 github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5AxSgDyEQcea8=
 github.com/sanposhiho/wastedassign/v2 v2.0.6 h1:+6/hQIHKNJAUixEj6EmOngGIisyeI+T3335lYTyxRoA=
 github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI=
-github.com/sashamelentyev/usestdlibvars v1.8.0 h1:QnWP9IOEuRyYKH+IG0LlQIjuJlc0rfdo4K3/Zh3WRMw=
-github.com/sashamelentyev/usestdlibvars v1.8.0/go.mod h1:BFt7b5mSVHaaa26ZupiNRV2ODViQBxZZVhtAxAJRrjs=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4=
 github.com/securego/gosec/v2 v2.12.0 h1:CQWdW7ATFpvLSohMVsajscfyHJ5rsGmEXmsNcsDNmAg=
diff --git a/lockdown.go b/lockdown.go
index 50009f796..bcbdd11f5 100644
--- a/lockdown.go
+++ b/lockdown.go
@@ -5,8 +5,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"net/http"
-	"net/url"
-	"strconv"
 	"time"
 )
 
@@ -46,109 +44,145 @@ type ZoneLockdownListResponse struct {
 	ResultInfo `json:"result_info"`
 }
 
+// ZoneLockdownCreateParams contains required and optional params
+// for creating a zone lockdown.
+type ZoneLockdownCreateParams struct {
+	Description    string               `json:"description"`
+	URLs           []string             `json:"urls"`
+	Configurations []ZoneLockdownConfig `json:"configurations"`
+	Paused         bool                 `json:"paused"`
+}
+
+// ZoneLockdownUpdateParams contains required and optional params
+// for updating a zone lockdown.
+type ZoneLockdownUpdateParams struct {
+	ID             string               `json:"id"`
+	Description    string               `json:"description"`
+	URLs           []string             `json:"urls"`
+	Configurations []ZoneLockdownConfig `json:"configurations"`
+	Paused         bool                 `json:"paused"`
+}
+
+type LockdownListParams struct {
+	ResultInfo
+}
+
 // CreateZoneLockdown creates a Zone ZoneLockdown rule for the given zone ID.
 //
 // API reference: https://api.cloudflare.com/#zone-ZoneLockdown-create-a-ZoneLockdown-rule
-func (api *API) CreateZoneLockdown(ctx context.Context, zoneID string, ld ZoneLockdown) (*ZoneLockdownResponse, error) {
-	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns", zoneID)
-	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, ld)
+func (api *API) CreateZoneLockdown(ctx context.Context, rc *ResourceContainer, params ZoneLockdownCreateParams) (ZoneLockdown, error) {
+	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns", rc.Identifier)
+	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params)
 	if err != nil {
-		return nil, err
+		return ZoneLockdown{}, err
 	}
 
 	response := &ZoneLockdownResponse{}
 	err = json.Unmarshal(res, &response)
 	if err != nil {
-		return nil, fmt.Errorf("%s: %w", errUnmarshalError, err)
+		return ZoneLockdown{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
 	}
 
-	return response, nil
+	return response.Result, nil
 }
 
-// UpdateZoneLockdown updates a Zone ZoneLockdown rule (based on the ID) for the
-// given zone ID.
+// UpdateZoneLockdown updates a Zone ZoneLockdown rule (based on the ID) for the given zone ID.
 //
 // API reference: https://api.cloudflare.com/#zone-ZoneLockdown-update-ZoneLockdown-rule
-func (api *API) UpdateZoneLockdown(ctx context.Context, zoneID string, id string, ld ZoneLockdown) (*ZoneLockdownResponse, error) {
-	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns/%s", zoneID, id)
-	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, ld)
+func (api *API) UpdateZoneLockdown(ctx context.Context, rc *ResourceContainer, params ZoneLockdownUpdateParams) (ZoneLockdown, error) {
+	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns/%s", rc.Identifier, params.ID)
+	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params)
 	if err != nil {
-		return nil, err
+		return ZoneLockdown{}, err
 	}
 
 	response := &ZoneLockdownResponse{}
 	err = json.Unmarshal(res, &response)
 	if err != nil {
-		return nil, fmt.Errorf("%s: %w", errUnmarshalError, err)
+		return ZoneLockdown{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
 	}
 
-	return response, nil
+	return response.Result, nil
 }
 
-// DeleteZoneLockdown deletes a Zone ZoneLockdown rule (based on the ID) for the
-// given zone ID.
+// DeleteZoneLockdown deletes a Zone ZoneLockdown rule (based on the ID) for the given zone ID.
 //
 // API reference: https://api.cloudflare.com/#zone-ZoneLockdown-delete-ZoneLockdown-rule
-func (api *API) DeleteZoneLockdown(ctx context.Context, zoneID string, id string) (*ZoneLockdownResponse, error) {
-	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns/%s", zoneID, id)
+func (api *API) DeleteZoneLockdown(ctx context.Context, rc *ResourceContainer, id string) (ZoneLockdown, error) {
+	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns/%s", rc.Identifier, id)
 	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
 	if err != nil {
-		return nil, err
+		return ZoneLockdown{}, err
 	}
 
 	response := &ZoneLockdownResponse{}
 	err = json.Unmarshal(res, &response)
 	if err != nil {
-		return nil, fmt.Errorf("%s: %w", errUnmarshalError, err)
+		return ZoneLockdown{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
 	}
 
-	return response, nil
+	return response.Result, nil
 }
 
-// ZoneLockdown retrieves a Zone ZoneLockdown rule (based on the ID) for the
-// given zone ID.
+// ZoneLockdown retrieves a Zone ZoneLockdown rule (based on the ID) for the given zone ID.
 //
 // API reference: https://api.cloudflare.com/#zone-ZoneLockdown-ZoneLockdown-rule-details
-func (api *API) ZoneLockdown(ctx context.Context, zoneID string, id string) (*ZoneLockdownResponse, error) {
-	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns/%s", zoneID, id)
+func (api *API) ZoneLockdown(ctx context.Context, rc *ResourceContainer, id string) (ZoneLockdown, error) {
+	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns/%s", rc.Identifier, id)
 	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
 	if err != nil {
-		return nil, err
+		return ZoneLockdown{}, err
 	}
 
 	response := &ZoneLockdownResponse{}
 	err = json.Unmarshal(res, &response)
 	if err != nil {
-		return nil, fmt.Errorf("%s: %w", errUnmarshalError, err)
+		return ZoneLockdown{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
 	}
 
-	return response, nil
+	return response.Result, nil
 }
 
-// ListZoneLockdowns retrieves a list of Zone ZoneLockdown rules for a given
-// zone ID by page number.
+// ListZoneLockdowns retrieves every Zone ZoneLockdown rules for a given zone ID.
+//
+// Automatically paginates all results unless `params.PerPage` and `params.Page`
+// is set.
 //
 // API reference: https://api.cloudflare.com/#zone-ZoneLockdown-list-ZoneLockdown-rules
-func (api *API) ListZoneLockdowns(ctx context.Context, zoneID string, page int) (*ZoneLockdownListResponse, error) {
-	v := url.Values{}
-	if page <= 0 {
-		page = 1
+func (api *API) ListZoneLockdowns(ctx context.Context, rc *ResourceContainer, params LockdownListParams) ([]ZoneLockdown, *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
 	}
 
-	v.Set("page", strconv.Itoa(page))
-	v.Set("per_page", strconv.Itoa(100))
+	var zoneLockdowns []ZoneLockdown
+	var zResponse ZoneLockdownListResponse
+	for {
+		uri := buildURI(fmt.Sprintf("/zones/%s/firewall/lockdowns", rc.Identifier), params)
 
-	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns?%s", zoneID, v.Encode())
-	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
-	if err != nil {
-		return nil, err
-	}
+		res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+		if err != nil {
+			return []ZoneLockdown{}, &ResultInfo{}, err
+		}
 
-	response := &ZoneLockdownListResponse{}
-	err = json.Unmarshal(res, &response)
-	if err != nil {
-		return nil, fmt.Errorf("%s: %w", errUnmarshalError, err)
+		err = json.Unmarshal(res, &zResponse)
+		if err != nil {
+			return []ZoneLockdown{}, &ResultInfo{}, fmt.Errorf("failed to unmarshal filters JSON data: %w", err)
+		}
+
+		zoneLockdowns = append(zoneLockdowns, zResponse.Result...)
+		params.ResultInfo = zResponse.ResultInfo.Next()
+
+		if params.ResultInfo.Done() || !autoPaginate {
+			break
+		}
 	}
 
-	return response, nil
+	return zoneLockdowns, &zResponse.ResultInfo, nil
 }
diff --git a/r2_bucket.go b/r2_bucket.go
deleted file mode 100644
index 63cb64a6b..000000000
--- a/r2_bucket.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package cloudflare
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"net/http"
-)
-
-var (
-	ErrMissingBucketName = errors.New("require bucket name missing")
-)
-
-// CreateR2Bucket Creates a new R2 bucket.
-//
-// API reference: https://api.cloudflare.com/#r2-bucket-create-bucket
-func (api *API) CreateR2Bucket(ctx context.Context, rc *ResourceContainer, bucketName string) error {
-	if rc.Identifier == "" {
-		return ErrMissingAccountID
-	}
-
-	if bucketName == "" {
-		return ErrMissingBucketName
-	}
-
-	uri := fmt.Sprintf("/accounts/%s/r2/buckets/%s", rc.Identifier, bucketName)
-	_, err := api.makeRequestContext(ctx, http.MethodPost, uri, nil)
-
-	return err
-}
-
-// DeleteR2Bucket Deletes an existing R2 bucket.
-//
-// API reference: https://api.cloudflare.com/#r2-bucket-delete-bucket
-func (api *API) DeleteR2Bucket(ctx context.Context, rc *ResourceContainer, bucketName string) error {
-	if rc.Identifier == "" {
-		return ErrMissingAccountID
-	}
-
-	if bucketName == "" {
-		return ErrMissingBucketName
-	}
-
-	uri := fmt.Sprintf("/accounts/%s/r2/buckets/%s", rc.Identifier, bucketName)
-	_, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
-
-	return err
-}
diff --git a/workers_account_settings.go b/workers_account_settings.go
deleted file mode 100644
index 942db6dd0..000000000
--- a/workers_account_settings.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package cloudflare
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"net/http"
-)
-
-type WorkersAccountSettings struct {
-	DefaultUsageModel string `json:"default_usage_model,omitempty"`
-	GreenCompute      bool   `json:"green_compute,omitempty"`
-}
-
-type CreateWorkersAccountSettingsParameters struct {
-	DefaultUsageModel string `json:"default_usage_model,omitempty"`
-	GreenCompute      bool   `json:"green_compute,omitempty"`
-}
-
-type CreateWorkersAccountSettingsResponse struct {
-	Response
-	Result WorkersAccountSettings
-}
-
-type WorkersAccountSettingsParameters struct{}
-
-type WorkersAccountSettingsResponse struct {
-	Response
-	Result WorkersAccountSettings
-}
-
-// CreateWorkersAccountSettings sets the account settings for Workers.
-//
-// API reference: https://api.cloudflare.com/#worker-account-settings-create-worker-account-settings
-func (api *API) CreateWorkersAccountSettings(ctx context.Context, rc *ResourceContainer, params CreateWorkersAccountSettingsParameters) (WorkersAccountSettings, error) {
-	if rc.Identifier == "" {
-		return WorkersAccountSettings{}, ErrMissingAccountID
-	}
-
-	uri := fmt.Sprintf("/accounts/%s/workers/account-settings", rc.Identifier)
-	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params)
-	if err != nil {
-		return WorkersAccountSettings{}, err
-	}
-
-	var workersAccountSettingsResponse CreateWorkersAccountSettingsResponse
-	if err := json.Unmarshal(res, &workersAccountSettingsResponse); err != nil {
-		return WorkersAccountSettings{}, err
-	}
-
-	return workersAccountSettingsResponse.Result, nil
-}
-
-// WorkersAccountSettings returns the current account settings for Workers.
-//
-// API reference: https://api.cloudflare.com/#worker-account-settings-fetch-worker-account-settings
-func (api *API) WorkersAccountSettings(ctx context.Context, rc *ResourceContainer, params WorkersAccountSettingsParameters) (WorkersAccountSettings, error) {
-	if rc.Identifier == "" {
-		return WorkersAccountSettings{}, ErrMissingAccountID
-	}
-
-	uri := fmt.Sprintf("/accounts/%s/workers/account-settings", rc.Identifier)
-	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, params)
-	if err != nil {
-		return WorkersAccountSettings{}, err
-	}
-
-	var workersAccountSettingsResponse CreateWorkersAccountSettingsResponse
-	if err := json.Unmarshal(res, &workersAccountSettingsResponse); err != nil {
-		return WorkersAccountSettings{}, err
-	}
-
-	return workersAccountSettingsResponse.Result, nil
-}
diff --git a/workers_tail.go b/workers_tail.go
deleted file mode 100644
index 672f82191..000000000
--- a/workers_tail.go
+++ /dev/null
@@ -1,112 +0,0 @@
-package cloudflare
-
-import (
-	"context"
-	"encoding/json"
-	"errors"
-	"fmt"
-	"net/http"
-	"time"
-)
-
-var (
-	ErrMissingScriptName = errors.New("required script name missing")
-	ErrMissingTailID     = errors.New("required tail id missing")
-)
-
-type WorkersTail struct {
-	ID        string     `json:"id"`
-	URL       string     `json:"url"`
-	ExpiresAt *time.Time `json:"expires_at"`
-}
-
-type StartWorkersTailResponse struct {
-	Response
-	Result WorkersTail
-}
-
-type ListWorkersTailParameters struct {
-	AccountID  string
-	ScriptName string
-}
-
-type ListWorkersTailResponse struct {
-	Response
-	Result WorkersTail
-}
-
-// StartWorkersTail Starts a tail that receives logs and exception from a Worker
-//
-// API reference: https://api.cloudflare.com/#worker-tail-logs-start-tail
-func (api *API) StartWorkersTail(ctx context.Context, rc *ResourceContainer, scriptName string) (WorkersTail, error) {
-	if rc.Identifier == "" {
-		return WorkersTail{}, ErrMissingAccountID
-	}
-	if scriptName == "" {
-		return WorkersTail{}, ErrMissingScriptName
-	}
-
-	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/tails", rc.Identifier, scriptName)
-	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, nil)
-	if err != nil {
-		return WorkersTail{}, err
-	}
-
-	var workerstailResponse StartWorkersTailResponse
-	if err := json.Unmarshal(res, &workerstailResponse); err != nil {
-		return WorkersTail{}, err
-	}
-
-	return workerstailResponse.Result, nil
-}
-
-// ListWorkersTail Get list of tails currently deployed on a worker
-//
-// API reference: https://api.cloudflare.com/#worker-tail-logs-list-tails
-func (api *API) ListWorkersTail(ctx context.Context, rc *ResourceContainer, params ListWorkersTailParameters) (WorkersTail, error) {
-	if rc.Identifier == "" {
-		return WorkersTail{}, ErrMissingAccountID
-	}
-
-	if params.ScriptName == "" {
-		return WorkersTail{}, ErrMissingScriptName
-	}
-
-	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/tails", rc.Identifier, params.ScriptName)
-	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
-	if err != nil {
-		return WorkersTail{}, err
-	}
-
-	var workerstailResponse ListWorkersTailResponse
-	if err := json.Unmarshal(res, &workerstailResponse); err != nil {
-		return WorkersTail{}, err
-	}
-
-	return workerstailResponse.Result, nil
-}
-
-// DeleteWorkersTail Deletes a tail from a Worker
-//
-// API reference: https://api.cloudflare.com/#worker-tail-logs-delete-tail
-func (api *API) DeleteWorkersTail(ctx context.Context, rc *ResourceContainer, scriptName, tailID string) error {
-	if rc.Identifier == "" {
-		return ErrMissingAccountID
-	}
-
-	if scriptName == "" {
-		return ErrMissingScriptName
-	}
-
-	if tailID == "" {
-		return ErrMissingTailID
-	}
-
-	uri := fmt.Sprintf("/accounts/%s/workers/scripts/%s/tails/%s", rc.Identifier, scriptName, tailID)
-	_, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index a7cfdd13f..5c307228f 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -4,7 +4,7 @@ jobs:
   test:
     strategy:
       matrix:
-        go-version: [1.17, 1.18, 1.19]
+        go-version: [1.17, 1.18]
         os: [ubuntu-latest, macos-latest, windows-latest]
     runs-on: ${{ matrix.os }}
     steps:
diff --git a/lockdown_example_test.go b/lockdown_example_test.go
index 4f9d90844..8df05e116 100644
--- a/lockdown_example_test.go
+++ b/lockdown_example_test.go
@@ -21,12 +21,12 @@ func ExampleAPI_ListZoneLockdowns_all() {
 	}
 
 	// Fetch all Zone Lockdown rules for a zone, by page.
-	rules, err := api.ListZoneLockdowns(context.Background(), zoneID, 1)
+	rules, _, err := api.ListZoneLockdowns(context.Background(), cloudflare.ZoneIdentifier(zoneID), cloudflare.LockdownListParams{})
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	for _, r := range rules.Result {
+	for _, r := range rules {
 		fmt.Printf("%s: %s\n", strings.Join(r.URLs, ", "), r.Configurations)
 	}
 }
@@ -42,7 +42,7 @@ func ExampleAPI_CreateZoneLockdown() {
 		log.Fatal(err)
 	}
 
-	newZoneLockdown := cloudflare.ZoneLockdown{
+	newZoneLockdown := cloudflare.ZoneLockdownCreateParams{
 		Description: "Test Zone Lockdown Rule",
 		URLs: []string{
 			"*.example.org/test",
@@ -53,11 +53,10 @@ func ExampleAPI_CreateZoneLockdown() {
 				Value:  "127.0.0.1",
 			},
 		},
-		Paused:   false,
-		Priority: 1,
+		Paused: false,
 	}
 
-	response, err := api.CreateZoneLockdown(context.Background(), zoneID, newZoneLockdown)
+	response, err := api.CreateZoneLockdown(context.Background(), cloudflare.ZoneIdentifier(zoneID), newZoneLockdown)
 	if err != nil {
 		log.Fatal(err)
 	}
diff --git a/lockdown_test.go b/lockdown_test.go
index b5d152b86..cd4b7c050 100644
--- a/lockdown_test.go
+++ b/lockdown_test.go
@@ -16,7 +16,17 @@ func TestCreateZoneLockdown(t *testing.T) {
 	setup()
 	defer teardown()
 
-	input := ZoneLockdown{
+	input := ZoneLockdownCreateParams{
+		Description: "Restrict access to these endpoints to requests from a known IP address",
+		URLs:        []string{"api.mysite.com/some/endpoint*"},
+		Configurations: []ZoneLockdownConfig{{
+			Target: "ip",
+			Value:  "198.51.100.4",
+		}},
+		Paused: false,
+	}
+
+	want := ZoneLockdown{
 		Description: "Restrict access to these endpoints to requests from a known IP address",
 		URLs:        []string{"api.mysite.com/some/endpoint*"},
 		Configurations: []ZoneLockdownConfig{{
@@ -32,7 +42,7 @@ func TestCreateZoneLockdown(t *testing.T) {
 		var v ZoneLockdown
 		err := json.NewDecoder(r.Body).Decode(&v)
 		require.NoError(t, err)
-		assert.Equal(t, input, v)
+		assert.Equal(t, want, v)
 
 		w.Header().Set("content-type", "application/json")
 		fmt.Fprint(w, `{
@@ -60,35 +70,35 @@ func TestCreateZoneLockdown(t *testing.T) {
 
 	mux.HandleFunc("/zones/"+testZoneID+"/firewall/lockdowns", handler)
 
-	actual, err := client.CreateZoneLockdown(context.Background(), testZoneID, input)
+	actual, err := client.CreateZoneLockdown(context.Background(), ZoneIdentifier(testZoneID), input)
 	require.NoError(t, err)
 
+	want.ID = "372e67954025e0ba6aaa6d586b9e0b59"
 	createdOn, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00Z")
 	modifiedOn, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00Z")
-	want := &ZoneLockdownResponse{
-		Result: ZoneLockdown{
-			ID:             "372e67954025e0ba6aaa6d586b9e0b59",
-			Description:    input.Description,
-			URLs:           input.URLs,
-			Configurations: input.Configurations,
-			Paused:         input.Paused,
-			CreatedOn:      &createdOn,
-			ModifiedOn:     &modifiedOn,
-		},
-		Response: Response{
-			Success:  true,
-			Errors:   []ResponseInfo{},
-			Messages: []ResponseInfo{},
-		},
-	}
+	want.CreatedOn = &createdOn
+	want.ModifiedOn = &modifiedOn
+
 	assert.Equal(t, want, actual)
 }
 
-func TestUpdateZoneLockdownt(t *testing.T) {
+func TestUpdateZoneLockdown(t *testing.T) {
 	setup()
 	defer teardown()
 
-	input := ZoneLockdown{
+	input := ZoneLockdownUpdateParams{
+		ID:          "372e67954025e0ba6aaa6d586b9e0b59",
+		Description: "Restrict access to these endpoints to requests from a known IP address",
+		URLs:        []string{"api.mysite.com/some/endpoint*"},
+		Configurations: []ZoneLockdownConfig{{
+			Target: "ip",
+			Value:  "198.51.100.4",
+		}},
+		Paused: false,
+	}
+
+	want := ZoneLockdown{
+		ID:          "372e67954025e0ba6aaa6d586b9e0b59",
 		Description: "Restrict access to these endpoints to requests from a known IP address",
 		URLs:        []string{"api.mysite.com/some/endpoint*"},
 		Configurations: []ZoneLockdownConfig{{
@@ -104,7 +114,7 @@ func TestUpdateZoneLockdownt(t *testing.T) {
 		var v ZoneLockdown
 		err := json.NewDecoder(r.Body).Decode(&v)
 		require.NoError(t, err)
-		assert.Equal(t, input, v)
+		assert.Equal(t, want, v)
 
 		w.Header().Set("content-type", "application/json")
 		fmt.Fprint(w, `{
@@ -130,31 +140,16 @@ func TestUpdateZoneLockdownt(t *testing.T) {
 		}`)
 	}
 
-	zoneLockdownID := "372e67954025e0ba6aaa6d586b9e0b59"
-
-	mux.HandleFunc("/zones/"+testZoneID+"/firewall/lockdowns/"+zoneLockdownID, handler)
+	mux.HandleFunc("/zones/"+testZoneID+"/firewall/lockdowns/372e67954025e0ba6aaa6d586b9e0b59", handler)
 
-	actual, err := client.UpdateZoneLockdown(context.Background(), testZoneID, zoneLockdownID, input)
+	actual, err := client.UpdateZoneLockdown(context.Background(), ZoneIdentifier(testZoneID), input)
 	require.NoError(t, err)
 
 	createdOn, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00Z")
 	modifiedOn, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00Z")
-	want := &ZoneLockdownResponse{
-		Result: ZoneLockdown{
-			ID:             zoneLockdownID,
-			Description:    input.Description,
-			URLs:           input.URLs,
-			Configurations: input.Configurations,
-			Paused:         input.Paused,
-			CreatedOn:      &createdOn,
-			ModifiedOn:     &modifiedOn,
-		},
-		Response: Response{
-			Success:  true,
-			Errors:   []ResponseInfo{},
-			Messages: []ResponseInfo{},
-		},
-	}
+	want.CreatedOn = &createdOn
+	want.ModifiedOn = &modifiedOn
+
 	assert.Equal(t, want, actual)
 }
 
@@ -180,18 +175,11 @@ func TestDeleteZoneLockdown(t *testing.T) {
 
 	mux.HandleFunc("/zones/"+testZoneID+"/firewall/lockdowns/"+zoneLockdownID, handler)
 
-	actual, err := client.DeleteZoneLockdown(context.Background(), testZoneID, zoneLockdownID)
+	actual, err := client.DeleteZoneLockdown(context.Background(), ZoneIdentifier(testZoneID), zoneLockdownID)
 	require.NoError(t, err)
 
-	want := &ZoneLockdownResponse{
-		Result: ZoneLockdown{
-			ID: zoneLockdownID,
-		},
-		Response: Response{
-			Success:  true,
-			Errors:   []ResponseInfo{},
-			Messages: []ResponseInfo{},
-		},
+	want := ZoneLockdown{
+		ID: zoneLockdownID,
 	}
 	assert.Equal(t, want, actual)
 }
@@ -231,29 +219,22 @@ func TestZoneLockdown(t *testing.T) {
 
 	mux.HandleFunc("/zones/"+testZoneID+"/firewall/lockdowns/"+zoneLockdownID, handler)
 
-	actual, err := client.ZoneLockdown(context.Background(), testZoneID, zoneLockdownID)
+	actual, err := client.ZoneLockdown(context.Background(), ZoneIdentifier(testZoneID), zoneLockdownID)
 	require.NoError(t, err)
 
 	createdOn, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00Z")
 	modifiedOn, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00Z")
-	want := &ZoneLockdownResponse{
-		Result: ZoneLockdown{
-			ID:          zoneLockdownID,
-			Description: "Restrict access to these endpoints to requests from a known IP address",
-			URLs:        []string{"api.mysite.com/some/endpoint*"},
-			Configurations: []ZoneLockdownConfig{{
-				Target: "ip",
-				Value:  "198.51.100.4",
-			}},
-			Paused:     false,
-			CreatedOn:  &createdOn,
-			ModifiedOn: &modifiedOn,
-		},
-		Response: Response{
-			Success:  true,
-			Errors:   []ResponseInfo{},
-			Messages: []ResponseInfo{},
-		},
+	want := ZoneLockdown{
+		ID:          zoneLockdownID,
+		Description: "Restrict access to these endpoints to requests from a known IP address",
+		URLs:        []string{"api.mysite.com/some/endpoint*"},
+		Configurations: []ZoneLockdownConfig{{
+			Target: "ip",
+			Value:  "198.51.100.4",
+		}},
+		Paused:     false,
+		CreatedOn:  &createdOn,
+		ModifiedOn: &modifiedOn,
 	}
 	assert.Equal(t, want, actual)
 }
@@ -264,7 +245,6 @@ func TestListZoneLockdowns(t *testing.T) {
 
 	handler := func(w http.ResponseWriter, r *http.Request) {
 		assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s")
-		assert.Equal(t, "1", r.URL.Query().Get("page"))
 
 		w.Header().Set("content-type", "application/json")
 		fmt.Fprint(w, `{
@@ -300,35 +280,23 @@ func TestListZoneLockdowns(t *testing.T) {
 
 	mux.HandleFunc("/zones/"+testZoneID+"/firewall/lockdowns", handler)
 
-	actual, err := client.ListZoneLockdowns(context.Background(), testZoneID, 1)
+	actual, _, err := client.ListZoneLockdowns(context.Background(), ZoneIdentifier(testZoneID), LockdownListParams{})
 	require.NoError(t, err)
 
 	createdOn, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00Z")
 	modifiedOn, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00Z")
-	want := &ZoneLockdownListResponse{
-		Result: []ZoneLockdown{{
-			ID:          "372e67954025e0ba6aaa6d586b9e0b59",
-			Description: "Restrict access to these endpoints to requests from a known IP address",
-			URLs:        []string{"api.mysite.com/some/endpoint*"},
-			Configurations: []ZoneLockdownConfig{{
-				Target: "ip",
-				Value:  "198.51.100.4",
-			}},
-			Paused:     false,
-			CreatedOn:  &createdOn,
-			ModifiedOn: &modifiedOn,
+	want := []ZoneLockdown{{
+		ID:          "372e67954025e0ba6aaa6d586b9e0b59",
+		Description: "Restrict access to these endpoints to requests from a known IP address",
+		URLs:        []string{"api.mysite.com/some/endpoint*"},
+		Configurations: []ZoneLockdownConfig{{
+			Target: "ip",
+			Value:  "198.51.100.4",
 		}},
-		Response: Response{
-			Success:  true,
-			Errors:   []ResponseInfo{},
-			Messages: []ResponseInfo{},
-		},
-		ResultInfo: ResultInfo{
-			Page:    1,
-			PerPage: 20,
-			Count:   1,
-			Total:   2000,
-		},
+		Paused:     false,
+		CreatedOn:  &createdOn,
+		ModifiedOn: &modifiedOn,
+	},
 	}
 	assert.Equal(t, want, actual)
 }
diff --git a/r2_bucket_test.go b/r2_bucket_test.go
deleted file mode 100644
index 227f36e9c..000000000
--- a/r2_bucket_test.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package cloudflare
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-)
-
-const testBucketName = "example-bucket"
-
-func TestR2_CreateBucket(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc(fmt.Sprintf("/accounts/%s/r2/buckets/%s", testAccountID, testBucketName), 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, `{
-  "success": true,
-  "errors": [],
-  "messages": [],
-  "result": {}
-}`)
-	})
-
-	err := client.CreateR2Bucket(context.Background(), AccountIdentifier(""), "")
-	if assert.Error(t, err) {
-		assert.Equal(t, ErrMissingAccountID, err)
-	}
-
-	err = client.CreateR2Bucket(context.Background(), AccountIdentifier(testAccountID), "")
-	if assert.Error(t, err) {
-		assert.Equal(t, ErrMissingBucketName, err)
-	}
-
-	err = client.CreateR2Bucket(context.Background(), AccountIdentifier(testAccountID), "example-bucket")
-	assert.NoError(t, err)
-}
-
-func TestR2_DeleteBucket(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc(fmt.Sprintf("/accounts/%s/r2/buckets/%s", testAccountID, testBucketName), 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, `{
-  "success": true,
-  "errors": [],
-  "messages": [],
-  "result": {}
-}`)
-	})
-
-	err := client.DeleteR2Bucket(context.Background(), AccountIdentifier(""), "")
-	if assert.Error(t, err) {
-		assert.Equal(t, ErrMissingAccountID, err)
-	}
-
-	err = client.DeleteR2Bucket(context.Background(), AccountIdentifier(testAccountID), "")
-	if assert.Error(t, err) {
-		assert.Equal(t, ErrMissingBucketName, err)
-	}
-
-	err = client.DeleteR2Bucket(context.Background(), AccountIdentifier(testAccountID), "example-bucket")
-	assert.NoError(t, err)
-}
diff --git a/workers_account_settings_test.go b/workers_account_settings_test.go
deleted file mode 100644
index 0bdd35fd3..000000000
--- a/workers_account_settings_test.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package cloudflare
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestWorkersAccountSettings_CreateAccountSettings(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc(fmt.Sprintf("/accounts/%s/workers/account-settings", testAccountID), 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, `{
-  "success": true,
-  "errors": [],
-  "messages": [],
-  "result": {
-    "default_usage_model": "unbound",
-	"green_compute": false
-  }
-}`)
-	})
-	res, err := client.CreateWorkersAccountSettings(context.Background(), AccountIdentifier(testAccountID), CreateWorkersAccountSettingsParameters{DefaultUsageModel: "unbound", GreenCompute: false})
-	want := WorkersAccountSettings{
-		DefaultUsageModel: "unbound",
-		GreenCompute:      false,
-	}
-
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-}
-
-func TestWorkersAccountSettings_GetAccountSettings(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc(fmt.Sprintf("/accounts/%s/workers/account-settings", testAccountID), 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, `{
-  "success": true,
-  "errors": [],
-  "messages": [],
-  "result": {
-    "default_usage_model": "unbound",
-	"green_compute": true
-  }
-}`)
-	})
-
-	res, err := client.WorkersAccountSettings(context.Background(), AccountIdentifier(testAccountID), WorkersAccountSettingsParameters{})
-	want := WorkersAccountSettings{
-		DefaultUsageModel: "unbound",
-		GreenCompute:      true,
-	}
-
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-}
diff --git a/workers_tail_test.go b/workers_tail_test.go
deleted file mode 100644
index ed57b4d80..000000000
--- a/workers_tail_test.go
+++ /dev/null
@@ -1,118 +0,0 @@
-package cloudflare
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-	"testing"
-	"time"
-
-	"github.com/stretchr/testify/assert"
-)
-
-const (
-	testScriptName = "this-is_my_script-01"
-	testTailID     = "03dc9f77817b488fb26c5861ec18f791"
-)
-
-func TestWorkersTail_StartWorkersTail(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc(fmt.Sprintf("/accounts/%s/workers/scripts/%s/tails", testAccountID, testScriptName), 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, `{
-  "success": true,
-  "errors": [],
-  "messages": [],
-  "result": {
-    "id": "03dc9f77817b488fb26c5861ec18f791",
-    "url": "wss://tail.developers.workers.dev/03dc9f77817b488fb26c5861ec18f791",
-    "expires_at": "2021-08-20T19:15:51Z"
-  }
-}`)
-	})
-
-	_, err := client.StartWorkersTail(context.Background(), AccountIdentifier(testAccountID), "")
-	if assert.Error(t, err) {
-		assert.Equal(t, ErrMissingScriptName, err)
-	}
-
-	res, err := client.StartWorkersTail(context.Background(), AccountIdentifier(testAccountID), testScriptName)
-	expiresAt, _ := time.Parse(time.RFC3339, "2021-08-20T19:15:51Z")
-	want := WorkersTail{
-		ID:        "03dc9f77817b488fb26c5861ec18f791",
-		URL:       "wss://tail.developers.workers.dev/03dc9f77817b488fb26c5861ec18f791",
-		ExpiresAt: &expiresAt,
-	}
-
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-}
-
-func TestWorkersTail_ListWorkersTail(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc(fmt.Sprintf("/accounts/%s/workers/scripts/%s/tails", testAccountID, testScriptName), 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, `{
-  "success": true,
-  "errors": [],
-  "messages": [],
-  "result": {
-    "id": "03dc9f77817b488fb26c5861ec18f791",
-    "url": "wss://tail.developers.workers.dev/03dc9f77817b488fb26c5861ec18f791",
-    "expires_at": "2021-08-20T19:15:51Z"
-  }
-}`)
-	})
-
-	_, err := client.ListWorkersTail(context.Background(), AccountIdentifier(testAccountID), ListWorkersTailParameters{ScriptName: ""})
-	if assert.Error(t, err) {
-		assert.Equal(t, ErrMissingScriptName, err)
-	}
-
-	res, err := client.ListWorkersTail(context.Background(), AccountIdentifier(testAccountID), ListWorkersTailParameters{ScriptName: testScriptName})
-	expiresAt, _ := time.Parse(time.RFC3339, "2021-08-20T19:15:51Z")
-	want := WorkersTail{
-		ID:        "03dc9f77817b488fb26c5861ec18f791",
-		URL:       "wss://tail.developers.workers.dev/03dc9f77817b488fb26c5861ec18f791",
-		ExpiresAt: &expiresAt,
-	}
-
-	if assert.NoError(t, err) {
-		assert.Equal(t, want, res)
-	}
-}
-
-func TestWorkersTail_DeleteWorkersTail(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc(fmt.Sprintf("/accounts/%s/workers/scripts/%s/tails/%s", testAccountID, testScriptName, testTailID), func(w http.ResponseWriter, r *http.Request) {
-		assert.Equal(t, http.MethodDelete, r.Method, "Expected method 'POST', got %s", r.Method)
-		w.Header().Set("content-type", "application/json")
-		fmt.Fprintf(w, `{
-  "success": true,
-  "errors": [],
-  "messages": [],
-}`)
-	})
-
-	err := client.DeleteWorkersTail(context.Background(), AccountIdentifier(testAccountID), "", "")
-	if assert.Error(t, err) {
-		assert.Equal(t, ErrMissingScriptName, err)
-	}
-
-	err = client.DeleteWorkersTail(context.Background(), AccountIdentifier(testAccountID), testScriptName, "")
-	if assert.Error(t, err) {
-		assert.Equal(t, ErrMissingTailID, err)
-	}
-
-	err = client.DeleteWorkersTail(context.Background(), AccountIdentifier(testAccountID), testScriptName, testTailID)
-	assert.NoError(t, err)
-}