Repository: cloudflare/cloudflare-go
Pull request #1428 (2023-10-26 17:55:13)

ip_access_rules: Add ListIPAccessRules api call
Adds List IP Access Rules API Call [docs](https://developers.cloudflare.com/api/operations/ip-access-rules-for-a-zone-list-ip-access-rules)

## Description

Provides access to an api endpoint not provided by this library currently

## Has your change been tested?

Yes, tested basic call

## 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.
- [no] My change requires a change to the documentation.
- [n/a] 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 in [cloudflare/api-schemas](https://github.com/cloudflare/api-schemas) 
      and relies on 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.
dkoston: fixed the lint issue
jacobbednarz: your fork doesn't allow maintainers to send changes so here is a patch instead. `IP` should be uppercase in it's usage. here is a patch for it - https://gist.github.com/jacobbednarz/57245ff4c77be94127294a761fd9d00f

Patch:
diff --git a/.changelog/1428.txt b/.changelog/1428.txt
new file mode 100644
index 000000000..f0c3236ea
--- /dev/null
+++ b/.changelog/1428.txt
@@ -0,0 +1,3 @@
+```release-note:enhancement
+ip_access_rules: Add ListIPAccessRules() to list IP Access Rules
+```
diff --git a/ip_access_rules.go b/ip_access_rules.go
new file mode 100644
index 000000000..58d29cd08
--- /dev/null
+++ b/ip_access_rules.go
@@ -0,0 +1,89 @@
+package cloudflare
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+
+	"github.com/goccy/go-json"
+)
+
+type ListIPAccessRulesOrderOption string
+type ListIPAccessRulesMatchOption string
+type IPAccessRulesModeOption string
+
+const (
+	IPAccessRulesConfigurationTarget  ListIPAccessRulesOrderOption = "configuration.target"
+	IPAccessRulesConfigurationValue   ListIPAccessRulesOrderOption = "configuration.value"
+	IPAccessRulesMatchOptionAll       ListIPAccessRulesMatchOption = "all"
+	IPAccessRulesMatchOptionAny       ListIPAccessRulesMatchOption = "any"
+	IPAccessRulesModeBlock            IPAccessRulesModeOption      = "block"
+	IPAccessRulesModeChallenge        IPAccessRulesModeOption      = "challenge"
+	IPAccessRulesModeJsChallenge      IPAccessRulesModeOption      = "js_challenge"
+	IPAccessRulesModeManagedChallenge IPAccessRulesModeOption      = "managed_challenge"
+	IPAccessRulesModeWhitelist        IPAccessRulesModeOption      = "whitelist"
+)
+
+type ListIPAccessRulesFilters struct {
+	Configuration IPAccessRuleConfiguration    `json:"configuration,omitempty"`
+	Match         ListIPAccessRulesMatchOption `json:"match,omitempty"`
+	Mode          IPAccessRulesModeOption      `json:"mode,omitempty"`
+	Notes         string                       `json:"notes,omitempty"`
+}
+
+type ListIPAccessRulesParams struct {
+	Direction string                       `url:"direction,omitempty"`
+	Filters   ListIPAccessRulesFilters     `url:"filters,omitempty"`
+	Order     ListIPAccessRulesOrderOption `url:"order,omitempty"`
+	PaginationOptions
+}
+
+type IPAccessRuleConfiguration struct {
+	Target string `json:"target,omitempty"`
+	Value  string `json:"value,omitempty"`
+}
+
+type IPAccessRule struct {
+	AllowedModes  []IPAccessRulesModeOption `json:"allowed_modes"`
+	Configuration IPAccessRuleConfiguration `json:"configuration"`
+	CreatedOn     string                    `json:"created_on"`
+	ID            string                    `json:"id"`
+	Mode          IPAccessRulesModeOption   `json:"mode"`
+	ModifiedOn    string                    `json:"modified_on"`
+	Notes         string                    `json:"notes"`
+}
+
+type ListIPAccessRulesResponse struct {
+	Result     []IPAccessRule `json:"result"`
+	ResultInfo `json:"result_info"`
+	Response
+}
+
+// ListIPAccessRules fetches IP Access rules of a zone/user/account. You can
+// filter the results using several optional parameters.
+//
+// API references:
+//   - https://developers.cloudflare.com/api/operations/ip-access-rules-for-a-user-list-ip-access-rules
+//   - https://developers.cloudflare.com/api/operations/ip-access-rules-for-a-zone-list-ip-access-rules
+//   - https://developers.cloudflare.com/api/operations/ip-access-rules-for-an-account-list-ip-access-rules
+func (api *API) ListIPAccessRules(ctx context.Context, rc *ResourceContainer, params ListIPAccessRulesParams) ([]IPAccessRule, *ResultInfo, error) {
+	if rc.Identifier == "" {
+		return []IPAccessRule{}, &ResultInfo{}, ErrMissingResourceIdentifier
+	}
+
+	uri := buildURI(fmt.Sprintf("/%s/%s/firewall/access_rules/rules", rc.Level, rc.Identifier), params)
+
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return []IPAccessRule{}, &ResultInfo{}, err
+	}
+
+	result := ListIPAccessRulesResponse{}
+
+	err = json.Unmarshal(res, &result)
+	if err != nil {
+		return []IPAccessRule{}, &ResultInfo{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return result.Result, &result.ResultInfo, nil
+}


Test patch:
diff --git a/ip_access_rules_test.go b/ip_access_rules_test.go
new file mode 100644
index 000000000..4eba12443
--- /dev/null
+++ b/ip_access_rules_test.go
@@ -0,0 +1,311 @@
+package cloudflare
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestListIPAccessRules(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, `{
+			"result":[
+						{
+							"allowed_modes": [
+								"whitelist",
+								"block",
+								"challenge",
+								"js_challenge",
+								"managed_challenge"
+							],
+							"configuration": {
+								"target": "ip",
+								"value": "198.51.100.1"
+							},
+							"created_on": "2014-01-01T05:20:00.12345Z",
+							"id": "f2d427378e7542acb295380d352e2ebd",
+							"mode": "whitelist",
+							"modified_on": "2014-01-01T05:20:00.12345Z",
+							"notes": "Whitelisting this IP."
+						},
+						{
+							"allowed_modes": [
+								"whitelist",
+								"block",
+								"challenge",
+								"js_challenge",
+								"managed_challenge"
+							],
+							"configuration": {
+								"target": "ip",
+								"value": "198.51.100.2"
+							},
+							"created_on": "2014-01-01T05:20:00.12345Z",
+							"id": "92f17202ed8bd63d69a66b86a49a8f6b",
+							"mode": "block",
+							"modified_on": "2014-01-01T05:20:00.12345Z",
+							"notes": "This rule is enabled because of an event that occurred on date X."
+						},
+						{
+							"allowed_modes": [
+								"whitelist",
+								"block",
+								"challenge",
+								"js_challenge",
+								"managed_challenge"
+							],
+							"configuration": {
+								"target": "ip",
+								"value": "198.51.100.3"
+							},
+							"created_on": "2014-01-01T05:20:00.12345Z",
+							"id": "4ae338944d6143378c3cf05a7c77d983",
+							"mode": "challenge",
+							"modified_on": "2014-01-01T05:20:00.12345Z",
+							"notes": "This rule is enabled because of an event that occurred on date Y."
+						},
+						{
+							"allowed_modes": [
+								"whitelist",
+								"block",
+								"challenge",
+								"js_challenge",
+								"managed_challenge"
+							],
+							"configuration": {
+								"target": "ip",
+								"value": "198.51.100.4"
+							},
+							"created_on": "2014-01-01T05:20:00.12345Z",
+							"id": "52161eb6af4241bb9d4b32394be72fdf",
+							"mode": "js_challenge",
+							"modified_on": "2014-01-01T05:20:00.12345Z",
+							"notes": "This rule is enabled because of an event that occurred on date Z."
+						},
+						{
+							"allowed_modes": [
+								"whitelist",
+								"block",
+								"challenge",
+								"js_challenge",
+								"managed_challenge"
+							],
+							"configuration": {
+								"target": "ip",
+								"value": "198.51.100.5"
+							},
+							"created_on": "2014-01-01T05:20:00.12345Z",
+							"id": "cbf4b7a5a2a24e59a03044d6d44ceb09",
+							"mode": "managed_challenge",
+							"modified_on": "2014-01-01T05:20:00.12345Z",
+							"notes": "This rule is enabled because we like the challenge page."
+						}
+			],
+			"success":true,
+			"errors":null,
+			"messages":null,
+			"result_info":{
+				"page":1,
+				"per_page":25,
+				"count":5,
+				"total_count":5
+			}
+		}
+		`)
+	}
+
+	mux.HandleFunc("/zones/d56084adb405e0b7e32c52321bf07be6/firewall/access_rules/rules", handler)
+	want := []IPAccessRule{
+		{
+			AllowedModes: []IPAccessRulesModeOption{
+				IPAccessRulesModeWhitelist,
+				IPAccessRulesModeBlock,
+				IPAccessRulesModeChallenge,
+				IPAccessRulesModeJsChallenge,
+				IPAccessRulesModeManagedChallenge,
+			},
+			ID: "f2d427378e7542acb295380d352e2ebd",
+			Configuration: IPAccessRuleConfiguration{
+				Target: "ip",
+				Value:  "198.51.100.1",
+			},
+			CreatedOn:  "2014-01-01T05:20:00.12345Z",
+			Mode:       "whitelist",
+			ModifiedOn: "2014-01-01T05:20:00.12345Z",
+			Notes:      "Whitelisting this IP.",
+		},
+		{
+			AllowedModes: []IPAccessRulesModeOption{
+				IPAccessRulesModeWhitelist,
+				IPAccessRulesModeBlock,
+				IPAccessRulesModeChallenge,
+				IPAccessRulesModeJsChallenge,
+				IPAccessRulesModeManagedChallenge,
+			},
+			ID: "92f17202ed8bd63d69a66b86a49a8f6b",
+			Configuration: IPAccessRuleConfiguration{
+				Target: "ip",
+				Value:  "198.51.100.2",
+			},
+			CreatedOn:  "2014-01-01T05:20:00.12345Z",
+			Mode:       "block",
+			ModifiedOn: "2014-01-01T05:20:00.12345Z",
+			Notes:      "This rule is enabled because of an event that occurred on date X.",
+		},
+		{
+			AllowedModes: []IPAccessRulesModeOption{
+				IPAccessRulesModeWhitelist,
+				IPAccessRulesModeBlock,
+				IPAccessRulesModeChallenge,
+				IPAccessRulesModeJsChallenge,
+				IPAccessRulesModeManagedChallenge,
+			},
+			ID: "4ae338944d6143378c3cf05a7c77d983",
+			Configuration: IPAccessRuleConfiguration{
+				Target: "ip",
+				Value:  "198.51.100.3",
+			},
+			CreatedOn:  "2014-01-01T05:20:00.12345Z",
+			Mode:       "challenge",
+			ModifiedOn: "2014-01-01T05:20:00.12345Z",
+			Notes:      "This rule is enabled because of an event that occurred on date Y.",
+		},
+		{
+			AllowedModes: []IPAccessRulesModeOption{
+				IPAccessRulesModeWhitelist,
+				IPAccessRulesModeBlock,
+				IPAccessRulesModeChallenge,
+				IPAccessRulesModeJsChallenge,
+				IPAccessRulesModeManagedChallenge,
+			},
+			ID: "52161eb6af4241bb9d4b32394be72fdf",
+			Configuration: IPAccessRuleConfiguration{
+				Target: "ip",
+				Value:  "198.51.100.4",
+			},
+			CreatedOn:  "2014-01-01T05:20:00.12345Z",
+			Mode:       "js_challenge",
+			ModifiedOn: "2014-01-01T05:20:00.12345Z",
+			Notes:      "This rule is enabled because of an event that occurred on date Z.",
+		},
+		{
+			AllowedModes: []IPAccessRulesModeOption{
+				IPAccessRulesModeWhitelist,
+				IPAccessRulesModeBlock,
+				IPAccessRulesModeChallenge,
+				IPAccessRulesModeJsChallenge,
+				IPAccessRulesModeManagedChallenge,
+			},
+			ID: "cbf4b7a5a2a24e59a03044d6d44ceb09",
+			Configuration: IPAccessRuleConfiguration{
+				Target: "ip",
+				Value:  "198.51.100.5",
+			},
+			CreatedOn:  "2014-01-01T05:20:00.12345Z",
+			Mode:       "managed_challenge",
+			ModifiedOn: "2014-01-01T05:20:00.12345Z",
+			Notes:      "This rule is enabled because we like the challenge page.",
+		},
+	}
+
+	actual, _, err := client.ListIPAccessRules(context.Background(),
+		ZoneIdentifier("d56084adb405e0b7e32c52321bf07be6"), ListIPAccessRulesParams{})
+
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, actual)
+	}
+}
+
+func TestListIPAccessRulesWithParams(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, `{
+			"result":[
+						{
+							"allowed_modes": [
+								"whitelist",
+								"block",
+								"challenge",
+								"js_challenge",
+								"managed_challenge"
+							],
+							"configuration": {
+								"target": "ip",
+								"value": "198.51.100.5"
+							},
+							"created_on": "2014-01-01T05:20:00.12345Z",
+							"id": "cbf4b7a5a2a24e59a03044d6d44ceb09",
+							"mode": "managed_challenge",
+							"modified_on": "2014-01-01T05:20:00.12345Z",
+							"notes": "This rule is enabled because we like the challenge page."
+						}
+			],
+			"success":true,
+			"errors":null,
+			"messages":null,
+			"result_info":{
+				"page":1,
+				"per_page":100,
+				"count":1,
+				"total_count":1
+			}
+		}
+		`)
+	}
+
+	mux.HandleFunc("/zones/d56084adb405e0b7e32c52321bf07be6/firewall/access_rules/rules", handler)
+	want := []IPAccessRule{
+		{
+			AllowedModes: []IPAccessRulesModeOption{
+				IPAccessRulesModeWhitelist,
+				IPAccessRulesModeBlock,
+				IPAccessRulesModeChallenge,
+				IPAccessRulesModeJsChallenge,
+				IPAccessRulesModeManagedChallenge,
+			},
+			ID: "cbf4b7a5a2a24e59a03044d6d44ceb09",
+			Configuration: IPAccessRuleConfiguration{
+				Target: "ip",
+				Value:  "198.51.100.5",
+			},
+			CreatedOn:  "2014-01-01T05:20:00.12345Z",
+			Mode:       "managed_challenge",
+			ModifiedOn: "2014-01-01T05:20:00.12345Z",
+			Notes:      "This rule is enabled because we like the challenge page.",
+		},
+	}
+
+	actual, _, err := client.ListIPAccessRules(context.Background(),
+		ZoneIdentifier("d56084adb405e0b7e32c52321bf07be6"), ListIPAccessRulesParams{
+			Direction: "asc",
+			Filters: ListIPAccessRulesFilters{
+				Configuration: IPAccessRuleConfiguration{
+					Target: "ip",
+				},
+				Match: "any",
+				Mode:  "manage_challenge",
+				Notes: "This rule is enabled because we like the challenge page.",
+			},
+			Order: IPAccessRulesConfigurationTarget,
+			PaginationOptions: PaginationOptions{
+				Page:    1,
+				PerPage: 100,
+			},
+		})
+
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, actual)
+	}
+}