Repository: cloudflare/cloudflare-go
Pull request #1474 (2024-01-02 18:13:46)

Add Zaraz
<!-- 
Provide a general summary of your changes in the title above. You should
remove this overview, any sections and any section descriptions you
don't need below before submitting. There isn't a strict requirement to
use this template if you can structure your description and still cover
these points. 
-->

## Description

The primary goal is to be able to terraform Zaraz (RM-17254)

The following endpoints need to be covered.

/config (GET and PUT)
/workflow (GET and PUT)
/publish (POST)
/history (GET)
/export (GET)
/default (GET)

<!--
Describe your changes in detail through motivation and context. Why is
this change required? What problem does it solve? If it fixes an open
issue, link to the issue using GitHub's closing issues keywords[1].
-->
## Has your change been tested?

Yes. Added a unit test for every function. The API itself is stable and is used regularly via the dash.

<!--
Explain how the change has been tested and what you ran to confirm your
change affects other parts of the code. Automated tests are generally
expected and changes without tests should explain why they aren't
required.
-->

## 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 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/



Patch:
diff --git a/.changelog/1474.txt b/.changelog/1474.txt
new file mode 100644
index 000000000..6c275e3ec
--- /dev/null
+++ b/.changelog/1474.txt
@@ -0,0 +1,3 @@
+```release-note:enhancement
+zaraz: Add support for CRUD APIs
+```
diff --git a/zaraz.go b/zaraz.go
new file mode 100644
index 000000000..dedfdc4c9
--- /dev/null
+++ b/zaraz.go
@@ -0,0 +1,421 @@
+package cloudflare
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"time"
+
+	"github.com/goccy/go-json"
+)
+
+type ZarazConfig struct {
+	DebugKey      string                   `json:"debugKey"`
+	Tools         map[string]ZarazTool     `json:"tools"`
+	Triggers      map[string]ZarazTrigger  `json:"triggers"`
+	ZarazVersion  int64                    `json:"zarazVersion"`
+	Consent       ZarazConsent             `json:"consent,omitempty"`
+	DataLayer     *bool                    `json:"dataLayer,omitempty"`
+	Dlp           []any                    `json:"dlp,omitempty"`
+	HistoryChange *bool                    `json:"historyChange,omitempty"`
+	Settings      ZarazConfigSettings      `json:"settings,omitempty"`
+	Variables     map[string]ZarazVariable `json:"variables,omitempty"`
+}
+
+type ZarazWorker struct {
+	EscapedWorkerName string `json:"escapedWorkerName"`
+	WorkerTag         string `json:"workerTag"`
+	MutableId         string `json:"mutableId,omitempty"`
+}
+type ZarazConfigSettings struct {
+	AutoInjectScript    *bool       `json:"autoInjectScript"`
+	InjectIframes       *bool       `json:"injectIframes,omitempty"`
+	Ecommerce           *bool       `json:"ecommerce,omitempty"`
+	HideQueryParams     *bool       `json:"hideQueryParams,omitempty"`
+	HideIpAddress       *bool       `json:"hideIPAddress,omitempty"`
+	HideUserAgent       *bool       `json:"hideUserAgent,omitempty"`
+	HideExternalReferer *bool       `json:"hideExternalReferer,omitempty"`
+	CookieDomain        string      `json:"cookieDomain,omitempty"`
+	InitPath            string      `json:"initPath,omitempty"`
+	ScriptPath          string      `json:"scriptPath,omitempty"`
+	TrackPath           string      `json:"trackPath,omitempty"`
+	EventsApiPath       string      `json:"eventsApiPath,omitempty"`
+	McRootPath          string      `json:"mcRootPath,omitempty"`
+	ContextEnricher     ZarazWorker `json:"contextEnricher,omitempty"`
+}
+
+type ZarazNeoEvent struct {
+	BlockingTriggers []string       `json:"blockingTriggers"`
+	FiringTriggers   []string       `json:"firingTriggers"`
+	Data             map[string]any `json:"data"`
+	ActionType       string         `json:"actionType,omitempty"`
+}
+
+type ZarazToolType string
+
+const (
+	ZarazToolLibrary   ZarazToolType = "library"
+	ZarazToolComponent ZarazToolType = "component"
+	ZarazToolCustomMc  ZarazToolType = "custom-mc"
+)
+
+type ZarazTool struct {
+	BlockingTriggers []string        `json:"blockingTriggers"`
+	Enabled          *bool           `json:"enabled"`
+	DefaultFields    map[string]any  `json:"defaultFields"`
+	Name             string          `json:"name"`
+	NeoEvents        []ZarazNeoEvent `json:"neoEvents"`
+	Type             ZarazToolType   `json:"type"`
+	DefaultPurpose   string          `json:"defaultPurpose,omitempty"`
+	Library          string          `json:"library,omitempty"`
+	Component        string          `json:"component,omitempty"`
+	Permissions      []string        `json:"permissions,omitempty"`
+	Settings         map[string]any  `json:"settings,omitempty"`
+	Worker           ZarazWorker     `json:"worker,omitempty"`
+}
+
+type ZarazTriggerSystem string
+
+const ZarazPageload ZarazTriggerSystem = "pageload"
+
+type ZarazLoadRuleOp string
+
+type ZarazRuleType string
+
+const (
+	ZarazClickListener     ZarazRuleType = "clickListener"
+	ZarazTimer             ZarazRuleType = "timer"
+	ZarazFormSubmission    ZarazRuleType = "formSubmission"
+	ZarazVariableMatch     ZarazRuleType = "variableMatch"
+	ZarazScrollDepth       ZarazRuleType = "scrollDepth"
+	ZarazElementVisibility ZarazRuleType = "elementVisibility"
+	ZarazClientEval        ZarazRuleType = "clientEval"
+)
+
+type ZarazSelectorType string
+
+const (
+	ZarazXPath ZarazSelectorType = "xpath"
+	ZarazCSS   ZarazSelectorType = "css"
+)
+
+type ZarazRuleSettings struct {
+	Type        ZarazSelectorType `json:"type,omitempty"`
+	Selector    string            `json:"selector,omitempty"`
+	WaitForTags int               `json:"waitForTags,omitempty"`
+	Interval    int               `json:"interval,omitempty"`
+	Limit       int               `json:"limit,omitempty"`
+	Validate    *bool             `json:"validate,omitempty"`
+	Variable    string            `json:"variable,omitempty"`
+	Match       string            `json:"match,omitempty"`
+	Positions   string            `json:"positions,omitempty"`
+	Op          ZarazLoadRuleOp   `json:"op,omitempty"`
+	Value       string            `json:"value,omitempty"`
+}
+
+type ZarazTriggerRule struct {
+	Id       string            `json:"id"`
+	Match    string            `json:"match,omitempty"`
+	Op       ZarazLoadRuleOp   `json:"op,omitempty"`
+	Value    string            `json:"value,omitempty"`
+	Action   ZarazRuleType     `json:"action"`
+	Settings ZarazRuleSettings `json:"settings"`
+}
+
+type ZarazTrigger struct {
+	Name         string             `json:"name"`
+	Description  string             `json:"description,omitempty"`
+	LoadRules    []ZarazTriggerRule `json:"loadRules"`
+	ExcludeRules []ZarazTriggerRule `json:"excludeRules"`
+	ClientRules  []any              `json:"clientRules,omitempty"` // what is this?
+	System       ZarazTriggerSystem `json:"system,omitempty"`
+}
+
+type ZarazVariableType string
+
+const (
+	ZarazVarString ZarazVariableType = "string"
+	ZarazVarSecret ZarazVariableType = "secret"
+	ZarazVarWorker ZarazVariableType = "worker"
+)
+
+type ZarazVariable struct {
+	Name  string            `json:"name"`
+	Type  ZarazVariableType `json:"type"`
+	Value interface{}       `json:"value"`
+}
+
+type ZarazButtonTextTranslations struct {
+	AcceptAll        map[string]string `json:"accept_all"`
+	RejectAll        map[string]string `json:"reject_all"`
+	ConfirmMyChoices map[string]string `json:"confirm_my_choices"`
+}
+
+type ZarazPurpose struct {
+	Name        string `json:"name"`
+	Description string `json:"description"`
+}
+
+type ZarazPurposeWithTranslations struct {
+	Name        map[string]string `json:"name"`
+	Description map[string]string `json:"description"`
+	Order       int               `json:"order"`
+}
+
+type ZarazConsent struct {
+	Enabled                               *bool                                   `json:"enabled"`
+	ButtonTextTranslations                ZarazButtonTextTranslations             `json:"buttonTextTranslations,omitempty"`
+	CompanyEmail                          string                                  `json:"companyEmail,omitempty"`
+	CompanyName                           string                                  `json:"companyName,omitempty"`
+	CompanyStreetAddress                  string                                  `json:"companyStreetAddress,omitempty"`
+	ConsentModalIntroHTML                 string                                  `json:"consentModalIntroHTML,omitempty"`
+	ConsentModalIntroHTMLWithTranslations map[string]string                       `json:"consentModalIntroHTMLWithTranslations,omitempty"`
+	CookieName                            string                                  `json:"cookieName,omitempty"`
+	CustomCSS                             string                                  `json:"customCSS,omitempty"`
+	CustomIntroDisclaimerDismissed        *bool                                   `json:"customIntroDisclaimerDismissed,omitempty"`
+	DefaultLanguage                       string                                  `json:"defaultLanguage,omitempty"`
+	HideModal                             *bool                                   `json:"hideModal,omitempty"`
+	Purposes                              map[string]ZarazPurpose                 `json:"purposes,omitempty"`
+	PurposesWithTranslations              map[string]ZarazPurposeWithTranslations `json:"purposesWithTranslations,omitempty"`
+}
+
+type ZarazConfigResponse struct {
+	Result ZarazConfig `json:"result"`
+	Response
+}
+
+type ZarazWorkflowResponse struct {
+	Result string `json:"result"`
+	Response
+}
+
+type ZarazPublishResponse struct {
+	Result string `json:"result"`
+	Response
+}
+
+type UpdateZarazConfigParams struct {
+	DebugKey      string                   `json:"debugKey"`
+	Tools         map[string]ZarazTool     `json:"tools"`
+	Triggers      map[string]ZarazTrigger  `json:"triggers"`
+	ZarazVersion  int64                    `json:"zarazVersion"`
+	Consent       ZarazConsent             `json:"consent,omitempty"`
+	DataLayer     *bool                    `json:"dataLayer,omitempty"`
+	Dlp           []any                    `json:"dlp,omitempty"`
+	HistoryChange *bool                    `json:"historyChange,omitempty"`
+	Settings      ZarazConfigSettings      `json:"settings,omitempty"`
+	Variables     map[string]ZarazVariable `json:"variables,omitempty"`
+}
+
+type UpdateZarazWorkflowParams struct {
+	Workflow string `json:"workflow"`
+}
+
+type PublishZarazConfigParams struct {
+	Description string `json:"description"`
+}
+
+type ZarazHistoryRecord struct {
+	ID          int64      `json:"id,omitempty"`
+	UserID      string     `json:"userId,omitempty"`
+	Description string     `json:"description,omitempty"`
+	CreatedAt   *time.Time `json:"createdAt,omitempty"`
+	UpdatedAt   *time.Time `json:"updatedAt,omitempty"`
+}
+
+type ZarazConfigHistoryListResponse struct {
+	Result []ZarazHistoryRecord `json:"result"`
+	Response
+	ResultInfo `json:"result_info"`
+}
+
+type ListZarazConfigHistoryParams struct {
+	ResultInfo
+}
+
+type GetZarazConfigsByIdResponse = map[string]interface{}
+
+// listZarazConfigHistoryDefaultPageSize represents the default per_page size of the API.
+var listZarazConfigHistoryDefaultPageSize int = 100
+
+func (api *API) GetZarazConfig(ctx context.Context, rc *ResourceContainer) (ZarazConfigResponse, error) {
+	if rc.Identifier == "" {
+		return ZarazConfigResponse{}, ErrMissingZoneID
+	}
+
+	uri := fmt.Sprintf("/zones/%s/settings/zaraz/v2/config", rc.Identifier)
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return ZarazConfigResponse{}, err
+	}
+
+	var recordResp ZarazConfigResponse
+	err = json.Unmarshal(res, &recordResp)
+	if err != nil {
+		return ZarazConfigResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return recordResp, nil
+}
+
+func (api *API) UpdateZarazConfig(ctx context.Context, rc *ResourceContainer, params UpdateZarazConfigParams) (ZarazConfigResponse, error) {
+	if rc.Identifier == "" {
+		return ZarazConfigResponse{}, ErrMissingZoneID
+	}
+
+	uri := fmt.Sprintf("/zones/%s/settings/zaraz/v2/config", rc.Identifier)
+	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params)
+	if err != nil {
+		return ZarazConfigResponse{}, err
+	}
+
+	var updateResp ZarazConfigResponse
+	err = json.Unmarshal(res, &updateResp)
+	if err != nil {
+		return ZarazConfigResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return updateResp, nil
+}
+
+func (api *API) GetZarazWorkflow(ctx context.Context, rc *ResourceContainer) (ZarazWorkflowResponse, error) {
+	if rc.Identifier == "" {
+		return ZarazWorkflowResponse{}, ErrMissingZoneID
+	}
+
+	uri := fmt.Sprintf("/zones/%s/settings/zaraz/v2/workflow", rc.Identifier)
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return ZarazWorkflowResponse{}, err
+	}
+
+	var response ZarazWorkflowResponse
+	err = json.Unmarshal(res, &response)
+	if err != nil {
+		return ZarazWorkflowResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return response, nil
+}
+
+func (api *API) UpdateZarazWorkflow(ctx context.Context, rc *ResourceContainer, params UpdateZarazWorkflowParams) (ZarazWorkflowResponse, error) {
+	if rc.Identifier == "" {
+		return ZarazWorkflowResponse{}, ErrMissingZoneID
+	}
+
+	uri := fmt.Sprintf("/zones/%s/settings/zaraz/v2/workflow", rc.Identifier)
+	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params.Workflow)
+	if err != nil {
+		return ZarazWorkflowResponse{}, err
+	}
+
+	var response ZarazWorkflowResponse
+	err = json.Unmarshal(res, &response)
+	if err != nil {
+		return ZarazWorkflowResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return response, nil
+}
+
+func (api *API) PublishZarazConfig(ctx context.Context, rc *ResourceContainer, params PublishZarazConfigParams) (ZarazPublishResponse, error) {
+	if rc.Identifier == "" {
+		return ZarazPublishResponse{}, ErrMissingZoneID
+	}
+
+	uri := fmt.Sprintf("/zones/%s/settings/zaraz/v2/publish", rc.Identifier)
+	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params.Description)
+	if err != nil {
+		return ZarazPublishResponse{}, err
+	}
+
+	var response ZarazPublishResponse
+	err = json.Unmarshal(res, &response)
+	if err != nil {
+		return ZarazPublishResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return response, nil
+}
+
+func (api *API) ListZarazConfigHistory(ctx context.Context, rc *ResourceContainer, params ListZarazConfigHistoryParams) ([]ZarazHistoryRecord, *ResultInfo, error) {
+	if rc.Identifier == "" {
+		return nil, nil, ErrMissingZoneID
+	}
+
+	autoPaginate := true
+	if params.PerPage >= 1 || params.Page >= 1 {
+		autoPaginate = false
+	}
+
+	if params.PerPage < 1 {
+		params.PerPage = listZarazConfigHistoryDefaultPageSize
+	}
+
+	if params.Page < 1 {
+		params.Page = 1
+	}
+
+	var records []ZarazHistoryRecord
+	var lastResultInfo ResultInfo
+
+	for {
+		uri := buildURI(fmt.Sprintf("/zones/%s/settings/zaraz/v2/history", rc.Identifier), params)
+		res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+		if err != nil {
+			return []ZarazHistoryRecord{}, &ResultInfo{}, err
+		}
+		var listResponse ZarazConfigHistoryListResponse
+		err = json.Unmarshal(res, &listResponse)
+		if err != nil {
+			return []ZarazHistoryRecord{}, &ResultInfo{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+		}
+		records = append(records, listResponse.Result...)
+		lastResultInfo = listResponse.ResultInfo
+		params.ResultInfo = listResponse.ResultInfo.Next()
+		if params.ResultInfo.Done() || !autoPaginate {
+			break
+		}
+	}
+	return records, &lastResultInfo, nil
+}
+
+func (api *API) GetDefaultZarazConfig(ctx context.Context, rc *ResourceContainer) (ZarazConfigResponse, error) {
+	if rc.Identifier == "" {
+		return ZarazConfigResponse{}, ErrMissingZoneID
+	}
+
+	uri := fmt.Sprintf("/zones/%s/settings/zaraz/v2/default", rc.Identifier)
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return ZarazConfigResponse{}, err
+	}
+
+	var recordResp ZarazConfigResponse
+	err = json.Unmarshal(res, &recordResp)
+	if err != nil {
+		return ZarazConfigResponse{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return recordResp, nil
+}
+
+func (api *API) ExportZarazConfig(ctx context.Context, rc *ResourceContainer) error {
+	if rc.Identifier == "" {
+		return ErrMissingZoneID
+	}
+
+	uri := fmt.Sprintf("/zones/%s/settings/zaraz/v2/export", rc.Identifier)
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return err
+	}
+
+	var recordResp ZarazConfig
+	err = json.Unmarshal(res, &recordResp)
+	if err != nil {
+		return fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return nil
+}


Test patch:
diff --git a/zaraz_test.go b/zaraz_test.go
new file mode 100644
index 000000000..a59115e3e
--- /dev/null
+++ b/zaraz_test.go
@@ -0,0 +1,741 @@
+package cloudflare
+
+import (
+	"context"
+	"fmt"
+	"io"
+	"net/http"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+var trueValue bool = true
+var falseValue bool = false
+
+var expectedConfig ZarazConfig = ZarazConfig{
+	DebugKey:      "cheese",
+	ZarazVersion:  44,
+	DataLayer:     &trueValue,
+	Dlp:           []any{},
+	HistoryChange: &trueValue,
+	Settings: ZarazConfigSettings{
+		AutoInjectScript: &trueValue,
+	},
+	Tools: map[string]ZarazTool{
+		"PBQr": {
+			BlockingTriggers: []string{},
+			Enabled:          &trueValue,
+			DefaultFields:    map[string]any{},
+			Name:             "Custom HTML",
+			NeoEvents: []ZarazNeoEvent{
+				{
+					ActionType:       "event",
+					BlockingTriggers: []string{},
+					Data: map[string]any{
+						"__zaraz_setting_name": "pageview1",
+						"htmlCode":             "<script>console.log('pageview')</script>",
+					},
+					FiringTriggers: []string{"Pageview"},
+				},
+			},
+			Type:           ZarazToolComponent,
+			DefaultPurpose: "rJJC",
+			Component:      "html",
+			Permissions:    []string{"execute_unsafe_scripts"},
+			Settings:       map[string]any{},
+		},
+	},
+	Triggers: map[string]ZarazTrigger{
+		"Pageview": {
+			Name:         "Pageview",
+			Description:  "All page loads",
+			LoadRules:    []ZarazTriggerRule{{Match: "{{ client.__zarazTrack }}", Op: "EQUALS", Value: "Pageview"}},
+			ExcludeRules: []ZarazTriggerRule{},
+			ClientRules:  []any{},
+			System:       ZarazPageload,
+		},
+		"TFOl": {
+			Name:         "test",
+			Description:  "",
+			LoadRules:    []ZarazTriggerRule{{Id: "Kqsc", Match: "test", Op: "CONTAINS", Value: "test"}, {Id: "EDnV", Action: ZarazClickListener, Settings: ZarazRuleSettings{Selector: "test", Type: ZarazCSS}}},
+			ExcludeRules: []ZarazTriggerRule{},
+		},
+	},
+	Variables: map[string]ZarazVariable{
+		"jwIx": {
+			Name:  "test",
+			Type:  ZarazVarString,
+			Value: "sss",
+		},
+		"pAuL": {
+			Name: "test-worker-var",
+			Type: ZarazVarWorker,
+			Value: map[string]interface{}{
+				"escapedWorkerName": "worker-var-example",
+				"mutableId":         "m.zpt3q__WyW-61WM2qwgGoBl4Nxg-sfBsaMhu9NayjwU",
+				"workerTag":         "68aba570db9d4ec5b159624e2f7ad8bf",
+			},
+		},
+	},
+	Consent: ZarazConsent{
+		Enabled: &trueValue,
+		ButtonTextTranslations: ZarazButtonTextTranslations{
+			AcceptAll:        map[string]string{"en": "Accept ALL"},
+			ConfirmMyChoices: map[string]string{"en": "YES!"},
+			RejectAll:        map[string]string{"en": "Reject ALL"},
+		},
+		CompanyEmail:                          "email@example.com",
+		ConsentModalIntroHTMLWithTranslations: map[string]string{"en": "Lorem ipsum dolar set Amet?"},
+		CookieName:                            "zaraz-consent",
+		CustomCSS:                             ".test {\n    color: red;\n}",
+		CustomIntroDisclaimerDismissed:        &trueValue,
+		DefaultLanguage:                       "en",
+		HideModal:                             &falseValue,
+		PurposesWithTranslations: map[string]ZarazPurposeWithTranslations{
+			"rJJC": {
+				Description: map[string]string{"en": "Blah blah"},
+				Name:        map[string]string{"en": "Analytics"},
+				Order:       0,
+			},
+		},
+	},
+}
+
+func TestGetZarazConfig(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.Fprint(w, `{
+			"errors": [],
+			"messages": [],
+			"success": true,
+			"result": {
+				"consent": {
+				  "buttonTextTranslations": {
+					"accept_all": {
+					  "en": "Accept ALL"
+					},
+					"confirm_my_choices": {
+					  "en": "YES!"
+					},
+					"reject_all": {
+					  "en": "Reject ALL"
+					}
+				  },
+				  "companyEmail": "email@example.com",
+				  "consentModalIntroHTMLWithTranslations": {
+					"en": "Lorem ipsum dolar set Amet?"
+				  },
+				  "cookieName": "zaraz-consent",
+				  "customCSS": ".test {\n    color: red;\n}",
+				  "customIntroDisclaimerDismissed": true,
+				  "defaultLanguage": "en",
+				  "enabled": true,
+				  "hideModal": false,
+				  "purposesWithTranslations": {
+					"rJJC": {
+					  "description": {
+						"en": "Blah blah"
+					  },
+					  "name": {
+						"en": "Analytics"
+					  },
+					  "order": 0
+					}
+				  }
+				},
+				"dataLayer": true,
+				"debugKey": "cheese",
+				"dlp": [],
+				"historyChange": true,
+				"invalidKey": "cheese",
+				"settings": {
+				  "autoInjectScript": true
+				},
+				"tools": {
+				  "PBQr": {
+					"blockingTriggers": [],
+					"component": "html",
+					"defaultFields": {},
+					"defaultPurpose": "rJJC",
+					"enabled": true,
+					"mode": {
+					  "cloud": false,
+					  "ignoreSPA": true,
+					  "light": false,
+					  "sample": false,
+					  "segment": {
+						"end": 100,
+						"start": 0
+					  },
+					  "trigger": "pageload"
+					},
+					"name": "Custom HTML",
+					"neoEvents": [
+					  {
+						"actionType": "event",
+						"blockingTriggers": [],
+						"data": {
+						  "__zaraz_setting_name": "pageview1",
+						  "htmlCode": "<script>console.log('pageview')</script>"
+						},
+						"firingTriggers": [
+						  "Pageview"
+						]
+					  }
+					],
+					"permissions": [
+					  "execute_unsafe_scripts"
+					],
+					"settings": {},
+					"type": "component"
+				  }
+				},
+				"triggers": {
+				  "Pageview": {
+					"clientRules": [],
+					"description": "All page loads",
+					"excludeRules": [],
+					"loadRules": [
+					  {
+						"match": "{{ client.__zarazTrack }}",
+						"op": "EQUALS",
+						"value": "Pageview"
+					  }
+					],
+					"name": "Pageview",
+					"system": "pageload"
+				  },
+				  "TFOl": {
+					"description": "",
+					"excludeRules": [],
+					"loadRules": [
+					  {
+						"id": "Kqsc",
+						"match": "test",
+						"op": "CONTAINS",
+						"value": "test"
+					  },
+					  {
+						"action": "clickListener",
+						"id": "EDnV",
+						"settings": {
+						  "selector": "test",
+						  "type": "css",
+						  "waitForTags": 0
+						}
+					  }
+					],
+					"name": "test"
+				  }
+				},
+				"variables": {
+				  "jwIx": {
+					"name": "test",
+					"type": "string",
+					"value": "sss"
+				  },
+				  "pAuL": {
+					"name": "test-worker-var",
+					"type": "worker",
+					"value": {
+					  "escapedWorkerName": "worker-var-example",
+					  "mutableId": "m.zpt3q__WyW-61WM2qwgGoBl4Nxg-sfBsaMhu9NayjwU",
+					  "workerTag": "68aba570db9d4ec5b159624e2f7ad8bf"
+					}
+				  }
+				},
+				"zarazVersion": 44
+			  }
+		}`)
+	}
+
+	mux.HandleFunc("/zones/"+testZoneID+"/settings/zaraz/v2/config", handler)
+
+	actual, err := client.GetZarazConfig(context.Background(), ZoneIdentifier(testZoneID))
+	require.NoError(t, err)
+
+	assert.Equal(t, expectedConfig, actual.Result)
+}
+
+func TestUpdateZarazConfig(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.Fprint(w, `{
+			"errors": [],
+			"messages": [],
+			"success": true,
+			"result": {
+				"consent": {
+				  "buttonTextTranslations": {
+					"accept_all": {
+					  "en": "Accept ALL"
+					},
+					"confirm_my_choices": {
+					  "en": "YES!"
+					},
+					"reject_all": {
+					  "en": "Reject ALL"
+					}
+				  },
+				  "companyEmail": "email@example.com",
+				  "consentModalIntroHTMLWithTranslations": {
+					"en": "Lorem ipsum dolar set Amet?"
+				  },
+				  "cookieName": "zaraz-consent",
+				  "customCSS": ".test {\n    color: red;\n}",
+				  "customIntroDisclaimerDismissed": true,
+				  "defaultLanguage": "en",
+				  "enabled": true,
+				  "hideModal": false,
+				  "purposesWithTranslations": {
+					"rJJC": {
+					  "description": {
+						"en": "Blah blah"
+					  },
+					  "name": {
+						"en": "Analytics"
+					  },
+					  "order": 0
+					}
+				  }
+				},
+				"dataLayer": true,
+				"debugKey": "butter",
+				"dlp": [],
+				"historyChange": true,
+				"invalidKey": "cheese",
+				"settings": {
+				  "autoInjectScript": true
+				},
+				"tools": {
+				  "PBQr": {
+					"blockingTriggers": [],
+					"component": "html",
+					"defaultFields": {},
+					"defaultPurpose": "rJJC",
+					"enabled": true,
+					"mode": {
+					  "cloud": false,
+					  "ignoreSPA": true,
+					  "light": false,
+					  "sample": false,
+					  "segment": {
+						"end": 100,
+						"start": 0
+					  },
+					  "trigger": "pageload"
+					},
+					"name": "Custom HTML",
+					"neoEvents": [
+					  {
+						"actionType": "event",
+						"blockingTriggers": [],
+						"data": {
+						  "__zaraz_setting_name": "pageview1",
+						  "htmlCode": "<script>console.log('pageview')</script>"
+						},
+						"firingTriggers": [
+						  "Pageview"
+						]
+					  }
+					],
+					"permissions": [
+					  "execute_unsafe_scripts"
+					],
+					"settings": {},
+					"type": "component"
+				  }
+				},
+				"triggers": {
+				  "Pageview": {
+					"clientRules": [],
+					"description": "All page loads",
+					"excludeRules": [],
+					"loadRules": [
+					  {
+						"match": "{{ client.__zarazTrack }}",
+						"op": "EQUALS",
+						"value": "Pageview"
+					  }
+					],
+					"name": "Pageview",
+					"system": "pageload"
+				  },
+				  "TFOl": {
+					"description": "",
+					"excludeRules": [],
+					"loadRules": [
+					  {
+						"id": "Kqsc",
+						"match": "test",
+						"op": "CONTAINS",
+						"value": "test"
+					  },
+					  {
+						"action": "clickListener",
+						"id": "EDnV",
+						"settings": {
+						  "selector": "test",
+						  "type": "css",
+						  "waitForTags": 0
+						}
+					  }
+					],
+					"name": "test"
+				  }
+				},
+				"variables": {
+				  "jwIx": {
+					"name": "test",
+					"type": "string",
+					"value": "sss"
+				  },
+				  "pAuL": {
+					"name": "test-worker-var",
+					"type": "worker",
+					"value": {
+					  "escapedWorkerName": "worker-var-example",
+					  "mutableId": "m.zpt3q__WyW-61WM2qwgGoBl4Nxg-sfBsaMhu9NayjwU",
+					  "workerTag": "68aba570db9d4ec5b159624e2f7ad8bf"
+					}
+				  }
+				},
+				"zarazVersion": 44
+			  }
+		}`)
+	}
+
+	mux.HandleFunc("/zones/"+testZoneID+"/settings/zaraz/v2/config", handler)
+	payload := UpdateZarazConfigParams{
+		DebugKey:      "cheese",
+		ZarazVersion:  44,
+		DataLayer:     &trueValue,
+		Dlp:           []any{},
+		HistoryChange: &trueValue,
+		Settings: ZarazConfigSettings{
+			AutoInjectScript: &trueValue,
+		},
+		Tools: map[string]ZarazTool{
+			"PBQr": {
+				BlockingTriggers: []string{},
+				Enabled:          &trueValue,
+				DefaultFields:    map[string]any{},
+				Name:             "Custom HTML",
+				NeoEvents: []ZarazNeoEvent{
+					{
+						ActionType:       "event",
+						BlockingTriggers: []string{},
+						Data: map[string]any{
+							"__zaraz_setting_name": "pageview1",
+							"htmlCode":             "<script>console.log('pageview')</script>",
+						},
+						FiringTriggers: []string{"Pageview"},
+					},
+				},
+				Type:           ZarazToolComponent,
+				DefaultPurpose: "rJJC",
+				Component:      "html",
+				Permissions:    []string{"execute_unsafe_scripts"},
+				Settings:       map[string]any{},
+			},
+		},
+		Triggers: map[string]ZarazTrigger{
+			"Pageview": {
+				Name:         "Pageview",
+				Description:  "All page loads",
+				LoadRules:    []ZarazTriggerRule{{Match: "{{ client.__zarazTrack }}", Op: "EQUALS", Value: "Pageview"}},
+				ExcludeRules: []ZarazTriggerRule{},
+				ClientRules:  []any{},
+				System:       ZarazPageload,
+			},
+			"TFOl": {
+				Name:         "test",
+				Description:  "",
+				LoadRules:    []ZarazTriggerRule{{Id: "Kqsc", Match: "test", Op: "CONTAINS", Value: "test"}, {Id: "EDnV", Action: ZarazClickListener, Settings: ZarazRuleSettings{Selector: "test", Type: ZarazCSS}}},
+				ExcludeRules: []ZarazTriggerRule{},
+			},
+		},
+		Variables: map[string]ZarazVariable{
+			"jwIx": {
+				Name:  "test",
+				Type:  ZarazVarString,
+				Value: "sss",
+			},
+			"pAuL": {
+				Name: "test-worker-var",
+				Type: ZarazVarWorker,
+				Value: map[string]interface{}{
+					"escapedWorkerName": "worker-var-example",
+					"mutableId":         "m.zpt3q__WyW-61WM2qwgGoBl4Nxg-sfBsaMhu9NayjwU",
+					"workerTag":         "68aba570db9d4ec5b159624e2f7ad8bf",
+				},
+			},
+		},
+		Consent: ZarazConsent{
+			Enabled: &trueValue,
+			ButtonTextTranslations: ZarazButtonTextTranslations{
+				AcceptAll:        map[string]string{"en": "Accept ALL"},
+				ConfirmMyChoices: map[string]string{"en": "YES!"},
+				RejectAll:        map[string]string{"en": "Reject ALL"},
+			},
+			CompanyEmail:                          "email@example.com",
+			ConsentModalIntroHTMLWithTranslations: map[string]string{"en": "Lorem ipsum dolar set Amet?"},
+			CookieName:                            "zaraz-consent",
+			CustomCSS:                             ".test {\n    color: red;\n}",
+			CustomIntroDisclaimerDismissed:        &trueValue,
+			DefaultLanguage:                       "en",
+			HideModal:                             &falseValue,
+			PurposesWithTranslations: map[string]ZarazPurposeWithTranslations{
+				"rJJC": {
+					Description: map[string]string{"en": "Blah blah"},
+					Name:        map[string]string{"en": "Analytics"},
+					Order:       0,
+				},
+			},
+		},
+	}
+	payload.DebugKey = "butter" // Updating config
+	modifiedConfig := expectedConfig
+	modifiedConfig.DebugKey = "butter" // Updating config
+	expected := ZarazConfigResponse{
+		Result: modifiedConfig,
+	}
+
+	actual, err := client.UpdateZarazConfig(context.Background(), ZoneIdentifier(testZoneID), payload)
+	require.NoError(t, err)
+
+	assert.Equal(t, expected.Result, actual.Result)
+}
+
+func TestGetZarazWorkflow(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.Fprint(w, `{
+			"errors": [],
+			"messages": [],
+			"success": true,
+			"result": "realtime"
+		}`)
+	}
+
+	mux.HandleFunc("/zones/"+testZoneID+"/settings/zaraz/v2/workflow", handler)
+	want := ZarazWorkflowResponse{
+		Result: "realtime",
+		Response: Response{
+			Success:  true,
+			Messages: []ResponseInfo{},
+			Errors:   []ResponseInfo{},
+		},
+	}
+
+	actual, err := client.GetZarazWorkflow(context.Background(), ZoneIdentifier(testZoneID))
+
+	require.NoError(t, err)
+
+	assert.Equal(t, want, actual)
+}
+
+func TestUpdateZarazWorkflow(t *testing.T) {
+	setup()
+	defer teardown()
+
+	payload := UpdateZarazWorkflowParams{
+		Workflow: "realtime",
+	}
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method)
+		body, _ := io.ReadAll(r.Body)
+		bodyString := string(body)
+		assert.Equal(t, fmt.Sprintf("\"%s\"", payload.Workflow), bodyString)
+
+		w.Header().Set("Content-Type", "application/json")
+		fmt.Fprint(w, `{
+			"errors": [],
+			"messages": [],
+			"success": true,
+			"result": "realtime"
+		}`)
+	}
+
+	mux.HandleFunc("/zones/"+testZoneID+"/settings/zaraz/v2/workflow", handler)
+	want := ZarazWorkflowResponse{
+		Result: "realtime",
+		Response: Response{
+			Success:  true,
+			Messages: []ResponseInfo{},
+			Errors:   []ResponseInfo{},
+		},
+	}
+
+	actual, err := client.UpdateZarazWorkflow(context.Background(), ZoneIdentifier(testZoneID), payload)
+
+	require.NoError(t, err)
+
+	assert.Equal(t, want, actual)
+}
+
+func TestPublishZarazConfig(t *testing.T) {
+	setup()
+	defer teardown()
+
+	payload := PublishZarazConfigParams{
+		Description: "test description",
+	}
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method)
+		body, _ := io.ReadAll(r.Body)
+		bodyString := string(body)
+		assert.Equal(t, fmt.Sprintf("\"%s\"", payload.Description), bodyString)
+
+		w.Header().Set("Content-Type", "application/json")
+		fmt.Fprint(w, `{
+			"errors": [],
+			"messages": [],
+			"success": true,
+			"result": "Config has been published successfully"
+		}`)
+	}
+
+	mux.HandleFunc("/zones/"+testZoneID+"/settings/zaraz/v2/publish", handler)
+	want := ZarazPublishResponse{
+		Result: "Config has been published successfully",
+		Response: Response{
+			Success:  true,
+			Messages: []ResponseInfo{},
+			Errors:   []ResponseInfo{},
+		},
+	}
+
+	actual, err := client.PublishZarazConfig(context.Background(), ZoneIdentifier(testZoneID), payload)
+
+	require.NoError(t, err)
+
+	assert.Equal(t, want, actual)
+}
+
+func TestGetZarazConfigHistoryList(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.Fprint(w, `{
+			"result": [
+			  {
+				"createdAt": "2023-01-01T05:20:00Z",
+				"description": "test 1",
+				"id": 1005736,
+				"updatedAt": "2023-01-01T05:20:00Z",
+				"userId": "9ceddf6f117afe04c64716c83468d3a4"
+			  },
+			  {
+				"createdAt": "2023-01-01T05:20:00Z",
+				"description": "test 2",
+				"id": 1005735,
+				"updatedAt": "2023-01-01T05:20:00Z",
+				"userId": "9ceddf6f117afe04c64716c83468d3a4"
+			  }
+			],
+			"success": true,
+			"errors": [],
+			"messages": [],
+			"result_info": {
+			  "page": 1,
+			  "per_page": 2,
+			  "count": 2,
+			  "total_count": 8
+			}
+		  }`)
+	}
+
+	mux.HandleFunc("/zones/"+testZoneID+"/settings/zaraz/v2/history", handler)
+	createdAt, _ := time.Parse(time.RFC3339, "2023-01-01T05:20:00Z")
+	updatedAt, _ := time.Parse(time.RFC3339, "2023-01-01T05:20:00Z")
+	want := []ZarazHistoryRecord{
+		{
+			CreatedAt:   &createdAt,
+			Description: "test 1",
+			ID:          1005736,
+			UpdatedAt:   &updatedAt,
+			UserID:      "9ceddf6f117afe04c64716c83468d3a4",
+		},
+		{
+			CreatedAt:   &createdAt,
+			Description: "test 2",
+			ID:          1005735,
+			UpdatedAt:   &updatedAt,
+			UserID:      "9ceddf6f117afe04c64716c83468d3a4",
+		},
+	}
+
+	actual, _, err := client.ListZarazConfigHistory(context.Background(), ZoneIdentifier(testZoneID), ListZarazConfigHistoryParams{
+		ResultInfo: ResultInfo{
+			PerPage: 2,
+		},
+	})
+
+	require.NoError(t, err)
+
+	assert.Equal(t, want, actual)
+}
+
+func TestGetDefaultZarazConfig(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", "text/plain")
+		fmt.Fprint(w, `{
+			"someTestKeyThatRepsTheConfig": "test"
+		}`)
+	}
+
+	mux.HandleFunc("/zones/"+testZoneID+"/settings/zaraz/v2/default", handler)
+
+	_, err := client.GetDefaultZarazConfig(context.Background(), ZoneIdentifier(testZoneID))
+	require.NoError(t, err)
+}
+
+func TestExportZarazConfig(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", "text/plain")
+		fmt.Fprint(w, `{
+			"someTestKeyThatRepsTheConfig": "test"
+		}`)
+	}
+
+	mux.HandleFunc("/zones/"+testZoneID+"/settings/zaraz/v2/export", handler)
+
+	err := client.ExportZarazConfig(context.Background(), ZoneIdentifier(testZoneID))
+	require.NoError(t, err)
+}