Repository: cloudflare/cloudflare-go (go)

Issue:
support teams Local Domain Fallback endpoints
### Current cloudflare-go version

0.28.0

### Description

We'd like to manage the Teams Local Domain Fallback settings via the terraform provider, but need support here first.

* https://api.cloudflare.com/#devices-set-local-domain-fallback-list

### Use cases

This is required for terraform support.

### Potential cloudflare-go usage

```go
func (api *API) ListLocalDomainFallback(ctx context.Context, accountID string) ([]LocalDomains, error) {
}

func (api *API) UpdateLocalDomainFallback(ctx context.Context, accountID string, domains []LocalDomains) ([]LocalDomains, error) {
}
```


### References

https://github.com/cloudflare/terraform-provider-cloudflare/issues/1352

Pull request description:
Adding support for fallback domains
Adds support for fallback domains: https://api.cloudflare.com/#devices-get-local-domain-fallback-list

closes #759

## Has your change been tested?

Unit tests included. Will do manual testing with cloudflare-terraform

## 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/


Patch:
diff --git a/fallback_domain.go b/fallback_domain.go
new file mode 100644
index 000000000..e15df9d06
--- /dev/null
+++ b/fallback_domain.go
@@ -0,0 +1,64 @@
+package cloudflare
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/pkg/errors"
+)
+
+// FallbackDomainResponse represents the response from the get fallback
+// domain endpoints.
+type FallbackDomainResponse struct {
+	Response
+	Result []FallbackDomain `json:"result"`
+}
+
+// FallbackDomain represents the individual domain struct.
+type FallbackDomain struct {
+	Suffix       string `json:"suffix,omitempty"`
+	Description  string `json:"description,omitempty"`
+	DNSServer    string `json:"dns_server,omitempty"`
+}
+
+// ListFallbackDomains returns all fallback domains within an account.
+//
+// API reference: https://api.cloudflare.com/#devices-get-local-domain-fallback-list
+func (api *API) ListFallbackDomains(ctx context.Context, accountID string) ([]FallbackDomain, error) {
+	uri := fmt.Sprintf("/%s/%s/devices/policy/fallback_domains", AccountRouteRoot, accountID)
+
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return []FallbackDomain{}, err
+	}
+
+	var fallbackDomainResponse FallbackDomainResponse
+	err = json.Unmarshal(res, &fallbackDomainResponse)
+	if err != nil {
+		return []FallbackDomain{}, errors.Wrap(err, errUnmarshalError)
+	}
+
+	return fallbackDomainResponse.Result, nil
+}
+
+// UpdateFallbackDomain updates the existing fallback domain policy.
+//
+// API reference: https://api.cloudflare.com/#devices-set-local-domain-fallback-list
+func (api *API) UpdateFallbackDomain(ctx context.Context, accountID string, domains []FallbackDomain) ([]FallbackDomain, error) {
+	uri := fmt.Sprintf("/%s/%s/devices/policy/fallback_domains", AccountRouteRoot, accountID)
+
+	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, domains)
+	if err != nil {
+		return []FallbackDomain{}, err
+	}
+
+	var fallbackDomainResponse FallbackDomainResponse
+	err = json.Unmarshal(res, &fallbackDomainResponse)
+	if err != nil {
+		return []FallbackDomain{}, errors.Wrap(err, errUnmarshalError)
+	}
+
+	return fallbackDomainResponse.Result, nil
+}
\ No newline at end of file


Test patch:
diff --git a/fallback_domain_test.go b/fallback_domain_test.go
new file mode 100644
index 000000000..7817a2dfa
--- /dev/null
+++ b/fallback_domain_test.go
@@ -0,0 +1,140 @@
+package cloudflare
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestListFallbackDomain(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": [
+        {
+          "suffix": "example.com",
+          "description": "Domain bypass for local development"
+       }
+      ]
+    }
+    `)
+	}
+
+	want := []FallbackDomain{{
+		Suffix:        "example.com",
+		Description: "Domain bypass for local development",
+	}}
+
+	mux.HandleFunc("/accounts/"+testAccountID+"/devices/policy/fallback_domains", handler)
+
+	actual, err := client.ListFallbackDomains(context.Background(), testAccountID)
+
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, actual)
+	}
+}
+
+func TestFallbackDomainDNSServer(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": [
+        {
+          "suffix": "example.com",
+          "description": "Domain bypass for local development",
+					"dns_server": "['192.168.0.1', '10.1.1.1']"
+       }
+      ]
+    }
+    `)
+	}
+
+	want := []FallbackDomain{{
+		Suffix:        "example.com",
+		Description:   "Domain bypass for local development",
+		DNSServer:     "['192.168.0.1', '10.1.1.1']",
+	}}
+
+	mux.HandleFunc("/accounts/"+testAccountID+"/devices/policy/fallback_domains", handler)
+
+	actual, err := client.ListFallbackDomains(context.Background(), testAccountID)
+
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, actual)
+	}
+}
+
+func TestUpdateFallbackDomain(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": [
+        {
+          "suffix": "example_one.com",
+          "description": "example one",
+					"dns_server": "['192.168.0.1', '10.1.1.1']"
+       },
+       {
+          "suffix": "example_two.com",
+          "description": "example two"
+       },
+       {
+          "suffix": "example_three.com",
+          "description": "example three"
+       }
+      ]
+    }
+    `)
+	}
+
+	domains := []FallbackDomain{
+		{
+			Suffix:      "example_one.com",
+			Description: "example one",
+			DNSServer:   "['192.168.0.1', '10.1.1.1']",
+		},
+		{
+			Suffix:      "example_two.com",
+			Description: "example two",
+		},
+		{
+			Suffix:      "example_three.com",
+			Description: "example three",
+		},
+	}
+
+	mux.HandleFunc("/accounts/"+testAccountID+"/devices/policy/fallback_domains", handler)
+
+	actual, err := client.UpdateFallbackDomain(context.Background(), testAccountID, domains)
+
+	if assert.NoError(t, err) {
+		assert.Equal(t, domains, actual)
+	}
+}
\ No newline at end of file