From 1cd322e038308ef6dfd0d120f7b23b44c683ac19 Mon Sep 17 00:00:00 2001 From: Brad Date: Sun, 30 Jul 2023 11:37:24 -0500 Subject: [PATCH] add web analytics site support --- web_analytics.go | 393 +++++++++++++++++++++++++++++++++++++++ web_analytics_test.go | 424 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 817 insertions(+) create mode 100644 web_analytics.go create mode 100644 web_analytics_test.go diff --git a/web_analytics.go b/web_analytics.go new file mode 100644 index 000000000000..badff685d1c9 --- /dev/null +++ b/web_analytics.go @@ -0,0 +1,393 @@ +package cloudflare + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "net/url" + "strconv" + "time" +) + +var ( + ErrMissingWebAnalyticsSiteTag = errors.New("missing required web analytics site ID") + ErrMissingWebAnalyticsRulesetID = errors.New("missing required web analytics ruleset ID") + ErrMissingWebAnalyticsRuleID = errors.New("missing required web analytics rule ID") + ErrMissingWebAnalyticsSiteHost = errors.New("missing required web analytics host or zone tag") +) + +// WebAnalyticsSite describes a Web Analytics Site object. +type WebAnalyticsSite struct { + SiteTag string `json:"site_tag"` + SiteToken string `json:"site_token"` + Created time.Time `json:"created,omitempty"` + // Snippet is an encoded JS script to insert into your site HTML. + Snippet string `json:"snippet"` + // AutoInstall defines whether Cloudflare will inject the JS snippet automatically for orange-clouded sites. + AutoInstall bool `json:"auto_install"` + Ruleset WebAnalyticsRuleset `json:"ruleset"` + Rules []WebAnalyticsRule `json:"rules"` +} + +// WebAnalyticsRule describes a Web Analytics Rule object. +type WebAnalyticsRule struct { + ID string `json:"id,omitempty"` + Host string `json:"host"` + Paths []string `json:"paths"` + // Inclusive defines whether the rule includes or excludes the matched traffic from being measured in web analytics. + Inclusive bool `json:"inclusive"` + Created time.Time `json:"created,omitempty"` + // IsPaused defines whether the rule is paused (inactive) or not. + IsPaused bool `json:"is_paused"` + Priority int `json:"priority,omitempty"` +} + +// CreateWebAnalyticsRule describes the properties required to create or update a Web Analytics Rule object. +type CreateWebAnalyticsRule struct { + ID string `json:"id,omitempty"` + Host string `json:"host"` + Paths []string `json:"paths"` + // Inclusive defines whether the rule includes or excludes the matched traffic from being measured in web analytics. + Inclusive bool `json:"inclusive"` + IsPaused bool `json:"is_paused"` +} + +// WebAnalyticsRuleset describes a Web Analytics Ruleset object. +type WebAnalyticsRuleset struct { + ID string `json:"id"` + ZoneTag string `json:"zone_tag"` + ZoneName string `json:"zone_name"` + Enabled bool `json:"enabled"` +} + +// WebAnalyticsSiteResponse is the API response, containing a single WebAnalyticsSite. +type WebAnalyticsSiteResponse struct { + Response + Result WebAnalyticsSite `json:"result"` +} + +// WebAnalyticsSitesResponse is the API response, containing an array of WebAnalyticsSite. +type WebAnalyticsSitesResponse struct { + Response + ResultInfo ResultInfo `json:"result_info"` + Result []WebAnalyticsSite `json:"result"` +} + +// WebAnalyticsRuleResponse is the API response, containing a single WebAnalyticsRule. +type WebAnalyticsRuleResponse struct { + Response + Result WebAnalyticsRule `json:"result"` +} + +type WebAnalyticsRulesetRules struct { + Ruleset WebAnalyticsRuleset `json:"ruleset"` + Rules []WebAnalyticsRule `json:"rules"` +} + +// WebAnalyticsRulesResponse is the API response, containing a WebAnalyticsRuleset and array of WebAnalyticsRule. +type WebAnalyticsRulesResponse struct { + Response + Result WebAnalyticsRulesetRules `json:"result"` +} + +// WebAnalyticsIDResponse is the API response, containing a single ID. +type WebAnalyticsIDResponse struct { + Response + Result struct { + ID string `json:"id"` + } `json:"result"` +} + +// WebAnalyticsSiteTagResponse is the API response, containing a single ID. +type WebAnalyticsSiteTagResponse struct { + Response + Result struct { + SiteTag string `json:"site_tag"` + } `json:"result"` +} + +type CreateWebAnalyticsSiteParams struct { + // Host is the host to measure traffic for. + Host string `json:"host,omitempty"` + // ZoneTag is the zone tag to measure traffic for. + ZoneTag string `json:"zone_tag,omitempty"` + // AutoInstall defines whether Cloudflare will inject the JS snippet automatically for orange-clouded sites. + AutoInstall bool `json:"auto_install"` +} + +// CreateWebAnalyticsSite creates a new Web Analytics Site for an Account. +// +// API reference: https://api.cloudflare.com/#web-analytics-create-site +func (api *API) CreateWebAnalyticsSite(ctx context.Context, rc *ResourceContainer, params CreateWebAnalyticsSiteParams) (*WebAnalyticsSite, error) { + if params.Host == "" && params.ZoneTag == "" { + return nil, ErrMissingWebAnalyticsSiteHost + } + uri := fmt.Sprintf("/accounts/%s/rum/site_info", rc.Identifier) + res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params) + if err != nil { + return nil, err + } + var r WebAnalyticsSiteResponse + err = json.Unmarshal(res, &r) + if err != nil { + return nil, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + return &r.Result, nil +} + +type ListWebAnalyticsSitesParams struct { + // Page offset for pagination. + Page int + // Items per page limit for pagination. + PerPage int + // Property to order Sites by, "host" or "created". + OrderBy string +} + +// ListWebAnalyticsSites returns all Web Analytics Sites for an Account. +// +// API reference: https://api.cloudflare.com/#web-analytics-list-sites +func (api *API) ListWebAnalyticsSites(ctx context.Context, rc *ResourceContainer, params ListWebAnalyticsSitesParams) ([]WebAnalyticsSite, *ResultInfo, error) { + v := url.Values{} + if params.Page > 0 { + v.Set("page", strconv.Itoa(params.Page)) + } + if params.PerPage > 0 { + v.Set("per_page", strconv.Itoa(params.PerPage)) + } + if params.OrderBy != "" { + v.Set("order_by", params.OrderBy) + } + uri := fmt.Sprintf("/accounts/%s/rum/site_info/list?"+v.Encode(), rc.Identifier) + + res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil) + if err != nil { + return nil, nil, err + } + var r WebAnalyticsSitesResponse + err = json.Unmarshal(res, &r) + if err != nil { + return nil, nil, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + return r.Result, &r.ResultInfo, nil +} + +type WebAnalyticsSiteParams struct { + SiteTag string +} + +// WebAnalyticsSite fetches detail about one Web Analytics Site for an Account. +// +// API reference: https://api.cloudflare.com/#web-analytics-get-site +func (api *API) WebAnalyticsSite(ctx context.Context, rc *ResourceContainer, params WebAnalyticsSiteParams) (*WebAnalyticsSite, error) { + if params.SiteTag == "" { + return nil, ErrMissingWebAnalyticsSiteTag + } + uri := fmt.Sprintf("/accounts/%s/rum/site_info/%s", rc.Identifier, params.SiteTag) + res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil) + if err != nil { + return nil, err + } + var r WebAnalyticsSiteResponse + err = json.Unmarshal(res, &r) + if err != nil { + return nil, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + return &r.Result, nil +} + +type UpdateWebAnalyticsSiteParams struct { + SiteTag string `json:"-"` + // Host is the host to measure traffic for. + Host string `json:"host,omitempty"` + // ZoneTag is the zone tag to measure traffic for. + ZoneTag string `json:"zone_tag,omitempty"` + // AutoInstall defines whether Cloudflare will inject the JS snippet automatically for orange-clouded sites. + AutoInstall bool `json:"auto_install"` +} + +// UpdateWebAnalyticsSite lets you replace a Web Analytics Site for an Account. +// +// API reference: https://api.cloudflare.com/#web-analytics-update-site +func (api *API) UpdateWebAnalyticsSite(ctx context.Context, rc *ResourceContainer, params UpdateWebAnalyticsSiteParams) (*WebAnalyticsSite, error) { + if params.SiteTag == "" { + return nil, ErrMissingWebAnalyticsSiteTag + } + uri := fmt.Sprintf("/accounts/%s/rum/site_info/%s", rc.Identifier, params.SiteTag) + res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params) + if err != nil { + return nil, err + } + var r WebAnalyticsSiteResponse + err = json.Unmarshal(res, &r) + if err != nil { + return nil, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + return &r.Result, nil +} + +type DeleteWebAnalyticsSiteParams struct { + SiteTag string +} + +// DeleteWebAnalyticsSite deletes a Web Analytics Site for an Account. +// +// API reference: https://api.cloudflare.com/#web-analytics-delete-site +func (api *API) DeleteWebAnalyticsSite(ctx context.Context, rc *ResourceContainer, params DeleteWebAnalyticsSiteParams) (*string, error) { + if params.SiteTag == "" { + return nil, ErrMissingWebAnalyticsSiteTag + } + uri := fmt.Sprintf("/accounts/%s/rum/site_info/%s", rc.Identifier, params.SiteTag) + res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil) + if err != nil { + return nil, err + } + var r WebAnalyticsSiteResponse + err = json.Unmarshal(res, &r) + if err != nil { + return nil, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + return &r.Result.SiteTag, nil +} + +type CreateWebAnalyticsRuleParams struct { + RulesetID string + Rule CreateWebAnalyticsRule +} + +// CreateWebAnalyticsRule creates a new Web Analytics Rule for a WebAnalyticsRuleset. +// +// API reference: https://api.cloudflare.com/#web-analytics-create-rule +func (api *API) CreateWebAnalyticsRule(ctx context.Context, rc *ResourceContainer, params CreateWebAnalyticsRuleParams) (*WebAnalyticsRule, error) { + if params.RulesetID == "" { + return nil, ErrMissingWebAnalyticsRulesetID + } + uri := fmt.Sprintf("/accounts/%s/rum/v2/%s/rule", rc.Identifier, params.RulesetID) + res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params.Rule) + if err != nil { + return nil, err + } + var r WebAnalyticsRuleResponse + err = json.Unmarshal(res, &r) + if err != nil { + return nil, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + return &r.Result, nil +} + +type ListWebAnalyticsRulesParams struct { + RulesetID string +} + +// ListWebAnalyticsRules fetches all Web Analytics Rules for a WebAnalyticsRuleset. +// +// API reference: https://api.cloudflare.com/#web-analytics-list-rules +func (api *API) ListWebAnalyticsRules(ctx context.Context, rc *ResourceContainer, params ListWebAnalyticsRulesParams) (*WebAnalyticsRulesetRules, error) { + if params.RulesetID == "" { + return nil, ErrMissingWebAnalyticsRulesetID + } + uri := fmt.Sprintf("/accounts/%s/rum/v2/%s/rules", rc.Identifier, params.RulesetID) + res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil) + if err != nil { + return nil, err + } + var r WebAnalyticsRulesResponse + err = json.Unmarshal(res, &r) + if err != nil { + return nil, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + return &r.Result, nil +} + +type DeleteWebAnalyticsRuleParams struct { + RulesetID string + RuleID string +} + +// DeleteWebAnalyticsRule deletes a Web Analytics Rule for a WebAnalyticsRuleset. +// +// API reference: https://api.cloudflare.com/#web-analytics-delete-rule +func (api *API) DeleteWebAnalyticsRule(ctx context.Context, rc *ResourceContainer, params DeleteWebAnalyticsRuleParams) (*string, error) { + if params.RulesetID == "" { + return nil, ErrMissingWebAnalyticsRulesetID + } + if params.RuleID == "" { + return nil, ErrMissingWebAnalyticsRuleID + } + uri := fmt.Sprintf("/accounts/%s/rum/v2/%s/rule/%s", rc.Identifier, params.RulesetID, params.RuleID) + res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil) + if err != nil { + return nil, err + } + var r WebAnalyticsIDResponse + err = json.Unmarshal(res, &r) + if err != nil { + return nil, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + return &r.Result.ID, nil +} + +type UpdateWebAnalyticsRuleParams struct { + RulesetID string + RuleID string + Rule CreateWebAnalyticsRule +} + +// UpdateWebAnalyticsRule lets you replace a Web Analytics Rule for a WebAnalyticsRuleset. +// +// API reference: https://api.cloudflare.com/#web-analytics-update-rule +func (api *API) UpdateWebAnalyticsRule(ctx context.Context, rc *ResourceContainer, params UpdateWebAnalyticsRuleParams) (*WebAnalyticsRule, error) { + if params.RulesetID == "" { + return nil, ErrMissingWebAnalyticsRulesetID + } + if params.RuleID == "" { + return nil, ErrMissingWebAnalyticsRuleID + } + uri := fmt.Sprintf("/accounts/%s/rum/v2/%s/rule/%s", rc.Identifier, params.RulesetID, params.RuleID) + res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params.Rule) + if err != nil { + return nil, err + } + var r WebAnalyticsRuleResponse + err = json.Unmarshal(res, &r) + if err != nil { + return nil, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + return &r.Result, nil +} + +type ModifyWebAnalyticsRulesParams struct { + RulesetID string + // Rules is a list of rules to create if no id is specified, or modify if an existing id is specified. + Rules []CreateWebAnalyticsRule + // DeleteRules is a list of rule ids to be deleted + DeleteRules []string +} + +// ModifyWebAnalyticsRules lets you create, updated, or delete multiple Web Analytics Rule for a WebAnalyticsRuleset. +// +// API reference: https://api.cloudflare.com/#web-analytics-modify-rules +func (api *API) ModifyWebAnalyticsRules(ctx context.Context, rc *ResourceContainer, params ModifyWebAnalyticsRulesParams) (*WebAnalyticsRulesetRules, error) { + if params.RulesetID == "" { + return nil, ErrMissingWebAnalyticsRulesetID + } + uri := fmt.Sprintf("/accounts/%s/rum/v2/%s/rules", rc.Identifier, params.RulesetID) + res, err := api.makeRequestContext(ctx, http.MethodPost, uri, struct { + Rules []CreateWebAnalyticsRule `json:"rules"` + DeleteRules []string `json:"delete_rules"` + }{ + Rules: params.Rules, + DeleteRules: params.DeleteRules, + }) + if err != nil { + return nil, err + } + var r WebAnalyticsRulesResponse + err = json.Unmarshal(res, &r) + if err != nil { + return nil, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + return &r.Result, nil +} diff --git a/web_analytics_test.go b/web_analytics_test.go new file mode 100644 index 000000000000..3b3a5f1f3f33 --- /dev/null +++ b/web_analytics_test.go @@ -0,0 +1,424 @@ +package cloudflare + +import ( + "context" + "fmt" + "io" + "log" + "net/http" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +var siteTag = "46c32e0ea0e85e90aa1a6df4596b831e" +var siteToken = "75300e6c2c5648d983fcef2a6c03d14e" +var rulesetID = "2e8804e9-674f-4652-94a4-1c664d0d6764" +var ruleID = "3caf59c9-eda3-4f99-a4a3-ee5fc2358a78" + +// var snippetFormat = `\u003c!-- Cloudflare Web Analytics --\u003e\u003cscript defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{\"token\": \"%s\"}'\u003e\u003c/script\u003e\u003c!-- End Cloudflare Web Analytics --\u003e` +var snippetFormat = `%s` +var createdTimestamp = time.Now().UTC() +var siteJSON = fmt.Sprintf(` +{ + "site_tag": "%s", + "site_token": "%s", + "created": "%s", + "snippet": "%s", + "auto_install": true, + "ruleset": { + "zone_tag": "%s", + "zone_name": "example.com", + "enabled": true, + "id": "%s" + }, + "rules": [ + { + "host": "example.com", + "paths": [ + "*" + ], + "inclusive": true, + "created": "%s", + "is_paused": false, + "priority": 1000, + "id": "%s" + } + ] +} +`, siteTag, siteToken, createdTimestamp.Format(time.RFC3339Nano), fmt.Sprintf(snippetFormat, siteToken), testZoneID, rulesetID, createdTimestamp.Format(time.RFC3339Nano), ruleID) + +var rulesetJSON = fmt.Sprintf(` +{ + "id": "%s", + "zone_tag": "%s", + "zone_name": "%s", + "enabled": true +} +`, rulesetID, testZoneID, "example.com") + +var ruleJSON = fmt.Sprintf(` +{ + "id": "%s", + "host": "example.com", + "paths": [ + "*" + ], + "inclusive": true, + "created": "%s", + "is_paused": false, + "priority": 1000 +} +`, ruleID, createdTimestamp.Format(time.RFC3339Nano)) + +var site = WebAnalyticsSite{ + SiteTag: siteTag, + SiteToken: siteToken, + AutoInstall: true, + Snippet: fmt.Sprintf(snippetFormat, siteToken), + Ruleset: ruleset, + Rules: []WebAnalyticsRule{ + rule, + }, + Created: createdTimestamp.UTC(), +} + +var ruleset = WebAnalyticsRuleset{ + ID: rulesetID, + ZoneTag: testZoneID, + ZoneName: "example.com", + Enabled: true, +} + +var rule = WebAnalyticsRule{ + ID: ruleID, + Host: "example.com", + Paths: []string{ + "*", + }, + Inclusive: true, + Created: createdTimestamp.UTC(), + IsPaused: false, + Priority: 1000, +} + +func TestListWebAnalyticsSites(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) + assert.Equal(t, "1", r.URL.Query().Get("page")) + assert.Equal(t, "10", r.URL.Query().Get("per_page")) + assert.Equal(t, "host", r.URL.Query().Get("order_by")) + w.Header().Set("content-type", "application/json") + fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result": [ + %s + ], + "result_info": { + "page": 1, + "per_page": 10, + "count": 1, + "total_count": 1, + "total_pages": 1 + } + } + `, siteJSON) + } + mux.HandleFunc("/accounts/"+testAccountID+"/rum/site_info/list", handler) + want := []WebAnalyticsSite{site} + actual, resultInfo, err := client.ListWebAnalyticsSites(context.Background(), AccountIdentifier(testAccountID), ListWebAnalyticsSitesParams{ + Page: 1, + PerPage: 10, + OrderBy: "host", + }) + if assert.NoError(t, err) { + assert.Equal(t, want, actual) + assert.Equal(t, &ResultInfo{ + Page: 1, + PerPage: 10, + TotalPages: 1, + Count: 1, + Total: 1, + }, resultInfo) + } +} + +func TestWebAnalyticsSite(t *testing.T) { + setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) + w.Header().Set("content-type", "application/json") + fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result": %s + } + `, siteJSON) + } + mux.HandleFunc("/accounts/"+testAccountID+"/rum/site_info/"+siteTag, handler) + want := site + actual, err := client.WebAnalyticsSite(context.Background(), AccountIdentifier(testAccountID), WebAnalyticsSiteParams{ + SiteTag: siteTag, + }) + if assert.NoError(t, err) { + assert.Equal(t, &want, actual) + } +} + +func TestCreateWebAnalyticsSite(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") + fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result": %s + } + `, siteJSON) + } + mux.HandleFunc("/accounts/"+testAccountID+"/rum/site_info", handler) + want := site + actual, err := client.CreateWebAnalyticsSite(context.Background(), AccountIdentifier(testAccountID), CreateWebAnalyticsSiteParams{ + Host: "example.com", + AutoInstall: true, + }) + if assert.NoError(t, err) { + assert.Equal(t, &want, actual) + } +} + +func TestUpdateWebAnalyticsSite(t *testing.T) { + setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) + w.Header().Set("content-type", "application/json") + fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result": %s + } + `, siteJSON) + } + mux.HandleFunc("/accounts/"+testAccountID+"/rum/site_info/"+siteTag, handler) + want := site + actual, err := client.UpdateWebAnalyticsSite(context.Background(), AccountIdentifier(testAccountID), UpdateWebAnalyticsSiteParams{ + SiteTag: site.SiteTag, + Host: "example.com", + AutoInstall: true, + }) + if assert.NoError(t, err) { + assert.Equal(t, &want, actual) + } +} + +func TestDeleteWebAnalyticsSite(t *testing.T) { + setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodDelete, r.Method, "Expected method 'GET', got %s", r.Method) + w.Header().Set("content-type", "application/json") + fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result": { + "site_tag": "%s" + } + } + `, siteTag) + } + mux.HandleFunc("/accounts/"+testAccountID+"/rum/site_info/"+siteTag, handler) + want := siteTag + actual, err := client.DeleteWebAnalyticsSite(context.Background(), AccountIdentifier(testAccountID), DeleteWebAnalyticsSiteParams{ + SiteTag: siteTag, + }) + if assert.NoError(t, err) { + assert.Equal(t, &want, actual) + } +} + +func TestListWebAnalyticsRules(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": { + "ruleset": %s, + "rules": [ + %s + ] + } + } + `, rulesetJSON, ruleJSON) + } + mux.HandleFunc("/accounts/"+testAccountID+"/rum/v2/"+rulesetID+"/rules", handler) + want := WebAnalyticsRulesetRules{ + Ruleset: ruleset, + Rules: []WebAnalyticsRule{rule}, + } + actual, err := client.ListWebAnalyticsRules(context.Background(), AccountIdentifier(testAccountID), ListWebAnalyticsRulesParams{ + RulesetID: rulesetID, + }) + if assert.NoError(t, err) { + assert.Equal(t, &want, actual) + } +} + +func TestCreateWebAnalyticsRule(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") + fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result": %s + } + `, ruleJSON) + } + mux.HandleFunc("/accounts/"+testAccountID+"/rum/v2/"+rulesetID+"/rule", handler) + want := rule + actual, err := client.CreateWebAnalyticsRule(context.Background(), AccountIdentifier(testAccountID), CreateWebAnalyticsRuleParams{ + RulesetID: rulesetID, + Rule: CreateWebAnalyticsRule{ + Host: "example.com", + Paths: []string{"*"}, + Inclusive: true, + IsPaused: false, + }, + }) + if assert.NoError(t, err) { + assert.Equal(t, &want, actual) + } +} + +func TestUpdateWebAnalyticsRule(t *testing.T) { + setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) + w.Header().Set("content-type", "application/json") + fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result": %s + } + `, ruleJSON) + } + mux.HandleFunc("/accounts/"+testAccountID+"/rum/v2/"+rulesetID+"/rule/"+ruleID, handler) + want := rule + actual, err := client.UpdateWebAnalyticsRule(context.Background(), AccountIdentifier(testAccountID), UpdateWebAnalyticsRuleParams{ + RulesetID: rulesetID, + RuleID: ruleID, + Rule: CreateWebAnalyticsRule{ + Host: "example.com", + Paths: []string{"*"}, + Inclusive: true, + IsPaused: false, + }, + }) + if assert.NoError(t, err) { + assert.Equal(t, &want, actual) + } +} + +func TestDeleteWebAnalyticsRule(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": [], + "result": { + "id": "%s" + } + } + `, ruleID) + } + mux.HandleFunc("/accounts/"+testAccountID+"/rum/v2/"+rulesetID+"/rule/"+ruleID, handler) + want := ruleID + actual, err := client.DeleteWebAnalyticsRule(context.Background(), AccountIdentifier(testAccountID), DeleteWebAnalyticsRuleParams{ + RulesetID: rulesetID, + RuleID: ruleID, + }) + if assert.NoError(t, err) { + assert.Equal(t, &want, actual) + } +} + +func TestModifyWebAnalyticsRules(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) + b, _ := io.ReadAll(r.Body) + log.Println(string(b)) + w.Header().Set("content-type", "application/json") + fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result": { + "ruleset": %s, + "rules": [ + %s + ] + } + } + `, rulesetJSON, ruleJSON) + } + mux.HandleFunc("/accounts/"+testAccountID+"/rum/v2/"+rulesetID+"/rules", handler) + want := WebAnalyticsRulesetRules{ + Ruleset: ruleset, + Rules: []WebAnalyticsRule{rule}, + } + actual, err := client.ModifyWebAnalyticsRules(context.Background(), AccountIdentifier(testAccountID), ModifyWebAnalyticsRulesParams{ + RulesetID: rulesetID, + Rules: []CreateWebAnalyticsRule{ + { + ID: ruleID, + Host: "example.com", + Paths: []string{"/updated"}, + Inclusive: false, + IsPaused: false, + }, + }, + DeleteRules: []string{ruleID}, + }) + if assert.NoError(t, err) { + assert.Equal(t, &want, actual) + } +}