Repository: cloudflare/cloudflare-go
Pull request #1276 (2023-05-04 21:07:17)

WR-1279 add waiting room zone-level settings
## Description

This adds support for the new Waiting Room zone-level settings, which are used to enable search engine crawler bypass.

## Has your change been tested?

Tests were added, mirroring the other tests

## 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.
- [x] My change requires a change to the documentation.
- [x] 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.
codecov-commenter: ## [Codecov](https://app.codecov.io/gh/cloudflare/cloudflare-go/pull/1276?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare) Report
> Merging [#1276](https://app.codecov.io/gh/cloudflare/cloudflare-go/pull/1276?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare) (c8fac53) into [master](https://app.codecov.io/gh/cloudflare/cloudflare-go/commit/0c3db86282352878154a63c9bc67764dfded0780?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare) (0c3db86) will **decrease** coverage by `0.14%`.
> The diff coverage is `41.57%`.

```diff
@@            Coverage Diff             @@
##           master    #1276      +/-   ##
==========================================
- Coverage   49.23%   49.10%   -0.14%     
==========================================
  Files         131      133       +2     
  Lines       12665    13023     +358     
==========================================
+ Hits         6236     6395     +159     
- Misses       4988     5137     +149     
- Partials     1441     1491      +50     
```


| [Impacted Files](https://app.codecov.io/gh/cloudflare/cloudflare-go/pull/1276?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare) | Coverage Δ | |
|---|---|---|
| [access\_application.go](https://app.codecov.io/gh/cloudflare/cloudflare-go/pull/1276?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-YWNjZXNzX2FwcGxpY2F0aW9uLmdv) | `76.52% <ø> (ø)` | |
| [access\_organization.go](https://app.codecov.io/gh/cloudflare/cloudflare-go/pull/1276?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-YWNjZXNzX29yZ2FuaXphdGlvbi5nbw==) | `64.70% <ø> (ø)` | |
| [access\_policy.go](https://app.codecov.io/gh/cloudflare/cloudflare-go/pull/1276?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-YWNjZXNzX3BvbGljeS5nbw==) | `76.31% <ø> (ø)` | |
| [certificate\_packs.go](https://app.codecov.io/gh/cloudflare/cloudflare-go/pull/1276?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-Y2VydGlmaWNhdGVfcGFja3MuZ28=) | `50.90% <ø> (ø)` | |
| [cloudflare.go](https://app.codecov.io/gh/cloudflare/cloudflare-go/pull/1276?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-Y2xvdWRmbGFyZS5nbw==) | `67.28% <ø> (ø)` | |
| [device\_posture\_rule.go](https://app.codecov.io/gh/cloudflare/cloudflare-go/pull/1276?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-ZGV2aWNlX3Bvc3R1cmVfcnVsZS5nbw==) | `61.80% <ø> (ø)` | |
| [dns.go](https://app.codecov.io/gh/cloudflare/cloudflare-go/pull/1276?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-ZG5zLmdv) | `45.05% <0.00%> (-28.16%)` | :arrow_down: |
| [dns\_firewall.go](https://app.codecov.io/gh/cloudflare/cloudflare-go/pull/1276?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-ZG5zX2ZpcmV3YWxsLmdv) | `21.42% <ø> (ø)` | |
| [errors.go](https://app.codecov.io/gh/cloudflare/cloudflare-go/pull/1276?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-ZXJyb3JzLmdv) | `40.78% <ø> (ø)` | |
| [logpush.go](https://app.codecov.io/gh/cloudflare/cloudflare-go/pull/1276?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-bG9ncHVzaC5nbw==) | `52.10% <ø> (ø)` | |
| ... and [7 more](https://app.codecov.io/gh/cloudflare/cloudflare-go/pull/1276?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare) | |


jacobbednarz: thanks for the PR @piperswe 🙇 the changes themselves look fine (minor nitpicks inline) however, for the method signatures, i'd recommend swapping to the method signature conventions outlined in the [experimental documentation](https://github.com/cloudflare/cloudflare-go/blob/master/docs/experimental.md) which allows more flexibility but stronger type guarantees for the future. if you're looking for some prior art, you can checkout #1270 which was a new resource added within the last couple of weeks that follows these conventions. i've also commented on one of the methods to help you get started.

Patch:
diff --git a/.changelog/1275.txt b/.changelog/1275.txt
deleted file mode 100644
index 718b80065..000000000
--- a/.changelog/1275.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-```release-note:bug
-rulesets: allow `PreserveQueryString` to be nullable
-```
diff --git a/.changelog/1276.txt b/.changelog/1276.txt
new file mode 100644
index 000000000..713898c8d
--- /dev/null
+++ b/.changelog/1276.txt
@@ -0,0 +1,3 @@
+```release-note:enhancement
+waiting_room: add support for zone-level settings
+```
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 37010ebae..ca736cdde 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,10 +8,6 @@ ENHANCEMENTS:
 
 * device_posture_rule: add input fields tanium, intune and kolide ([#1268](https://github.com/cloudflare/cloudflare-go/issues/1268))
 
-BUG FIXES:
-
-* rulesets: allow `PreserveQueryString` to be nullable ([#1275](https://github.com/cloudflare/cloudflare-go/issues/1275))
-
 DEPENDENCIES:
 
 * deps: bumps github.com/urfave/cli/v2 from 2.25.1 to 2.25.3 ([#1274](https://github.com/cloudflare/cloudflare-go/issues/1274))
diff --git a/rulesets.go b/rulesets.go
index 72a7a4f3b..8110d0206 100644
--- a/rulesets.go
+++ b/rulesets.go
@@ -275,7 +275,7 @@ type RulesetRuleActionParametersAutoMinify struct {
 type RulesetRuleActionParametersFromValue struct {
 	StatusCode          uint16                               `json:"status_code,omitempty"`
 	TargetURL           RulesetRuleActionParametersTargetURL `json:"target_url"`
-	PreserveQueryString *bool                                `json:"preserve_query_string,omitempty"`
+	PreserveQueryString bool                                 `json:"preserve_query_string,omitempty"`
 }
 
 type RulesetRuleActionParametersTargetURL struct {
diff --git a/waiting_room.go b/waiting_room.go
index a60b5028e..737e49dc4 100644
--- a/waiting_room.go
+++ b/waiting_room.go
@@ -76,6 +76,12 @@ type WaitingRoomRule struct {
 	Enabled     *bool      `json:"enabled"`
 }
 
+// WaitingRoomSettings describes zone-level waiting room settings.
+type WaitingRoomSettings struct {
+	// Whether to allow verified search engine crawlers to bypass all waiting rooms on this zone
+	SearchEngineCrawlerBypass bool `json:"search_engine_crawler_bypass"`
+}
+
 // WaitingRoomPagePreviewURL describes a WaitingRoomPagePreviewURL object.
 type WaitingRoomPagePreviewURL struct {
 	PreviewURL string `json:"preview_url"`
@@ -98,6 +104,12 @@ type WaitingRoomsResponse struct {
 	Result []WaitingRoom `json:"result"`
 }
 
+// WaitingRoomSettingsResponse is the API response, containing zone-level Waiting Room settings.
+type WaitingRoomSettingsResponse struct {
+	Response
+	Result WaitingRoomSettings `json:"result"`
+}
+
 // WaitingRoomStatusResponse is the API response, containing the status of a waiting room.
 type WaitingRoomStatusResponse struct {
 	Response
@@ -532,3 +544,76 @@ func (api *API) DeleteWaitingRoomRule(ctx context.Context, rc *ResourceContainer
 
 	return r.Result, nil
 }
+
+// GetWaitingRoomSettings fetches the Waiting Room zone-level settings for a zone.
+//
+// API reference: https://api.cloudflare.com/#waiting-room-get-zone-settings
+func (api *API) GetWaitingRoomSettings(ctx context.Context, rc *ResourceContainer) (WaitingRoomSettings, error) {
+	if rc.Level != ZoneRouteLevel {
+		return WaitingRoomSettings{}, fmt.Errorf(errInvalidResourceContainerAccess, rc.Level)
+	}
+
+	uri := fmt.Sprintf("/zones/%s/waiting_rooms/settings", rc.Identifier)
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return WaitingRoomSettings{}, err
+	}
+	var r WaitingRoomSettingsResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return WaitingRoomSettings{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+	return r.Result, nil
+}
+
+type PatchWaitingRoomSettingsParams struct {
+	SearchEngineCrawlerBypass *bool `json:"search_engine_crawler_bypass,omitempty"`
+}
+
+// PatchWaitingRoomSettings lets you change individual zone-level Waiting Room settings. This is
+// in contrast to UpdateWaitingRoomSettings which replaces all settings.
+//
+// API reference: https://api.cloudflare.com/#waiting-room-patch-zone-settings
+func (api *API) PatchWaitingRoomSettings(ctx context.Context, rc *ResourceContainer, params PatchWaitingRoomSettingsParams) (WaitingRoomSettings, error) {
+	if rc.Level != ZoneRouteLevel {
+		return WaitingRoomSettings{}, fmt.Errorf(errInvalidResourceContainerAccess, rc.Level)
+	}
+
+	uri := fmt.Sprintf("/zones/%s/waiting_rooms/settings", rc.Identifier)
+	res, err := api.makeRequestContext(ctx, http.MethodPatch, uri, params)
+	if err != nil {
+		return WaitingRoomSettings{}, err
+	}
+	var r WaitingRoomSettingsResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return WaitingRoomSettings{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+	return r.Result, nil
+}
+
+type UpdateWaitingRoomSettingsParams struct {
+	SearchEngineCrawlerBypass *bool `json:"search_engine_crawler_bypass,omitempty"`
+}
+
+// UpdateWaitingRoomSettings lets you replace all zone-level Waiting Room settings. This is in contrast to
+// PatchWaitingRoomSettings which lets you change individual settings.
+//
+// API reference: https://api.cloudflare.com/#waiting-room-update-zone-settings
+func (api *API) UpdateWaitingRoomSettings(ctx context.Context, rc *ResourceContainer, params UpdateWaitingRoomSettingsParams) (WaitingRoomSettings, error) {
+	if rc.Level != ZoneRouteLevel {
+		return WaitingRoomSettings{}, fmt.Errorf(errInvalidResourceContainerAccess, rc.Level)
+	}
+
+	uri := fmt.Sprintf("/zones/%s/waiting_rooms/settings", rc.Identifier)
+	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params)
+	if err != nil {
+		return WaitingRoomSettings{}, err
+	}
+	var r WaitingRoomSettingsResponse
+	err = json.Unmarshal(res, &r)
+	if err != nil {
+		return WaitingRoomSettings{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+	return r.Result, nil
+}


Test patch:
diff --git a/rulesets_test.go b/rulesets_test.go
index 52e9ed88c..c126c7239 100644
--- a/rulesets_test.go
+++ b/rulesets_test.go
@@ -523,7 +523,7 @@ func TestGetRuleset_RedirectFromValue(t *testing.T) {
 				TargetURL: RulesetRuleActionParametersTargetURL{
 					Value: "some_host.com",
 				},
-				PreserveQueryString: BoolPtr(true),
+				PreserveQueryString: true,
 			},
 		},
 		Description: "Set dynamic redirect from value",
diff --git a/waiting_room_test.go b/waiting_room_test.go
index 902510305..057789906 100644
--- a/waiting_room_test.go
+++ b/waiting_room_test.go
@@ -97,6 +97,12 @@ var waitingRoomPagePreviewJSON = `
     }
     `
 
+var waitingRoomSettingsJSON = `
+    {
+      "search_engine_crawler_bypass": true
+    }
+   `
+
 var waitingRoom = WaitingRoom{
 	ID:                         waitingRoomID,
 	CreatedOn:                  testTimestampWaitingRoom,
@@ -160,6 +166,18 @@ var waitingRoomRule = WaitingRoomRule{
 	LastUpdated: &testTimestampWaitingRoom,
 }
 
+var waitingRoomSettings = WaitingRoomSettings{
+	SearchEngineCrawlerBypass: true,
+}
+
+var waitingRoomSettingsUpdate = UpdateWaitingRoomSettingsParams{
+	SearchEngineCrawlerBypass: BoolPtr(true),
+}
+
+var waitingRoomSettingsPatch = PatchWaitingRoomSettingsParams{
+	SearchEngineCrawlerBypass: BoolPtr(true),
+}
+
 func TestListWaitingRooms(t *testing.T) {
 	setup()
 	defer teardown()
@@ -785,3 +803,78 @@ func TestReplaceWaitingRoomRules(t *testing.T) {
 		assert.Equal(t, want, actual)
 	}
 }
+
+func TestWaitingRoomSettings(t *testing.T) {
+	setup()
+	defer teardown()
+
+	handler := 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": %s
+			}
+		`, waitingRoomSettingsJSON)
+	}
+
+	mux.HandleFunc("/zones/"+testZoneID+"/waiting_rooms/settings", handler)
+	want := waitingRoomSettings
+
+	actual, err := client.GetWaitingRoomSettings(context.Background(), ZoneIdentifier(testZoneID))
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, actual)
+	}
+}
+
+func TestUpdateWaitingRoomSettings(t *testing.T) {
+	setup()
+	defer teardown()
+
+	handler := 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": %s
+			}
+		`, waitingRoomSettingsJSON)
+	}
+
+	mux.HandleFunc("/zones/"+testZoneID+"/waiting_rooms/settings", handler)
+	want := waitingRoomSettings
+
+	actual, err := client.UpdateWaitingRoomSettings(context.Background(), ZoneIdentifier(testZoneID), waitingRoomSettingsUpdate)
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, actual)
+	}
+}
+
+func TestChangeWaitingRoomSettings(t *testing.T) {
+	setup()
+	defer teardown()
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodPatch, r.Method, "Expected method 'PATCH', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprintf(w, `{
+			  "success": true,
+			  "errors": [],
+			  "messages": [],
+			  "result": %s
+			}
+		`, waitingRoomSettingsJSON)
+	}
+
+	mux.HandleFunc("/zones/"+testZoneID+"/waiting_rooms/settings", handler)
+	want := waitingRoomSettings
+
+	actual, err := client.PatchWaitingRoomSettings(context.Background(), ZoneIdentifier(testZoneID), waitingRoomSettingsPatch)
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, actual)
+	}
+}