Repository: cloudflare/cloudflare-go
Pull request #1111 (2022-10-18 15:32:01)

DLP-631 Add support for DLP Endpoints
Adds support for Zero Trust DLP endpoints

## Description

We are adding DLP endpoints support to cloudflare-go and terraform, as we prepare to go live in production.

## Has your change been tested?

Manually, through a simple program running DLP endpoint calls against my account.

## 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.  --> **PENDING RELEASE**

[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://codecov.io/gh/cloudflare/cloudflare-go/pull/1111?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare) Report
> Merging [#1111](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1111?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare) (b3c497d) into [master](https://codecov.io/gh/cloudflare/cloudflare-go/commit/e39278eab70d99b8cee194cc1ef12f4b4b8611ed?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare) (e39278e) will **increase** coverage by `0.39%`.
> The diff coverage is `61.38%`.

```diff
@@            Coverage Diff             @@
##           master    #1111      +/-   ##
==========================================
+ Coverage   49.94%   50.34%   +0.39%     
==========================================
  Files         115      121       +6     
  Lines       10991    11581     +590     
==========================================
+ Hits         5490     5831     +341     
- Misses       4338     4501     +163     
- Partials     1163     1249      +86     
```


| [Impacted Files](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1111?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare) | Coverage Δ | |
|---|---|---|
| [access\_organization.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-YWNjZXNzX29yZ2FuaXphdGlvbi5nbw==) | `64.70% <ø> (ø)` | |
| [device\_posture\_rule.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-ZGV2aWNlX3Bvc3R1cmVfcnVsZS5nbw==) | `61.80% <ø> (ø)` | |
| [errors.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-ZXJyb3JzLmdv) | `40.78% <ø> (+0.20%)` | :arrow_up: |
| [pages\_project.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-cGFnZXNfcHJvamVjdC5nbw==) | `50.81% <ø> (ø)` | |
| [rulesets.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-cnVsZXNldHMuZ28=) | `35.09% <ø> (ø)` | |
| [stream.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-c3RyZWFtLmdv) | `64.91% <ø> (ø)` | |
| [teams\_accounts.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-dGVhbXNfYWNjb3VudHMuZ28=) | `53.84% <ø> (ø)` | |
| [teams\_devices.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-dGVhbXNfZGV2aWNlcy5nbw==) | `52.63% <ø> (ø)` | |
| [user\_agent.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-dXNlcl9hZ2VudC5nbw==) | `0.00% <0.00%> (ø)` | |
| [split\_tunnel.go](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare#diff-c3BsaXRfdHVubmVsLmdv) | `40.38% <26.92%> (-13.47%)` | :arrow_down: |
| ... and [21 more](https://codecov.io/gh/cloudflare/cloudflare-go/pull/1111/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare) | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudflare)


Patch:
diff --git a/.changelog/1105.txt b/.changelog/1105.txt
deleted file mode 100644
index a8631dec8..000000000
--- a/.changelog/1105.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-```release-note:enhancement
-total_tls: adds support for TotalTLS
-```
diff --git a/.changelog/1106.txt b/.changelog/1106.txt
deleted file mode 100644
index da158169d..000000000
--- a/.changelog/1106.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-```release-note:enhancement
-cloudflare: expose `Messages` from the `Response` object
-```
diff --git a/.changelog/1108.txt b/.changelog/1108.txt
deleted file mode 100644
index 76a58babe..000000000
--- a/.changelog/1108.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-```release-note:dependency
-deps: bumps github.com/urfave/cli/v2 from 2.19.2 to 2.20.2
-```
diff --git a/.changelog/1111.txt b/.changelog/1111.txt
new file mode 100644
index 000000000..330d7370e
--- /dev/null
+++ b/.changelog/1111.txt
@@ -0,0 +1,3 @@
+```release-note:enhancement
+dlp: Adds support for DLP resources
+```
diff --git a/.changelog/1112.txt b/.changelog/1112.txt
deleted file mode 100644
index 07aa7b3cc..000000000
--- a/.changelog/1112.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-```release-note:dependency
-deps: bumps goreleaser/goreleaser-action from 3.1.0 to 3.2.0
-```
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 9d6a46815..33f24363b 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -17,7 +17,7 @@ jobs:
         with:
           go-version: ^1.17
       - name: Run GoReleaser
-        uses: goreleaser/goreleaser-action@v3.2.0
+        uses: goreleaser/goreleaser-action@v3.1.0
         with:
           version: latest
           args: release --rm-dist
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 253630ae9..a858622a4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,13 +7,6 @@ BREAKING CHANGES:
 ENHANCEMENTS:
 
 * account_member: add support for domain scoped roles ([#1095](https://github.com/cloudflare/cloudflare-go/issues/1095))
-* cloudflare: expose `Messages` from the `Response` object ([#1106](https://github.com/cloudflare/cloudflare-go/issues/1106))
-* total_tls: adds support for TotalTLS ([#1105](https://github.com/cloudflare/cloudflare-go/issues/1105))
-
-DEPENDENCIES:
-
-* deps: bumps github.com/urfave/cli/v2 from 2.19.2 to 2.20.2 ([#1108](https://github.com/cloudflare/cloudflare-go/issues/1108))
-* deps: bumps goreleaser/goreleaser-action from 3.1.0 to 3.2.0 ([#1112](https://github.com/cloudflare/cloudflare-go/issues/1112))
 
 ## 0.52.0 (October 12th, 2022)
 
diff --git a/cloudflare.go b/cloudflare.go
index 9219f7239..68576c025 100644
--- a/cloudflare.go
+++ b/cloudflare.go
@@ -5,7 +5,6 @@ import (
 	"bytes"
 	"context"
 	"encoding/json"
-	"errors"
 	"fmt"
 	"io"
 	"io/ioutil"
@@ -17,6 +16,8 @@ import (
 	"strings"
 	"time"
 
+	"errors"
+
 	"golang.org/x/time/rate"
 )
 
@@ -346,7 +347,6 @@ func (api *API) makeRequestWithAuthTypeAndHeadersComplete(ctx context.Context, m
 			Errors:        errBody.Errors,
 			ErrorCodes:    errCodes,
 			ErrorMessages: errMsgs,
-			Messages:      errBody.Messages,
 		}
 
 		switch resp.StatusCode {
diff --git a/dlp_profile.go b/dlp_profile.go
new file mode 100644
index 000000000..8b80eb0db
--- /dev/null
+++ b/dlp_profile.go
@@ -0,0 +1,217 @@
+package cloudflare
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/http"
+	"time"
+)
+
+var (
+	ErrMissingProfileID = errors.New("missing required profile ID")
+)
+
+// DLPPattern represents a DLP Pattern that matches an entry.
+type DLPPattern struct {
+	Regex      string `json:"regex,omitempty"`
+	Validation string `json:"validation,omitempty"`
+}
+
+// DLPEntry represents a DLP Entry, which can be matched in HTTP bodies or files.
+type DLPEntry struct {
+	ID        string `json:"id,omitempty"`
+	Name      string `json:"name,omitempty"`
+	ProfileID string `json:"profile_id,omitempty"`
+	Enabled   *bool  `json:"enabled,omitempty"`
+	Type      string `json:"type,omitempty"`
+
+	// The following fields are only present for custom entries.
+
+	Pattern   *DLPPattern `json:"pattern,omitempty"`
+	CreatedAt *time.Time  `json:"created_at,omitempty"`
+	UpdatedAt *time.Time  `json:"updated_at,omitempty"`
+}
+
+// DLPProfile represents a DLP Profile, which contains a set
+// of entries.
+type DLPProfile struct {
+	ID          string `json:"id,omitempty"`
+	Name        string `json:"name,omitempty"`
+	Type        string `json:"type,omitempty"`
+	Description string `json:"description,omitempty"`
+
+	// The following fields are omitted for predefined DLP
+	// profiles
+	Entries   []DLPEntry `json:"entries,omitempty"`
+	CreatedAt *time.Time `json:"created_at,omitempty"`
+	UpdatedAt *time.Time `json:"updated_at,omitempty"`
+}
+
+// DLPProfilesCreateRequest represents a request to create a
+// set of profiles.
+type DLPProfilesCreateRequest struct {
+	Profiles []DLPProfile `json:"profiles"`
+}
+
+// DLPProfileListResponse represents the response from the list
+// dlp profiles endpoint.
+type DLPProfileListResponse struct {
+	Result []DLPProfile `json:"result"`
+	Response
+}
+
+// DLPProfileResponse is the API response, containing a single
+// access application.
+type DLPProfileResponse struct {
+	Success  bool       `json:"success"`
+	Errors   []string   `json:"errors"`
+	Messages []string   `json:"messages"`
+	Result   DLPProfile `json:"result"`
+}
+
+type ListDLPProfilesParams struct{}
+
+type CreateDLPProfilesParams struct {
+	Profiles []DLPProfile `json:"profiles"`
+	Type     string
+}
+
+type UpdateDLPProfileParams struct {
+	ProfileID string
+	Profile   DLPProfile
+	Type      string
+}
+
+// ListDLPProfiles returns all DLP profiles within an account.
+//
+// API reference: https://api.cloudflare.com/#dlp-profiles-list-all-profiles
+func (api *API) ListDLPProfiles(ctx context.Context, rc *ResourceContainer, params ListDLPProfilesParams) ([]DLPProfile, error) {
+	if rc.Identifier == "" {
+		return []DLPProfile{}, ErrMissingResourceIdentifier
+	}
+
+	uri := buildURI(fmt.Sprintf("/%s/%s/dlp/profiles", rc.Level, rc.Identifier), nil)
+
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return []DLPProfile{}, err
+	}
+
+	var dlpProfilesListResponse DLPProfileListResponse
+	err = json.Unmarshal(res, &dlpProfilesListResponse)
+	if err != nil {
+		return []DLPProfile{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return dlpProfilesListResponse.Result, nil
+}
+
+// GetDLPProfile returns a single DLP profile (custom or predefined) based on
+// the profile ID.
+//
+// API reference: https://api.cloudflare.com/#dlp-profiles-get-dlp-profile
+func (api *API) GetDLPProfile(ctx context.Context, rc *ResourceContainer, profileID string) (DLPProfile, error) {
+	if rc.Identifier == "" {
+		return DLPProfile{}, ErrMissingResourceIdentifier
+	}
+
+	if profileID == "" {
+		return DLPProfile{}, ErrMissingProfileID
+	}
+
+	uri := buildURI(fmt.Sprintf("/%s/%s/dlp/profiles/%s", rc.Level, rc.Identifier, profileID), nil)
+
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return DLPProfile{}, err
+	}
+
+	var dlpProfileResponse DLPProfileResponse
+	err = json.Unmarshal(res, &dlpProfileResponse)
+	if err != nil {
+		return DLPProfile{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return dlpProfileResponse.Result, nil
+}
+
+// CreateDLPProfiles creates a set of custom DLP Profile.
+//
+// API reference: https://api.cloudflare.com/#dlp-profiles-create-custom-profiles
+func (api *API) CreateDLPProfiles(ctx context.Context, rc *ResourceContainer, params CreateDLPProfilesParams) ([]DLPProfile, error) {
+	if rc.Identifier == "" {
+		return []DLPProfile{}, ErrMissingResourceIdentifier
+	}
+
+	if params.Type == "" {
+		params.Type = "custom"
+	}
+
+	uri := buildURI(fmt.Sprintf("/%s/%s/dlp/profiles/%s", rc.Level, rc.Identifier, params.Type), nil)
+
+	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params)
+	if err != nil {
+		return []DLPProfile{}, err
+	}
+
+	var dLPCustomProfilesResponse DLPProfileListResponse
+	err = json.Unmarshal(res, &dLPCustomProfilesResponse)
+	if err != nil {
+		return []DLPProfile{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return dLPCustomProfilesResponse.Result, nil
+}
+
+// DeleteDLPProfile deletes a DLP custom profile.
+//
+// API reference: https://api.cloudflare.com/#dlp-profiles-delete-custom-profile
+func (api *API) DeleteDLPProfile(ctx context.Context, rc *ResourceContainer, profileID string) error {
+	if rc.Identifier == "" {
+		return ErrMissingResourceIdentifier
+	}
+
+	if profileID == "" {
+		return ErrMissingProfileID
+	}
+
+	uri := buildURI(fmt.Sprintf("/%s/%s/dlp/profiles/custom/%s", rc.Level, rc.Identifier, profileID), nil)
+
+	_, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
+	return err
+}
+
+// UpdateDLPProfile updates a DLP profile.
+//
+// API reference: https://api.cloudflare.com/#dlp-profiles-update-custom-profile
+// API reference: https://api.cloudflare.com/#dlp-profiles-update-predefined-profile
+func (api *API) UpdateDLPProfile(ctx context.Context, rc *ResourceContainer, params UpdateDLPProfileParams) (DLPProfile, error) {
+	if rc.Identifier == "" {
+		return DLPProfile{}, ErrMissingResourceIdentifier
+	}
+
+	if params.Type == "" {
+		params.Type = "custom"
+	}
+
+	if params.ProfileID == "" {
+		return DLPProfile{}, ErrMissingProfileID
+	}
+
+	uri := buildURI(fmt.Sprintf("/%s/%s/dlp/profiles/%s/%s", rc.Level, rc.Identifier, params.Type, params.ProfileID), nil)
+
+	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params.Profile)
+	if err != nil {
+		return DLPProfile{}, err
+	}
+
+	var dlpProfileResponse DLPProfileResponse
+	err = json.Unmarshal(res, &dlpProfileResponse)
+	if err != nil {
+		return DLPProfile{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return dlpProfileResponse.Result, nil
+}
diff --git a/errors.go b/errors.go
index 9b92d1dca..14e045474 100644
--- a/errors.go
+++ b/errors.go
@@ -70,9 +70,6 @@ type Error struct {
 	// ErrorMessages is a list of all the error codes.
 	ErrorMessages []string
 
-	// Messages is a list of informational messages provided by the endpoint.
-	Messages []ResponseInfo
-
 	// RayID is the internal identifier for the request that was made.
 	RayID string
 }
@@ -93,21 +90,7 @@ func (e Error) Error() string {
 		errMessages = append(errMessages, m)
 	}
 
-	msgs := []string{}
-	for _, m := range e.Messages {
-		msgs = append(msgs, m.Message)
-	}
-
-	errString += strings.Join(errMessages, ", ")
-
-	// `Messages` is primarily used for additional validation failure notes in
-	// page rules. This shouldn't be used going forward but instead, use the
-	// error fields appropriately.
-	if len(msgs) > 0 {
-		errString += "\n" + strings.Join(msgs, "  \n")
-	}
-
-	return errString
+	return errString + strings.Join(errMessages, ", ")
 }
 
 // RequestError is for 4xx errors that we encounter not covered elsewhere
@@ -136,10 +119,6 @@ func (e RequestError) InternalErrorCodeIs(code int) bool {
 	return e.cloudflareError.InternalErrorCodeIs(code)
 }
 
-func (e RequestError) Messages() []ResponseInfo {
-	return e.cloudflareError.Messages
-}
-
 func (e RequestError) RayID() string {
 	return e.cloudflareError.RayID
 }
diff --git a/go.mod b/go.mod
index 3917b303c..df6f3e680 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,7 @@ require (
 	github.com/olekukonko/tablewriter v0.0.5
 	github.com/pkg/errors v0.9.1
 	github.com/stretchr/testify v1.8.0
-	github.com/urfave/cli/v2 v2.20.2
+	github.com/urfave/cli/v2 v2.19.2
 	golang.org/x/net v0.0.0-20220722155237-a158d28d115b
 	golang.org/x/time v0.0.0-20220224211638-0e9765cccd65
 )
diff --git a/go.sum b/go.sum
index a1eb1bc99..751262108 100644
--- a/go.sum
+++ b/go.sum
@@ -61,8 +61,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/urfave/cli/v2 v2.20.2 h1:dKA0LUjznZpwmmbrc0pOgcLTEilnHeM8Av9Yng77gHM=
-github.com/urfave/cli/v2 v2.20.2/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
+github.com/urfave/cli/v2 v2.19.2 h1:eXu5089gqqiDQKSnFW+H/FhjrxRGztwSxlTsVK7IuqQ=
+github.com/urfave/cli/v2 v2.19.2/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
 github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
 github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
diff --git a/total_tls.go b/total_tls.go
deleted file mode 100644
index 98772e8b3..000000000
--- a/total_tls.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package cloudflare
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"net/http"
-)
-
-type TotalTLS struct {
-	Enabled              *bool  `json:"enabled,omitempty"`
-	CertificateAuthority string `json:"certificate_authority,omitempty"`
-	ValidityDays         int    `json:"validity_days,omitempty"`
-}
-
-type TotalTLSResponse struct {
-	Response
-	Result TotalTLS `json:"result"`
-}
-
-// GetTotalTLS Get Total TLS Settings for a Zone.
-//
-// API Reference: https://api.cloudflare.com/#total-tls-total-tls-settings-details
-func (api *API) GetTotalTLS(ctx context.Context, rc *ResourceContainer) (TotalTLS, error) {
-	if rc.Identifier == "" {
-		return TotalTLS{}, ErrMissingZoneID
-	}
-	uri := fmt.Sprintf("/zones/%s/acm/total_tls", rc.Identifier)
-	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
-	if err != nil {
-		return TotalTLS{}, err
-	}
-
-	var r TotalTLSResponse
-	err = json.Unmarshal(res, &r)
-	if err != nil {
-		return TotalTLS{}, err
-	}
-
-	return r.Result, nil
-}
-
-// SetTotalTLS Set Total TLS Settings or disable the feature for a Zone.
-//
-// API Reference: https://api.cloudflare.com/#total-tls-enable-or-disable-total-tls
-func (api *API) SetTotalTLS(ctx context.Context, rc *ResourceContainer, params TotalTLS) (TotalTLS, error) {
-	if rc.Identifier == "" {
-		return TotalTLS{}, ErrMissingZoneID
-	}
-	uri := fmt.Sprintf("/zones/%s/acm/total_tls", rc.Identifier)
-	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params)
-	if err != nil {
-		return TotalTLS{}, err
-	}
-
-	var r TotalTLSResponse
-	err = json.Unmarshal(res, &r)
-	if err != nil {
-		return TotalTLS{}, err
-	}
-
-	return r.Result, nil
-}


Test patch:
diff --git a/dlp_profile_test.go b/dlp_profile_test.go
new file mode 100644
index 000000000..692db6f25
--- /dev/null
+++ b/dlp_profile_test.go
@@ -0,0 +1,574 @@
+package cloudflare
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestDLPProfiles(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": [
+				{
+					"id": "d658f520-6ecb-4a34-a725-ba37243c2d28",
+					"name": "U.S. Social Security Numbers",
+					"entries": [
+						{
+							"id": "111b9d4b-a5c6-40f0-957d-9d53b25dd84a",
+							"name": "SSN Numeric Detection",
+							"profile_id": "d658f520-6ecb-4a34-a725-ba37243c2d28",
+							"enabled": false,
+							"type": "predefined"
+						},
+						{
+							"id": "aec08712-ee49-4109-9d9f-3b229c5b3dcd",
+							"name": "SSN Text",
+							"profile_id": "d658f520-6ecb-4a34-a725-ba37243c2d28",
+							"enabled": false,
+							"type": "predefined"
+						}
+					],
+					"type": "predefined"
+				},
+				{
+					"id": "29678c26-a191-428d-9f63-6e20a4a636a4",
+					"name": "Example Custom Profile",
+					"entries": [
+						{
+							"id": "ef79b054-12d4-4067-bb30-b85f6267b91c",
+							"name": "matches credit card regex",
+							"profile_id": "29678c26-a191-428d-9f63-6e20a4a636a4",
+							"created_at": "2022-10-18T08:00:56Z",
+							"updated_at": "2022-10-18T08:00:57Z",
+							"pattern": {
+								"regex": "^4[0-9]$",
+								"validation": "luhn"
+							},
+							"enabled": true,
+							"type": "custom"
+						}
+					],
+					"created_at": "2022-10-18T08:00:56Z",
+					"updated_at": "2022-10-18T08:00:57Z",
+					"type": "custom",
+					"description": "just a custom profile example"
+				}
+			]
+		}
+		`)
+	}
+
+	createdAt, _ := time.Parse(time.RFC3339, "2022-10-18T08:00:56Z")
+	updatedAt, _ := time.Parse(time.RFC3339, "2022-10-18T08:00:57Z")
+
+	want := []DLPProfile{
+		{
+			ID:          "d658f520-6ecb-4a34-a725-ba37243c2d28",
+			Name:        "U.S. Social Security Numbers",
+			Type:        "predefined",
+			Description: "",
+			Entries: []DLPEntry{
+				{
+					ID:        "111b9d4b-a5c6-40f0-957d-9d53b25dd84a",
+					Name:      "SSN Numeric Detection",
+					ProfileID: "d658f520-6ecb-4a34-a725-ba37243c2d28",
+					Type:      "predefined",
+					Enabled:   BoolPtr(false),
+				},
+				{
+					ID:   "aec08712-ee49-4109-9d9f-3b229c5b3dcd",
+					Name: "SSN Text", ProfileID: "d658f520-6ecb-4a34-a725-ba37243c2d28",
+					Type:    "predefined",
+					Enabled: BoolPtr(false),
+				},
+			},
+		},
+		{
+			ID:          "29678c26-a191-428d-9f63-6e20a4a636a4",
+			Name:        "Example Custom Profile",
+			Type:        "custom",
+			Description: "just a custom profile example",
+			Entries: []DLPEntry{
+				{
+					ID:        "ef79b054-12d4-4067-bb30-b85f6267b91c",
+					Name:      "matches credit card regex",
+					ProfileID: "29678c26-a191-428d-9f63-6e20a4a636a4",
+					Enabled:   BoolPtr(true),
+					Type:      "custom",
+					Pattern: &DLPPattern{
+						Regex:      "^4[0-9]$",
+						Validation: "luhn",
+					},
+					CreatedAt: &createdAt,
+					UpdatedAt: &updatedAt,
+				},
+			},
+			CreatedAt: &createdAt,
+			UpdatedAt: &updatedAt,
+		}}
+
+	mux.HandleFunc("/accounts/"+testAccountID+"/dlp/profiles", handler)
+
+	actual, err := client.ListDLPProfiles(context.Background(), AccountIdentifier(testAccountID), ListDLPProfilesParams{})
+	require.NoError(t, err)
+	require.Equal(t, want, actual)
+}
+
+func TestGetDLPProfile(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": {
+				"id": "29678c26-a191-428d-9f63-6e20a4a636a4",
+				"name": "Example Custom Profile",
+				"entries": [
+					{
+						"id": "ef79b054-12d4-4067-bb30-b85f6267b91c",
+						"name": "matches credit card regex",
+						"profile_id": "29678c26-a191-428d-9f63-6e20a4a636a4",
+						"created_at": "2022-10-18T08:00:56Z",
+						"updated_at": "2022-10-18T08:00:57Z",
+						"pattern": {
+							"regex": "^4[0-9]$",
+							"validation": "luhn"
+						},
+						"enabled": true,
+						"type": "custom"
+					}
+				],
+				"created_at": "2022-10-18T08:00:56Z",
+				"updated_at": "2022-10-18T08:00:57Z",
+				"type": "custom",
+				"description": "just a custom profile example"
+			}
+		}`)
+	}
+
+	createdAt, _ := time.Parse(time.RFC3339, "2022-10-18T08:00:56Z")
+	updatedAt, _ := time.Parse(time.RFC3339, "2022-10-18T08:00:57Z")
+
+	want := DLPProfile{
+		ID:          "29678c26-a191-428d-9f63-6e20a4a636a4",
+		Name:        "Example Custom Profile",
+		Type:        "custom",
+		Description: "just a custom profile example",
+		Entries: []DLPEntry{
+			{
+				ID:        "ef79b054-12d4-4067-bb30-b85f6267b91c",
+				Name:      "matches credit card regex",
+				ProfileID: "29678c26-a191-428d-9f63-6e20a4a636a4",
+				Enabled:   BoolPtr(true),
+				Pattern: &DLPPattern{
+					Regex:      "^4[0-9]$",
+					Validation: "luhn",
+				},
+				Type:      "custom",
+				CreatedAt: &createdAt,
+				UpdatedAt: &updatedAt,
+			},
+		},
+		CreatedAt: &createdAt,
+		UpdatedAt: &updatedAt,
+	}
+
+	mux.HandleFunc("/accounts/"+testAccountID+"/dlp/profiles/29678c26-a191-428d-9f63-6e20a4a636a4", handler)
+
+	actual, err := client.GetDLPProfile(context.Background(), AccountIdentifier(testAccountID), "29678c26-a191-428d-9f63-6e20a4a636a4")
+	require.NoError(t, err)
+	require.Equal(t, want, actual)
+}
+
+func TestCreateDLPCustomProfiles(t *testing.T) {
+	setup()
+	defer teardown()
+
+	handler := 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")
+
+		var reqBody DLPProfilesCreateRequest
+		err := json.NewDecoder(r.Body).Decode(&reqBody)
+		require.Nil(t, err)
+		requestProfile := reqBody.Profiles[0]
+
+		fmt.Fprintf(w, `{
+			"success": true,
+			"errors": [],
+			"messages": [],
+			"result": [{
+				"id": "29678c26-a191-428d-9f63-6e20a4a636a4",
+				"name": "`+requestProfile.Name+`",
+				"entries": [
+					{
+						"id": "ef79b054-12d4-4067-bb30-b85f6267b91c",
+						"name": "`+requestProfile.Entries[0].Name+`",
+						"profile_id": "29678c26-a191-428d-9f63-6e20a4a636a4",
+						"created_at": "2022-10-18T08:00:56Z",
+						"updated_at": "2022-10-18T08:00:57Z",
+						"pattern": {
+							"regex": "`+requestProfile.Entries[0].Pattern.Regex+`",
+							"validation": "`+requestProfile.Entries[0].Pattern.Validation+`"
+						},
+						"enabled": `+fmt.Sprintf("%t", Bool(requestProfile.Entries[0].Enabled))+`,
+						"type": "custom"
+					}
+				],
+				"created_at": "2022-10-18T08:00:56Z",
+				"updated_at": "2022-10-18T08:00:57Z",
+				"type": "custom",
+				"description": "`+requestProfile.Description+`"
+			}]
+		}`)
+	}
+
+	createdAt, _ := time.Parse(time.RFC3339, "2022-10-18T08:00:56Z")
+	updatedAt, _ := time.Parse(time.RFC3339, "2022-10-18T08:00:57Z")
+
+	want := []DLPProfile{
+		{
+			ID:          "29678c26-a191-428d-9f63-6e20a4a636a4",
+			Name:        "Example Custom Profile",
+			Type:        "custom",
+			Description: "just a custom profile example",
+			Entries: []DLPEntry{
+				{
+					ID:        "ef79b054-12d4-4067-bb30-b85f6267b91c",
+					Name:      "matches credit card regex",
+					ProfileID: "29678c26-a191-428d-9f63-6e20a4a636a4",
+					Enabled:   BoolPtr(true),
+					Type:      "custom",
+					Pattern: &DLPPattern{
+						Regex:      "^4[0-9]$",
+						Validation: "luhn",
+					},
+					CreatedAt: &createdAt,
+					UpdatedAt: &updatedAt,
+				},
+			},
+			CreatedAt: &createdAt,
+			UpdatedAt: &updatedAt,
+		},
+	}
+
+	mux.HandleFunc("/accounts/"+testAccountID+"/dlp/profiles/custom", handler)
+
+	profiles := []DLPProfile{
+		{
+			Name:        "Example Custom Profile",
+			Description: "just a custom profile example",
+			Type:        "custom",
+			Entries: []DLPEntry{
+				{
+					Name:    "matches credit card regex",
+					Enabled: BoolPtr(true),
+					Pattern: &DLPPattern{
+						Regex:      "^4[0-9]$",
+						Validation: "luhn",
+					},
+				},
+			},
+		},
+	}
+	actual, err := client.CreateDLPProfiles(context.Background(), AccountIdentifier(testAccountID), CreateDLPProfilesParams{Profiles: profiles})
+	require.NoError(t, err)
+	require.Equal(t, want, actual)
+}
+
+func TestCreateDLPCustomProfile(t *testing.T) {
+	setup()
+	defer teardown()
+
+	handler := 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")
+
+		var reqBody DLPProfilesCreateRequest
+		err := json.NewDecoder(r.Body).Decode(&reqBody)
+		require.Nil(t, err)
+		requestProfile := reqBody.Profiles[0]
+
+		fmt.Fprintf(w, `{
+			"success": true,
+			"errors": [],
+			"messages": [],
+			"result": [{
+				"id": "29678c26-a191-428d-9f63-6e20a4a636a4",
+				"name": "`+requestProfile.Name+`",
+				"entries": [
+					{
+						"id": "ef79b054-12d4-4067-bb30-b85f6267b91c",
+						"name": "`+requestProfile.Entries[0].Name+`",
+						"profile_id": "29678c26-a191-428d-9f63-6e20a4a636a4",
+						"created_at": "2022-10-18T08:00:56Z",
+						"updated_at": "2022-10-18T08:00:57Z",
+						"pattern": {
+							"regex": "`+requestProfile.Entries[0].Pattern.Regex+`",
+							"validation": "`+requestProfile.Entries[0].Pattern.Validation+`"
+						},
+						"enabled": `+fmt.Sprintf("%t", Bool(requestProfile.Entries[0].Enabled))+`,
+						"type": "custom"
+					}
+				],
+				"created_at": "2022-10-18T08:00:56Z",
+				"updated_at": "2022-10-18T08:00:57Z",
+				"type": "custom",
+				"description": "`+requestProfile.Description+`"
+			}]
+		}`)
+	}
+
+	createdAt, _ := time.Parse(time.RFC3339, "2022-10-18T08:00:56Z")
+	updatedAt, _ := time.Parse(time.RFC3339, "2022-10-18T08:00:57Z")
+
+	want := []DLPProfile{{
+
+		ID:          "29678c26-a191-428d-9f63-6e20a4a636a4",
+		Name:        "Example Custom Profile",
+		Type:        "custom",
+		Description: "just a custom profile example",
+		Entries: []DLPEntry{
+			{
+				ID:        "ef79b054-12d4-4067-bb30-b85f6267b91c",
+				Name:      "matches credit card regex",
+				ProfileID: "29678c26-a191-428d-9f63-6e20a4a636a4",
+				Type:      "custom",
+				Enabled:   BoolPtr(true),
+				Pattern: &DLPPattern{
+					Regex:      "^4[0-9]$",
+					Validation: "luhn",
+				},
+				CreatedAt: &createdAt,
+				UpdatedAt: &updatedAt,
+			},
+		},
+		CreatedAt: &createdAt,
+		UpdatedAt: &updatedAt,
+	}}
+
+	mux.HandleFunc("/accounts/"+testAccountID+"/dlp/profiles/custom", handler)
+
+	profiles := []DLPProfile{{
+		Name:        "Example Custom Profile",
+		Description: "just a custom profile example",
+		Entries: []DLPEntry{
+			{
+				Name:    "matches credit card regex",
+				Enabled: BoolPtr(true),
+				Pattern: &DLPPattern{
+					Regex:      "^4[0-9]$",
+					Validation: "luhn",
+				},
+			},
+		},
+	}}
+
+	actual, err := client.CreateDLPProfiles(context.Background(), AccountIdentifier(testAccountID), CreateDLPProfilesParams{
+		Profiles: profiles,
+	})
+	require.NoError(t, err)
+	require.Equal(t, want, actual)
+}
+
+func TestUpdateDLPCustomProfile(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")
+
+		var requestProfile DLPProfile
+		err := json.NewDecoder(r.Body).Decode(&requestProfile)
+		require.Nil(t, err)
+
+		fmt.Fprintf(w, `{
+			"success": true,
+			"errors": [],
+			"messages": [],
+			"result": {
+				"id": "29678c26-a191-428d-9f63-6e20a4a636a4",
+				"name": "`+requestProfile.Name+`",
+				"entries": [
+					{
+						"id": "ef79b054-12d4-4067-bb30-b85f6267b91c",
+						"name": "`+requestProfile.Entries[0].Name+`",
+						"profile_id": "29678c26-a191-428d-9f63-6e20a4a636a4",
+						"created_at": "2022-10-18T08:00:56Z",
+						"updated_at": "2022-10-18T08:00:57Z",
+						"pattern": {
+							"regex": "`+requestProfile.Entries[0].Pattern.Regex+`",
+							"validation": "`+requestProfile.Entries[0].Pattern.Validation+`"
+						},
+						"enabled": `+fmt.Sprintf("%t", Bool(requestProfile.Entries[0].Enabled))+`,
+						"type": "custom"
+					}
+				],
+				"created_at": "2022-10-18T08:00:56Z",
+				"updated_at": "2022-10-18T08:00:57Z",
+				"type": "custom",
+				"description": "`+requestProfile.Description+`"
+			}
+		}`)
+	}
+
+	createdAt, _ := time.Parse(time.RFC3339, "2022-10-18T08:00:56Z")
+	updatedAt, _ := time.Parse(time.RFC3339, "2022-10-18T08:00:57Z")
+
+	want := DLPProfile{
+
+		ID:          "29678c26-a191-428d-9f63-6e20a4a636a4",
+		Name:        "Example Custom Profile",
+		Type:        "custom",
+		Description: "just a custom profile example",
+		Entries: []DLPEntry{
+			{
+				ID:        "ef79b054-12d4-4067-bb30-b85f6267b91c",
+				Name:      "matches credit card regex",
+				ProfileID: "29678c26-a191-428d-9f63-6e20a4a636a4",
+				Enabled:   BoolPtr(true),
+				Type:      "custom",
+				Pattern: &DLPPattern{
+					Regex:      "^4[0-9]$",
+					Validation: "luhn",
+				},
+				CreatedAt: &createdAt,
+				UpdatedAt: &updatedAt,
+			},
+		},
+		CreatedAt: &createdAt,
+		UpdatedAt: &updatedAt,
+	}
+
+	mux.HandleFunc("/accounts/"+testAccountID+"/dlp/profiles/custom/29678c26-a191-428d-9f63-6e20a4a636a4", handler)
+
+	customProfile := DLPProfile{
+		Name:        "Example Custom Profile",
+		Description: "just a custom profile example",
+		Entries: []DLPEntry{
+			{
+				Name:    "matches credit card regex",
+				Enabled: BoolPtr(true),
+				Pattern: &DLPPattern{
+					Regex:      "^4[0-9]$",
+					Validation: "luhn",
+				},
+			},
+		},
+	}
+	actual, err := client.UpdateDLPProfile(context.Background(), AccountIdentifier(testAccountID), UpdateDLPProfileParams{
+		ProfileID: "29678c26-a191-428d-9f63-6e20a4a636a4",
+		Type:      "custom",
+		Profile:   customProfile,
+	})
+	require.NoError(t, err)
+	require.Equal(t, want, actual)
+}
+
+func TestUpdateDLPPredefinedProfile(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")
+
+		var requestProfile DLPProfile
+		err := json.NewDecoder(r.Body).Decode(&requestProfile)
+		require.Nil(t, err)
+
+		fmt.Fprintf(w, `{
+			"success": true,
+			"errors": [],
+			"messages": [],
+			"result": {
+				"id": "29678c26-a191-428d-9f63-6e20a4a636a4",
+				"name": "Example predefined profile",
+				"entries": [
+					{
+						"id": "ef79b054-12d4-4067-bb30-b85f6267b91c",
+						"name": "Example predefined entry",
+						"profile_id": "29678c26-a191-428d-9f63-6e20a4a636a4",
+						"enabled": `+fmt.Sprintf("%t", Bool(requestProfile.Entries[0].Enabled))+`,
+						"type": "predefined"
+					}
+				],
+				"type": "predefined",
+				"description": "example predefined profile"
+			}
+		}`)
+	}
+
+	want := DLPProfile{
+		ID:          "29678c26-a191-428d-9f63-6e20a4a636a4",
+		Name:        "Example predefined profile",
+		Type:        "predefined",
+		Description: "example predefined profile",
+		Entries: []DLPEntry{
+			{
+				ID:        "ef79b054-12d4-4067-bb30-b85f6267b91c",
+				Name:      "Example predefined entry",
+				ProfileID: "29678c26-a191-428d-9f63-6e20a4a636a4",
+				Type:      "predefined",
+				Enabled:   BoolPtr(true),
+			},
+		},
+	}
+
+	mux.HandleFunc("/accounts/"+testAccountID+"/dlp/profiles/predefined/29678c26-a191-428d-9f63-6e20a4a636a4", handler)
+
+	actual, err := client.UpdateDLPProfile(context.Background(), AccountIdentifier(testAccountID), UpdateDLPProfileParams{
+		ProfileID: "29678c26-a191-428d-9f63-6e20a4a636a4",
+		Type:      "predefined",
+		Profile: DLPProfile{
+			Entries: []DLPEntry{
+				{
+					ID:      "29678c26-a191-428d-9f63-6e20a4a636a4",
+					Enabled: BoolPtr(true),
+				},
+			},
+		}})
+	require.NoError(t, err)
+	require.Equal(t, want, actual)
+}
+
+func TestDeleteDLPCustomProfile(t *testing.T) {
+	setup()
+	defer teardown()
+
+	handler := 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": []
+		}`)
+	}
+
+	mux.HandleFunc("/accounts/"+testAccountID+"/dlp/profiles/custom/29678c26-a191-428d-9f63-6e20a4a636a4", handler)
+
+	err := client.DeleteDLPProfile(context.Background(), AccountIdentifier(testAccountID), "29678c26-a191-428d-9f63-6e20a4a636a4")
+	require.NoError(t, err)
+}
diff --git a/total_tls_test.go b/total_tls_test.go
deleted file mode 100644
index 3b5e23963..000000000
--- a/total_tls_test.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package cloudflare
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestTotalTLS_GetSettings(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc(fmt.Sprintf("/zones/%s/acm/total_tls", testZoneID), 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": {
-		"enabled": true,
-		"certificate_authority": "google",
-		"validity_days": 90
-	  }
-	}`)
-	})
-
-	_, err := client.GetTotalTLS(context.Background(), ZoneIdentifier(""))
-	if assert.Error(t, err) {
-		assert.Equal(t, ErrMissingZoneID, err)
-	}
-
-	result, err := client.GetTotalTLS(context.Background(), ZoneIdentifier(testZoneID))
-	if assert.NoError(t, err) {
-		assert.Equal(t, BoolPtr(true), result.Enabled)
-		assert.Equal(t, "google", result.CertificateAuthority)
-		assert.Equal(t, 90, result.ValidityDays)
-	}
-}
-
-func TestTotalTLS_SetSettings(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc(fmt.Sprintf("/zones/%s/acm/total_tls", testZoneID), 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": {
-    "enabled": true,
-    "certificate_authority": "google",
-    "validity_days": 90
-  }
-}`)
-	})
-
-	_, err := client.SetTotalTLS(context.Background(), ZoneIdentifier(""), TotalTLS{})
-	if assert.Error(t, err) {
-		assert.Equal(t, ErrMissingZoneID, err)
-	}
-
-	result, err := client.SetTotalTLS(context.Background(), ZoneIdentifier(testZoneID), TotalTLS{CertificateAuthority: "google", Enabled: BoolPtr(true)})
-	if assert.NoError(t, err) {
-		assert.Equal(t, BoolPtr(true), result.Enabled)
-		assert.Equal(t, "google", result.CertificateAuthority)
-		assert.Equal(t, 90, result.ValidityDays)
-	}
-}