├── .gitignore ├── conv.go ├── example └── main.go ├── type-pageview.go ├── type-screenview.go ├── type-social.go ├── LICENSE ├── type-exception.go ├── type-event.go ├── client.go ├── type-item.go ├── type-transaction.go ├── README.md ├── type-timing.go ├── generate └── protocol.go └── type-client.go /.gitignore: -------------------------------------------------------------------------------- 1 | example/tracker-id.txt -------------------------------------------------------------------------------- /conv.go: -------------------------------------------------------------------------------- 1 | package ga 2 | 3 | import "fmt" 4 | 5 | func bool2str(val bool) string { 6 | if val { 7 | return "1" 8 | } else { 9 | return "0" 10 | } 11 | } 12 | 13 | func int2str(val int64) string { 14 | return fmt.Sprintf("%d", val) 15 | } 16 | 17 | func float2str(val float64) string { 18 | return fmt.Sprintf("%.6f", val) 19 | } 20 | -------------------------------------------------------------------------------- /example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/jpillora/go-ogle-analytics" 4 | 5 | func main() { 6 | client, err := ga.NewClient("UA-30305960-4") 7 | if err != nil { 8 | panic(err) 9 | } 10 | 11 | err = client.Send(ga.NewEvent("Foo", "Bar").Label("Bazz")) 12 | if err != nil { 13 | panic(err) 14 | } 15 | 16 | println("Event fired!") 17 | } 18 | -------------------------------------------------------------------------------- /type-pageview.go: -------------------------------------------------------------------------------- 1 | package ga 2 | 3 | import "net/url" 4 | 5 | //WARNING: This file was generated. Do not edit. 6 | 7 | //Pageview Hit Type 8 | type Pageview struct { 9 | } 10 | 11 | // NewPageview creates a new Pageview Hit Type. 12 | func NewPageview() *Pageview { 13 | h := &Pageview{} 14 | return h 15 | } 16 | 17 | func (h *Pageview) addFields(v url.Values) error { 18 | return nil 19 | } 20 | 21 | func (h *Pageview) Copy() *Pageview { 22 | c := *h 23 | return &c 24 | } 25 | -------------------------------------------------------------------------------- /type-screenview.go: -------------------------------------------------------------------------------- 1 | package ga 2 | 3 | import "net/url" 4 | 5 | //WARNING: This file was generated. Do not edit. 6 | 7 | //Screenview Hit Type 8 | type Screenview struct { 9 | } 10 | 11 | // NewScreenview creates a new Screenview Hit Type. 12 | func NewScreenview() *Screenview { 13 | h := &Screenview{} 14 | return h 15 | } 16 | 17 | func (h *Screenview) addFields(v url.Values) error { 18 | return nil 19 | } 20 | 21 | func (h *Screenview) Copy() *Screenview { 22 | c := *h 23 | return &c 24 | } 25 | -------------------------------------------------------------------------------- /type-social.go: -------------------------------------------------------------------------------- 1 | package ga 2 | 3 | import "net/url" 4 | 5 | //WARNING: This file was generated. Do not edit. 6 | 7 | //Social Hit Type 8 | type Social struct { 9 | network string 10 | action string 11 | actionTarget string 12 | } 13 | 14 | // NewSocial creates a new Social Hit Type. 15 | // Specifies the social network, for example Facebook or Google 16 | // Plus. 17 | // Specifies the social interaction action. For example on 18 | // Google Plus when a user clicks the +1 button, the social 19 | // action is 'plus'. 20 | // Specifies the target of a social interaction. This value 21 | // is typically a URL but can be any text. 22 | func NewSocial(network string, action string, actionTarget string) *Social { 23 | h := &Social{ 24 | network: network, 25 | action: action, 26 | actionTarget: actionTarget, 27 | } 28 | return h 29 | } 30 | 31 | func (h *Social) addFields(v url.Values) error { 32 | v.Add("sn", h.network) 33 | v.Add("sa", h.action) 34 | v.Add("st", h.actionTarget) 35 | return nil 36 | } 37 | 38 | func (h *Social) Copy() *Social { 39 | c := *h 40 | return &c 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright © 2015 dev@jpillora.com, Google Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /type-exception.go: -------------------------------------------------------------------------------- 1 | package ga 2 | 3 | import "net/url" 4 | 5 | //WARNING: This file was generated. Do not edit. 6 | 7 | //Exception Hit Type 8 | type Exception struct { 9 | description string 10 | descriptionSet bool 11 | isExceptionFatal bool 12 | isExceptionFatalSet bool 13 | } 14 | 15 | // NewException creates a new Exception Hit Type. 16 | 17 | func NewException() *Exception { 18 | h := &Exception{} 19 | return h 20 | } 21 | 22 | func (h *Exception) addFields(v url.Values) error { 23 | if h.descriptionSet { 24 | v.Add("exd", h.description) 25 | } 26 | if h.isExceptionFatalSet { 27 | v.Add("exf", bool2str(h.isExceptionFatal)) 28 | } 29 | return nil 30 | } 31 | 32 | // Specifies the description of an exception. 33 | func (h *Exception) Description(description string) *Exception { 34 | h.description = description 35 | h.descriptionSet = true 36 | return h 37 | } 38 | 39 | // Specifies whether the exception was fatal. 40 | func (h *Exception) IsExceptionFatal(isExceptionFatal bool) *Exception { 41 | h.isExceptionFatal = isExceptionFatal 42 | h.isExceptionFatalSet = true 43 | return h 44 | } 45 | 46 | func (h *Exception) Copy() *Exception { 47 | c := *h 48 | return &c 49 | } 50 | -------------------------------------------------------------------------------- /type-event.go: -------------------------------------------------------------------------------- 1 | package ga 2 | 3 | import "net/url" 4 | 5 | //WARNING: This file was generated. Do not edit. 6 | 7 | //Event Hit Type 8 | type Event struct { 9 | category string 10 | action string 11 | label string 12 | labelSet bool 13 | value int64 14 | valueSet bool 15 | } 16 | 17 | // NewEvent creates a new Event Hit Type. 18 | // Specifies the event category. 19 | // Specifies the event action. 20 | 21 | func NewEvent(category string, action string) *Event { 22 | h := &Event{ 23 | category: category, 24 | action: action, 25 | } 26 | return h 27 | } 28 | 29 | func (h *Event) addFields(v url.Values) error { 30 | v.Add("ec", h.category) 31 | v.Add("ea", h.action) 32 | if h.labelSet { 33 | v.Add("el", h.label) 34 | } 35 | if h.valueSet { 36 | v.Add("ev", int2str(h.value)) 37 | } 38 | return nil 39 | } 40 | 41 | // Specifies the event label. 42 | func (h *Event) Label(label string) *Event { 43 | h.label = label 44 | h.labelSet = true 45 | return h 46 | } 47 | 48 | // Specifies the event value. Values must be non-negative. 49 | func (h *Event) Value(value int64) *Event { 50 | h.value = value 51 | h.valueSet = true 52 | return h 53 | } 54 | 55 | func (h *Event) Copy() *Event { 56 | c := *h 57 | return &c 58 | } 59 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | //go:generate go run generate/protocol.go 2 | 3 | package ga 4 | 5 | import ( 6 | "bytes" 7 | "fmt" 8 | "net/http" 9 | "net/url" 10 | "regexp" 11 | ) 12 | 13 | var trackingIDMatcher = regexp.MustCompile(`^UA-\d+-\d+$`) 14 | 15 | func NewClient(trackingID string) (*Client, error) { 16 | if !trackingIDMatcher.MatchString(trackingID) { 17 | return nil, fmt.Errorf("Invalid Tracking ID: %s", trackingID) 18 | } 19 | return &Client{ 20 | UseTLS: true, 21 | HttpClient: http.DefaultClient, 22 | protocolVersion: "1", 23 | protocolVersionSet: true, 24 | trackingID: trackingID, 25 | clientID: "go-ga", 26 | clientIDSet: true, 27 | }, nil 28 | } 29 | 30 | type hitType interface { 31 | addFields(url.Values) error 32 | } 33 | 34 | func (c *Client) Send(h hitType) error { 35 | 36 | cpy := c.Copy() 37 | 38 | v := url.Values{} 39 | 40 | cpy.setType(h) 41 | 42 | err := cpy.addFields(v) 43 | if err != nil { 44 | return err 45 | } 46 | 47 | err = h.addFields(v) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | url := "" 53 | if cpy.UseTLS { 54 | url = "https://www.google-analytics.com/collect" 55 | } else { 56 | url = "http://ssl.google-analytics.com/collect" 57 | } 58 | 59 | str := v.Encode() 60 | buf := bytes.NewBufferString(str) 61 | 62 | resp, err := c.HttpClient.Post(url, "application/x-www-form-urlencoded", buf) 63 | if err != nil { 64 | return err 65 | } 66 | 67 | defer resp.Body.Close() 68 | 69 | if resp.StatusCode/100 != 2 { 70 | return fmt.Errorf("Rejected by Google with code %d", resp.StatusCode) 71 | } 72 | 73 | // fmt.Printf("POST %s => %d\n", str, resp.StatusCode) 74 | 75 | return nil 76 | } 77 | -------------------------------------------------------------------------------- /type-item.go: -------------------------------------------------------------------------------- 1 | package ga 2 | 3 | import "net/url" 4 | 5 | //WARNING: This file was generated. Do not edit. 6 | 7 | //Item Hit Type 8 | type Item struct { 9 | iD string 10 | name string 11 | price float64 12 | priceSet bool 13 | quantity int64 14 | quantitySet bool 15 | code string 16 | codeSet bool 17 | category string 18 | categorySet bool 19 | currencyCode string 20 | currencyCodeSet bool 21 | } 22 | 23 | // NewItem creates a new Item Hit Type. 24 | // A unique identifier for the transaction. This value should 25 | // be the same for both the Transaction hit and Items hits 26 | // associated to the particular transaction. 27 | // Specifies the item name. 28 | 29 | func NewItem(iD string, name string) *Item { 30 | h := &Item{ 31 | iD: iD, 32 | name: name, 33 | } 34 | return h 35 | } 36 | 37 | func (h *Item) addFields(v url.Values) error { 38 | v.Add("ti", h.iD) 39 | v.Add("in", h.name) 40 | if h.priceSet { 41 | v.Add("ip", float2str(h.price)) 42 | } 43 | if h.quantitySet { 44 | v.Add("iq", int2str(h.quantity)) 45 | } 46 | if h.codeSet { 47 | v.Add("ic", h.code) 48 | } 49 | if h.categorySet { 50 | v.Add("iv", h.category) 51 | } 52 | if h.currencyCodeSet { 53 | v.Add("cu", h.currencyCode) 54 | } 55 | return nil 56 | } 57 | 58 | // Specifies the price for a single item / unit. 59 | func (h *Item) Price(price float64) *Item { 60 | h.price = price 61 | h.priceSet = true 62 | return h 63 | } 64 | 65 | // Specifies the number of items purchased. 66 | func (h *Item) Quantity(quantity int64) *Item { 67 | h.quantity = quantity 68 | h.quantitySet = true 69 | return h 70 | } 71 | 72 | // Specifies the SKU or item code. 73 | func (h *Item) Code(code string) *Item { 74 | h.code = code 75 | h.codeSet = true 76 | return h 77 | } 78 | 79 | // Specifies the category that the item belongs to. 80 | func (h *Item) Category(category string) *Item { 81 | h.category = category 82 | h.categorySet = true 83 | return h 84 | } 85 | 86 | // When present indicates the local currency for all transaction 87 | // currency values. Value should be a valid ISO 4217 currency 88 | // code. 89 | func (h *Item) CurrencyCode(currencyCode string) *Item { 90 | h.currencyCode = currencyCode 91 | h.currencyCodeSet = true 92 | return h 93 | } 94 | 95 | func (h *Item) Copy() *Item { 96 | c := *h 97 | return &c 98 | } 99 | -------------------------------------------------------------------------------- /type-transaction.go: -------------------------------------------------------------------------------- 1 | package ga 2 | 3 | import "net/url" 4 | 5 | //WARNING: This file was generated. Do not edit. 6 | 7 | //Transaction Hit Type 8 | type Transaction struct { 9 | iD string 10 | affiliation string 11 | affiliationSet bool 12 | revenue float64 13 | revenueSet bool 14 | shipping float64 15 | shippingSet bool 16 | tax float64 17 | taxSet bool 18 | currencyCode string 19 | currencyCodeSet bool 20 | } 21 | 22 | // NewTransaction creates a new Transaction Hit Type. 23 | // A unique identifier for the transaction. This value should 24 | // be the same for both the Transaction hit and Items hits 25 | // associated to the particular transaction. 26 | 27 | func NewTransaction(iD string) *Transaction { 28 | h := &Transaction{ 29 | iD: iD, 30 | } 31 | return h 32 | } 33 | 34 | func (h *Transaction) addFields(v url.Values) error { 35 | v.Add("ti", h.iD) 36 | if h.affiliationSet { 37 | v.Add("ta", h.affiliation) 38 | } 39 | if h.revenueSet { 40 | v.Add("tr", float2str(h.revenue)) 41 | } 42 | if h.shippingSet { 43 | v.Add("ts", float2str(h.shipping)) 44 | } 45 | if h.taxSet { 46 | v.Add("tt", float2str(h.tax)) 47 | } 48 | if h.currencyCodeSet { 49 | v.Add("cu", h.currencyCode) 50 | } 51 | return nil 52 | } 53 | 54 | // Specifies the affiliation or store name. 55 | func (h *Transaction) Affiliation(affiliation string) *Transaction { 56 | h.affiliation = affiliation 57 | h.affiliationSet = true 58 | return h 59 | } 60 | 61 | // Specifies the total revenue associated with the transaction. 62 | // This value should include any shipping or tax costs. 63 | func (h *Transaction) Revenue(revenue float64) *Transaction { 64 | h.revenue = revenue 65 | h.revenueSet = true 66 | return h 67 | } 68 | 69 | // Specifies the total shipping cost of the transaction. 70 | func (h *Transaction) Shipping(shipping float64) *Transaction { 71 | h.shipping = shipping 72 | h.shippingSet = true 73 | return h 74 | } 75 | 76 | // Specifies the total tax of the transaction. 77 | func (h *Transaction) Tax(tax float64) *Transaction { 78 | h.tax = tax 79 | h.taxSet = true 80 | return h 81 | } 82 | 83 | // When present indicates the local currency for all transaction 84 | // currency values. Value should be a valid ISO 4217 currency 85 | // code. 86 | func (h *Transaction) CurrencyCode(currencyCode string) *Transaction { 87 | h.currencyCode = currencyCode 88 | h.currencyCodeSet = true 89 | return h 90 | } 91 | 92 | func (h *Transaction) Copy() *Transaction { 93 | c := *h 94 | return &c 95 | } 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Go-ogle Analytics 2 | 3 | Track and monitor your Go programs for free with Google Analytics 4 | 5 | The `ga` package is essentially a Go wrapper around the [Google Analytics - Measurement Protocol](https://developers.google.com/analytics/devguides/collection/protocol/v1/reference) 6 | 7 | **Warning** This package is 95% generated from the [Parameter Reference](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters) so it may contain bugs - please report them. GA allows "10 million hits per month per property" and will reject requests after that. 8 | 9 | ### Install 10 | 11 | ``` 12 | go get -v github.com/jpillora/go-ogle-analytics 13 | ``` 14 | 15 | ### API 16 | 17 | Create a new `client` and `Send()` a 'pageview', 'screenview', 'event', 'transaction', 'item', 'social', 'exception' or 'timing' event. 18 | 19 | #### http://godoc.org/github.com/jpillora/go-ogle-analytics 20 | 21 | ### Quick Usage 22 | 23 | 1. Log into GA and create a new property and note its Tracker ID 24 | 25 | 1. Create a `ga-test.go` file 26 | 27 | ``` go 28 | package main 29 | 30 | import "github.com/jpillora/go-ogle-analytics" 31 | 32 | func main() { 33 | client, err := ga.NewClient("UA-XXXXXXXX-Y") 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | err = client.Send(ga.NewEvent("Foo", "Bar").Label("Bazz")) 39 | if err != nil { 40 | panic(err) 41 | } 42 | 43 | println("Event fired!") 44 | } 45 | ``` 46 | 47 | 1. In GA, go to Real-time > Events 48 | 49 | 1. Run `ga-test.go` 50 | 51 | ``` 52 | $ go run ga-test.go 53 | Event fired! 54 | ``` 55 | 56 | 1. Watch as your event appears 57 | 58 | ![foo-ga](https://cloud.githubusercontent.com/assets/633843/5979585/023fc580-a8fd-11e4-803a-956610bcc2e2.png) 59 | 60 | #### MIT License 61 | 62 | Copyright © 2015 <dev@jpillora.com> 63 | 64 | Permission is hereby granted, free of charge, to any person obtaining 65 | a copy of this software and associated documentation files (the 66 | 'Software'), to deal in the Software without restriction, including 67 | without limitation the rights to use, copy, modify, merge, publish, 68 | distribute, sublicense, and/or sell copies of the Software, and to 69 | permit persons to whom the Software is furnished to do so, subject to 70 | the following conditions: 71 | 72 | The above copyright notice and this permission notice shall be 73 | included in all copies or substantial portions of the Software. 74 | 75 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 76 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 77 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 78 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 79 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 80 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 81 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /type-timing.go: -------------------------------------------------------------------------------- 1 | package ga 2 | 3 | import "net/url" 4 | 5 | //WARNING: This file was generated. Do not edit. 6 | 7 | //Timing Hit Type 8 | type Timing struct { 9 | userTimingCategory string 10 | userTimingCategorySet bool 11 | userTimingVariableName string 12 | userTimingVariableNameSet bool 13 | userTimingTime int64 14 | userTimingTimeSet bool 15 | userTimingLabel string 16 | userTimingLabelSet bool 17 | pageLoadTime int64 18 | pageLoadTimeSet bool 19 | dNSTime int64 20 | dNSTimeSet bool 21 | pageDownloadTime int64 22 | pageDownloadTimeSet bool 23 | redirectResponseTime int64 24 | redirectResponseTimeSet bool 25 | tCPConnectTime int64 26 | tCPConnectTimeSet bool 27 | serverResponseTime int64 28 | serverResponseTimeSet bool 29 | dOMInteractiveTime int64 30 | dOMInteractiveTimeSet bool 31 | contentLoadTime int64 32 | contentLoadTimeSet bool 33 | } 34 | 35 | // NewTiming creates a new Timing Hit Type. 36 | 37 | func NewTiming() *Timing { 38 | h := &Timing{} 39 | return h 40 | } 41 | 42 | func (h *Timing) addFields(v url.Values) error { 43 | if h.userTimingCategorySet { 44 | v.Add("utc", h.userTimingCategory) 45 | } 46 | if h.userTimingVariableNameSet { 47 | v.Add("utv", h.userTimingVariableName) 48 | } 49 | if h.userTimingTimeSet { 50 | v.Add("utt", int2str(h.userTimingTime)) 51 | } 52 | if h.userTimingLabelSet { 53 | v.Add("utl", h.userTimingLabel) 54 | } 55 | if h.pageLoadTimeSet { 56 | v.Add("plt", int2str(h.pageLoadTime)) 57 | } 58 | if h.dNSTimeSet { 59 | v.Add("dns", int2str(h.dNSTime)) 60 | } 61 | if h.pageDownloadTimeSet { 62 | v.Add("pdt", int2str(h.pageDownloadTime)) 63 | } 64 | if h.redirectResponseTimeSet { 65 | v.Add("rrt", int2str(h.redirectResponseTime)) 66 | } 67 | if h.tCPConnectTimeSet { 68 | v.Add("tcp", int2str(h.tCPConnectTime)) 69 | } 70 | if h.serverResponseTimeSet { 71 | v.Add("srt", int2str(h.serverResponseTime)) 72 | } 73 | if h.dOMInteractiveTimeSet { 74 | v.Add("dit", int2str(h.dOMInteractiveTime)) 75 | } 76 | if h.contentLoadTimeSet { 77 | v.Add("clt", int2str(h.contentLoadTime)) 78 | } 79 | return nil 80 | } 81 | 82 | // Specifies the user timing category. 83 | func (h *Timing) UserTimingCategory(userTimingCategory string) *Timing { 84 | h.userTimingCategory = userTimingCategory 85 | h.userTimingCategorySet = true 86 | return h 87 | } 88 | 89 | // Specifies the user timing variable. 90 | func (h *Timing) UserTimingVariableName(userTimingVariableName string) *Timing { 91 | h.userTimingVariableName = userTimingVariableName 92 | h.userTimingVariableNameSet = true 93 | return h 94 | } 95 | 96 | // Specifies the user timing value. The value is in milliseconds. 97 | func (h *Timing) UserTimingTime(userTimingTime int64) *Timing { 98 | h.userTimingTime = userTimingTime 99 | h.userTimingTimeSet = true 100 | return h 101 | } 102 | 103 | // Specifies the user timing label. 104 | func (h *Timing) UserTimingLabel(userTimingLabel string) *Timing { 105 | h.userTimingLabel = userTimingLabel 106 | h.userTimingLabelSet = true 107 | return h 108 | } 109 | 110 | // Specifies the time it took for a page to load. The value 111 | // is in milliseconds. 112 | func (h *Timing) PageLoadTime(pageLoadTime int64) *Timing { 113 | h.pageLoadTime = pageLoadTime 114 | h.pageLoadTimeSet = true 115 | return h 116 | } 117 | 118 | // Specifies the time it took to do a DNS lookup.The value 119 | // is in milliseconds. 120 | func (h *Timing) DNSTime(dNSTime int64) *Timing { 121 | h.dNSTime = dNSTime 122 | h.dNSTimeSet = true 123 | return h 124 | } 125 | 126 | // Specifies the time it took for the page to be downloaded. 127 | // The value is in milliseconds. 128 | func (h *Timing) PageDownloadTime(pageDownloadTime int64) *Timing { 129 | h.pageDownloadTime = pageDownloadTime 130 | h.pageDownloadTimeSet = true 131 | return h 132 | } 133 | 134 | // Specifies the time it took for any redirects to happen. 135 | // The value is in milliseconds. 136 | func (h *Timing) RedirectResponseTime(redirectResponseTime int64) *Timing { 137 | h.redirectResponseTime = redirectResponseTime 138 | h.redirectResponseTimeSet = true 139 | return h 140 | } 141 | 142 | // Specifies the time it took for a TCP connection to be made. 143 | // The value is in milliseconds. 144 | func (h *Timing) TCPConnectTime(tCPConnectTime int64) *Timing { 145 | h.tCPConnectTime = tCPConnectTime 146 | h.tCPConnectTimeSet = true 147 | return h 148 | } 149 | 150 | // Specifies the time it took for the server to respond after 151 | // the connect time. The value is in milliseconds. 152 | func (h *Timing) ServerResponseTime(serverResponseTime int64) *Timing { 153 | h.serverResponseTime = serverResponseTime 154 | h.serverResponseTimeSet = true 155 | return h 156 | } 157 | 158 | // Specifies the time it took for Document.readyState to be 159 | // 'interactive'. The value is in milliseconds. 160 | func (h *Timing) DOMInteractiveTime(dOMInteractiveTime int64) *Timing { 161 | h.dOMInteractiveTime = dOMInteractiveTime 162 | h.dOMInteractiveTimeSet = true 163 | return h 164 | } 165 | 166 | // Specifies the time it took for the DOMContentLoaded Event 167 | // to fire. The value is in milliseconds. 168 | func (h *Timing) ContentLoadTime(contentLoadTime int64) *Timing { 169 | h.contentLoadTime = contentLoadTime 170 | h.contentLoadTimeSet = true 171 | return h 172 | } 173 | 174 | func (h *Timing) Copy() *Timing { 175 | c := *h 176 | return &c 177 | } 178 | -------------------------------------------------------------------------------- /generate/protocol.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | //protocol generator, a file is generated for each hittype 4 | 5 | import ( 6 | "bytes" 7 | "fmt" 8 | "io/ioutil" 9 | "log" 10 | "os" 11 | "regexp" 12 | "sort" 13 | "strings" 14 | "text/template" 15 | 16 | "golang.org/x/tools/imports" 17 | 18 | "github.com/PuerkitoBio/goquery" 19 | "github.com/huandu/xstrings" 20 | ) 21 | 22 | const protocolV1 = "https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters" 23 | 24 | type HitType struct { 25 | Name, 26 | StructName string 27 | Fields []*Field 28 | Indices []string 29 | HitTypeIDs []string 30 | } 31 | 32 | type Field struct { 33 | Name, 34 | PrivateName, 35 | Docs, 36 | Param, 37 | ParamStr, 38 | Type, 39 | Default, 40 | MaxLen, 41 | Examples string 42 | Required bool 43 | HitTypes []string 44 | Indices []string 45 | } 46 | 47 | //=================================== 48 | 49 | var allFields = []*Field{} 50 | 51 | var hitTypes = map[string]*HitType{ 52 | //special base type 53 | "client": &HitType{Name: "client"}, 54 | } 55 | 56 | //==================================================== 57 | //the code template (split into separate strings for somewhat better readability) 58 | 59 | func buildCode() string { 60 | 61 | var clientFields = `{{if eq .Name "client" }} //Use TLS when Send()ing 62 | UseTLS bool 63 | HttpClient *http.Client 64 | {{end}}` 65 | 66 | var fields = `{{range $index, $element := .Fields}} {{.PrivateName}} {{.Type}}{{if not .Required}} 67 | {{.PrivateName}}Set bool{{end}} 68 | {{end}}` 69 | 70 | var paramVal = `{{if eq .Type "string"}}h.{{.PrivateName}}{{end}}` + 71 | `{{if eq .Type "bool"}}bool2str(h.{{.PrivateName}}){{end}}` + 72 | `{{if eq .Type "int64"}}int2str(h.{{.PrivateName}}){{end}}` + 73 | `{{if eq .Type "float64"}}float2str(h.{{.PrivateName}}){{end}}` 74 | 75 | var params = `{{range $index, $element := .Fields}}{{if ne .Param ""}}{{if not .Required}} if h.{{.PrivateName}}Set { 76 | {{end}} v.Add({{.ParamStr}}, ` + paramVal + `){{if not .Required}} 77 | }{{end}} 78 | {{end}}{{end}}` 79 | 80 | var constructorDocs = `{{if ne .Name "client" }}// New{{ .StructName }} ` + 81 | `creates a new {{ .StructName }} Hit Type.` + 82 | `{{range $index, $element := .Fields}} 83 | {{if .Required}}{{.Docs}}{{end}}{{end}}` 84 | 85 | var constructorParams = `{{range $index, $element := .Fields}}{{if .Required}}` + 86 | `{{if ne $index 0}},{{end}}{{.PrivateName}} {{.Type}}` + 87 | `{{end}}{{end}}` 88 | 89 | var constructor = constructorDocs + ` 90 | func New{{ .StructName }}(` + constructorParams + `) *{{.StructName}} { 91 | h := &{{ .StructName }}{ 92 | {{range $index, $element := .Fields}}{{if .Required}} {{.PrivateName}}: {{.PrivateName}}, 93 | {{end}}{{end}} } 94 | return h 95 | }{{end}} 96 | ` 97 | 98 | var clientFuncs = `{{if eq .Name "client" }} 99 | func (c *Client) setType(h hitType) { 100 | switch h.(type) { 101 | {{range $index, $id := .HitTypeIDs}} case *{{ exportName $id}}: 102 | c.hitType = "{{ $id }}" 103 | {{end}} } 104 | } 105 | {{end}}` 106 | 107 | var setterFuncs = `{{range $index, $element := .Fields}}{{if not .Required}}{{.Docs}} 108 | func (h *{{$.StructName}}) {{.Name}}({{.PrivateName}} {{.Type}}) *{{$.StructName}} { 109 | h.{{.PrivateName}} = {{.PrivateName}} 110 | h.{{.PrivateName}}Set = true 111 | return h 112 | } 113 | 114 | {{end}}{{end}}` 115 | 116 | return `package ga 117 | 118 | //WARNING: This file was generated. Do not edit. 119 | 120 | //{{.StructName}} Hit Type 121 | type {{.StructName}} struct { 122 | ` + clientFields + fields + `} 123 | 124 | ` + constructor + clientFuncs + ` 125 | 126 | func (h *{{.StructName}}) addFields(v url.Values) error { 127 | ` + params + ` return nil 128 | } 129 | 130 | ` + setterFuncs + ` 131 | 132 | func (h *{{.StructName}}) Copy() *{{.StructName}} { 133 | c := *h 134 | return &c 135 | } 136 | ` 137 | } 138 | 139 | var codeTemplate *template.Template 140 | 141 | //==================================================== 142 | 143 | //meta helpers 144 | func grepper(restr string) func(string) string { 145 | re := regexp.MustCompile(restr) 146 | return func(s string) string { 147 | matches := re.FindAllStringSubmatch(s, 1) 148 | if len(matches) != 1 { 149 | log.Fatalf("'%s' should match '%s' exactly once", restr, s) 150 | } 151 | groups := matches[0] 152 | if len(groups) != 2 { 153 | log.Fatalf("'%s' should have exactly one group (found %d)", restr, len(groups)) 154 | } 155 | return groups[1] 156 | } 157 | } 158 | 159 | func striper(restr string) func(string) string { 160 | re := regexp.MustCompile(restr) 161 | return func(s string) string { 162 | return re.ReplaceAllString(s, "") 163 | } 164 | } 165 | 166 | //helper functions 167 | var trim = strings.TrimSpace 168 | var preslash = grepper(`^([^\/]+)`) 169 | var alpha = striper(`[^A-Za-z]`) 170 | var indexMatcher = regexp.MustCompile(`<[A-Za-z]+>`) 171 | var indexVar = grepper(`<([A-Za-z]+)>`) 172 | var strVar = grepper(`'([a-z]+)'`) 173 | 174 | func comment(s string) string { 175 | words := strings.Split(s, " ") 176 | comment := "//" 177 | width := 0 178 | for _, w := range words { 179 | if width > 55 { 180 | width = 0 181 | comment += "\n//" 182 | } 183 | width += len(w) + 1 184 | comment += " " + w 185 | } 186 | return comment 187 | } 188 | 189 | func exportName(s string) string { 190 | words := strings.Split(s, " ") 191 | for i, w := range words { 192 | words[i] = xstrings.FirstRuneToUpper(w) 193 | } 194 | return strings.Join(words, "") 195 | } 196 | 197 | func goName(parent, field string) string { 198 | return strings.TrimPrefix(field, parent) 199 | } 200 | 201 | func goType(gaType string) string { 202 | switch gaType { 203 | case "text": 204 | return "string" 205 | case "integer": 206 | return "int64" 207 | case "boolean": 208 | return "bool" 209 | case "currency": 210 | return "float64" 211 | } 212 | log.Fatal("Unknown GA Type: " + gaType) 213 | return "" 214 | } 215 | 216 | func displayErr(code string, err error) { 217 | lines := strings.Split(code, "\n") 218 | for i, l := range lines { 219 | lines[i] = fmt.Sprintf("%2d: %s", i+1, l) 220 | } 221 | log.Fatalf("Template:\n%s\n====\nError: %s", strings.Join(lines, "\n"), err) 222 | } 223 | 224 | //==================================== 225 | 226 | //main pipeline 227 | func main() { 228 | check() 229 | } 230 | 231 | func check() { 232 | code := buildCode() 233 | t := template.New("ga-code") 234 | t = t.Funcs(template.FuncMap{ 235 | "exportName": exportName, 236 | }) 237 | t, err := t.Parse(code) 238 | if err != nil { 239 | displayErr(code, err) 240 | } 241 | codeTemplate = t 242 | parse() 243 | } 244 | 245 | func parse() { 246 | 247 | log.Println("reading: protocol.html") 248 | b, _ := ioutil.ReadFile("generate/protocol-v1.html") 249 | r := bytes.NewReader(b) 250 | doc, err := goquery.NewDocumentFromReader(r) 251 | 252 | // log.Println("loading: " + protocolV1) 253 | // doc, err := goquery.NewDocument(protocolV1) 254 | if err != nil { 255 | log.Fatal(err) 256 | } 257 | 258 | //special exluded field 259 | var hitTypeDocs string 260 | 261 | doc.Find("h3").Each(func(i int, s *goquery.Selection) { 262 | 263 | content := s.Next().Children() 264 | cells := content.Eq(2).Find("tr td") 265 | 266 | //get trimmed raw contents 267 | f := &Field{ 268 | Name: trim(s.Find("a").Text()), 269 | Required: !strings.Contains(content.Eq(0).Text(), "Optional"), 270 | Docs: trim(content.Eq(1).Text()), 271 | Param: trim(cells.Eq(0).Text()), 272 | Type: trim(cells.Eq(1).Text()), 273 | Default: trim(cells.Eq(2).Text()), 274 | MaxLen: trim(cells.Eq(3).Text()), 275 | HitTypes: strings.Split(trim(cells.Eq(4).Text()), ", "), 276 | Examples: trim(content.Eq(3).Text()), 277 | } 278 | 279 | if f.Name == "Hit type" { 280 | hitTypeDocs = f.Docs 281 | } 282 | allFields = append(allFields, f) 283 | }) 284 | 285 | buildTypes(hitTypeDocs) 286 | } 287 | 288 | func buildTypes(hitTypeDocs string) { 289 | log.Println("building types") 290 | //hit type docs contain all type names 291 | for _, t := range strings.Split(hitTypeDocs, ",") { 292 | t = strVar(t) 293 | hitTypes[t] = &HitType{Name: t} 294 | } 295 | 296 | //place each field in one or more types 297 | for _, f := range allFields { 298 | for _, t := range f.HitTypes { 299 | //the client type holds all the common fields 300 | if t == "all" { 301 | t = "client" 302 | } 303 | h, exists := hitTypes[t] 304 | if !exists { 305 | log.Fatalf("Unknown type: '%s'", t) 306 | } 307 | h.Fields = append(h.Fields, f) 308 | } 309 | } 310 | 311 | process() 312 | } 313 | 314 | func process() { 315 | log.Println("processing fields") 316 | for _, f := range allFields { 317 | processField(f) 318 | } 319 | for _, h := range hitTypes { 320 | processHitType(h) 321 | } 322 | generate() 323 | } 324 | 325 | func processField(f *Field) { 326 | 327 | f.Name = alpha(exportName(preslash(f.Name))) 328 | 329 | //trim the field name by the hittype name (prevents ga.Event.EventAction) 330 | for _, h := range f.HitTypes { 331 | f.Name = goName(exportName(h), f.Name) 332 | } 333 | 334 | f.PrivateName = xstrings.FirstRuneToLower(f.Name) 335 | 336 | //these are manually defaulted 337 | if f.Name == "ProtocolVersion" || f.Name == "ClientID" { 338 | f.Required = false 339 | } 340 | 341 | //special case: DOCS ARE WRONG 342 | //these are not optional 343 | if strings.Contains(f.Docs, "Must not be empty.") { 344 | f.Docs = strings.Replace(f.Docs, "Must not be empty.", "", 1) 345 | f.Required = true 346 | } 347 | 348 | //special case: 349 | 350 | //unexport the hit type field 351 | if f.Name == "HitType" { 352 | f.Name = xstrings.FirstRuneToLower(f.Name) 353 | } 354 | 355 | f.Docs = comment(trim(f.Docs)) 356 | f.Type = goType(f.Type) 357 | f.ParamStr = `"` + f.Param + `"` 358 | 359 | //check param for 360 | for _, i := range indexMatcher.FindAllString(f.Param, -1) { 361 | //extract each var 362 | newi := indexVar(i) 363 | f.Indices = append(f.Indices, newi) 364 | //convert param var into a string concat 365 | f.ParamStr = strings.Replace(f.ParamStr, i, `" + h.`+newi+` + "`, 1) 366 | } 367 | } 368 | 369 | func processHitType(h *HitType) { 370 | 371 | h.StructName = exportName(h.Name) 372 | 373 | //set of index vars 374 | is := map[string]bool{} 375 | for _, f := range h.Fields { 376 | //add all index vars to the set 377 | for _, i := range f.Indices { 378 | is[i] = true 379 | } 380 | } 381 | //extra psuedo-fields to be added 382 | for i, _ := range is { 383 | h.Indices = append(h.Indices, i) 384 | } 385 | sort.Strings(h.Indices) 386 | 387 | //ordered indicies 388 | for _, i := range h.Indices { 389 | h.Fields = append(h.Fields, &Field{ 390 | Name: xstrings.FirstRuneToUpper(i), 391 | PrivateName: i, 392 | Type: "string", 393 | Docs: "// " + xstrings.FirstRuneToUpper(i) + " is required by other properties", 394 | }) 395 | } 396 | 397 | //place all non-client hittype ids in client for templating 398 | if h.Name == "client" { 399 | for _, h2 := range hitTypes { 400 | if h2.Name != "client" { 401 | h.HitTypeIDs = append(h.HitTypeIDs, h2.Name) 402 | } 403 | } 404 | sort.Strings(h.HitTypeIDs) 405 | } 406 | } 407 | 408 | func generate() { 409 | log.Println("generating files") 410 | for _, h := range hitTypes { 411 | generateFile(h) 412 | } 413 | } 414 | 415 | func generateFile(h *HitType) { 416 | 417 | code := &bytes.Buffer{} 418 | codeTemplate.Execute(code, h) 419 | 420 | newCode, err := imports.Process("prog.go", code.Bytes(), &imports.Options{ 421 | AllErrors: true, 422 | TabWidth: 4, 423 | Comments: true, 424 | }) 425 | if err != nil { 426 | displayErr(code.String(), err) 427 | } 428 | 429 | fname := "type-" + h.Name + ".go" 430 | f, err := os.Create(fname) 431 | defer f.Close() 432 | if err != nil { 433 | log.Fatal(err) 434 | } 435 | f.Write(newCode) 436 | log.Println("generated " + fname) 437 | } 438 | -------------------------------------------------------------------------------- /type-client.go: -------------------------------------------------------------------------------- 1 | package ga 2 | 3 | import ( 4 | "net/http" 5 | "net/url" 6 | ) 7 | 8 | //WARNING: This file was generated. Do not edit. 9 | 10 | //Client Hit Type 11 | type Client struct { 12 | //Use TLS when Send()ing 13 | UseTLS bool 14 | HttpClient *http.Client 15 | protocolVersion string 16 | protocolVersionSet bool 17 | trackingID string 18 | anonymizeIP bool 19 | anonymizeIPSet bool 20 | dataSource string 21 | dataSourceSet bool 22 | queueTime int64 23 | queueTimeSet bool 24 | cacheBuster string 25 | cacheBusterSet bool 26 | clientID string 27 | clientIDSet bool 28 | userID string 29 | userIDSet bool 30 | sessionControl string 31 | sessionControlSet bool 32 | iPOverride string 33 | iPOverrideSet bool 34 | userAgentOverride string 35 | userAgentOverrideSet bool 36 | geographicalOverride string 37 | geographicalOverrideSet bool 38 | documentReferrer string 39 | documentReferrerSet bool 40 | campaignName string 41 | campaignNameSet bool 42 | campaignSource string 43 | campaignSourceSet bool 44 | campaignMedium string 45 | campaignMediumSet bool 46 | campaignKeyword string 47 | campaignKeywordSet bool 48 | campaignContent string 49 | campaignContentSet bool 50 | campaignID string 51 | campaignIDSet bool 52 | googleAdWordsID string 53 | googleAdWordsIDSet bool 54 | googleDisplayAdsID string 55 | googleDisplayAdsIDSet bool 56 | screenResolution string 57 | screenResolutionSet bool 58 | viewportSize string 59 | viewportSizeSet bool 60 | documentEncoding string 61 | documentEncodingSet bool 62 | screenColors string 63 | screenColorsSet bool 64 | userLanguage string 65 | userLanguageSet bool 66 | javaEnabled bool 67 | javaEnabledSet bool 68 | flashVersion string 69 | flashVersionSet bool 70 | hitType string 71 | nonInteractionHit bool 72 | nonInteractionHitSet bool 73 | documentLocationURL string 74 | documentLocationURLSet bool 75 | documentHostName string 76 | documentHostNameSet bool 77 | documentPath string 78 | documentPathSet bool 79 | documentTitle string 80 | documentTitleSet bool 81 | screenName string 82 | screenNameSet bool 83 | linkID string 84 | linkIDSet bool 85 | applicationName string 86 | applicationNameSet bool 87 | applicationID string 88 | applicationIDSet bool 89 | applicationVersion string 90 | applicationVersionSet bool 91 | applicationInstallerID string 92 | applicationInstallerIDSet bool 93 | productSKU string 94 | productSKUSet bool 95 | productName string 96 | productNameSet bool 97 | productBrand string 98 | productBrandSet bool 99 | productCategory string 100 | productCategorySet bool 101 | productVariant string 102 | productVariantSet bool 103 | productPrice float64 104 | productPriceSet bool 105 | productQuantity int64 106 | productQuantitySet bool 107 | productCouponCode string 108 | productCouponCodeSet bool 109 | productPosition int64 110 | productPositionSet bool 111 | productCustomDimension string 112 | productCustomDimensionSet bool 113 | productCustomMetric int64 114 | productCustomMetricSet bool 115 | productAction string 116 | productActionSet bool 117 | transactionID string 118 | transactionIDSet bool 119 | affiliation string 120 | affiliationSet bool 121 | revenue float64 122 | revenueSet bool 123 | tax float64 124 | taxSet bool 125 | shipping float64 126 | shippingSet bool 127 | couponCode string 128 | couponCodeSet bool 129 | productActionList string 130 | productActionListSet bool 131 | checkoutStep int64 132 | checkoutStepSet bool 133 | checkoutStepOption string 134 | checkoutStepOptionSet bool 135 | productImpressionListName string 136 | productImpressionListNameSet bool 137 | productImpressionSKU string 138 | productImpressionSKUSet bool 139 | productImpressionName string 140 | productImpressionNameSet bool 141 | productImpressionBrand string 142 | productImpressionBrandSet bool 143 | productImpressionCategory string 144 | productImpressionCategorySet bool 145 | productImpressionVariant string 146 | productImpressionVariantSet bool 147 | productImpressionPosition int64 148 | productImpressionPositionSet bool 149 | productImpressionPrice float64 150 | productImpressionPriceSet bool 151 | productImpressionCustomDimension string 152 | productImpressionCustomDimensionSet bool 153 | productImpressionCustomMetric int64 154 | productImpressionCustomMetricSet bool 155 | promotionID string 156 | promotionIDSet bool 157 | promotionName string 158 | promotionNameSet bool 159 | promotionCreative string 160 | promotionCreativeSet bool 161 | promotionPosition string 162 | promotionPositionSet bool 163 | promotionAction string 164 | promotionActionSet bool 165 | customDimension string 166 | customDimensionSet bool 167 | customMetric int64 168 | customMetricSet bool 169 | experimentID string 170 | experimentIDSet bool 171 | experimentVariant string 172 | experimentVariantSet bool 173 | dimensionIndex string 174 | dimensionIndexSet bool 175 | listIndex string 176 | listIndexSet bool 177 | metricIndex string 178 | metricIndexSet bool 179 | productIndex string 180 | productIndexSet bool 181 | promoIndex string 182 | promoIndexSet bool 183 | } 184 | 185 | func (c *Client) setType(h hitType) { 186 | switch h.(type) { 187 | case *Event: 188 | c.hitType = "event" 189 | case *Exception: 190 | c.hitType = "exception" 191 | case *Item: 192 | c.hitType = "item" 193 | case *Pageview: 194 | c.hitType = "pageview" 195 | case *Screenview: 196 | c.hitType = "screenview" 197 | case *Social: 198 | c.hitType = "social" 199 | case *Timing: 200 | c.hitType = "timing" 201 | case *Transaction: 202 | c.hitType = "transaction" 203 | } 204 | } 205 | 206 | func (h *Client) addFields(v url.Values) error { 207 | if h.protocolVersionSet { 208 | v.Add("v", h.protocolVersion) 209 | } 210 | v.Add("tid", h.trackingID) 211 | if h.anonymizeIPSet { 212 | v.Add("aip", bool2str(h.anonymizeIP)) 213 | } 214 | if h.dataSourceSet { 215 | v.Add("ds", h.dataSource) 216 | } 217 | if h.queueTimeSet { 218 | v.Add("qt", int2str(h.queueTime)) 219 | } 220 | if h.cacheBusterSet { 221 | v.Add("z", h.cacheBuster) 222 | } 223 | if h.clientIDSet { 224 | v.Add("cid", h.clientID) 225 | } 226 | if h.userIDSet { 227 | v.Add("uid", h.userID) 228 | } 229 | if h.sessionControlSet { 230 | v.Add("sc", h.sessionControl) 231 | } 232 | if h.iPOverrideSet { 233 | v.Add("uip", h.iPOverride) 234 | } 235 | if h.userAgentOverrideSet { 236 | v.Add("ua", h.userAgentOverride) 237 | } 238 | if h.geographicalOverrideSet { 239 | v.Add("geoid", h.geographicalOverride) 240 | } 241 | if h.documentReferrerSet { 242 | v.Add("dr", h.documentReferrer) 243 | } 244 | if h.campaignNameSet { 245 | v.Add("cn", h.campaignName) 246 | } 247 | if h.campaignSourceSet { 248 | v.Add("cs", h.campaignSource) 249 | } 250 | if h.campaignMediumSet { 251 | v.Add("cm", h.campaignMedium) 252 | } 253 | if h.campaignKeywordSet { 254 | v.Add("ck", h.campaignKeyword) 255 | } 256 | if h.campaignContentSet { 257 | v.Add("cc", h.campaignContent) 258 | } 259 | if h.campaignIDSet { 260 | v.Add("ci", h.campaignID) 261 | } 262 | if h.googleAdWordsIDSet { 263 | v.Add("gclid", h.googleAdWordsID) 264 | } 265 | if h.googleDisplayAdsIDSet { 266 | v.Add("dclid", h.googleDisplayAdsID) 267 | } 268 | if h.screenResolutionSet { 269 | v.Add("sr", h.screenResolution) 270 | } 271 | if h.viewportSizeSet { 272 | v.Add("vp", h.viewportSize) 273 | } 274 | if h.documentEncodingSet { 275 | v.Add("de", h.documentEncoding) 276 | } 277 | if h.screenColorsSet { 278 | v.Add("sd", h.screenColors) 279 | } 280 | if h.userLanguageSet { 281 | v.Add("ul", h.userLanguage) 282 | } 283 | if h.javaEnabledSet { 284 | v.Add("je", bool2str(h.javaEnabled)) 285 | } 286 | if h.flashVersionSet { 287 | v.Add("fl", h.flashVersion) 288 | } 289 | v.Add("t", h.hitType) 290 | if h.nonInteractionHitSet { 291 | v.Add("ni", bool2str(h.nonInteractionHit)) 292 | } 293 | if h.documentLocationURLSet { 294 | v.Add("dl", h.documentLocationURL) 295 | } 296 | if h.documentHostNameSet { 297 | v.Add("dh", h.documentHostName) 298 | } 299 | if h.documentPathSet { 300 | v.Add("dp", h.documentPath) 301 | } 302 | if h.documentTitleSet { 303 | v.Add("dt", h.documentTitle) 304 | } 305 | if h.screenNameSet { 306 | v.Add("cd", h.screenName) 307 | } 308 | if h.linkIDSet { 309 | v.Add("linkid", h.linkID) 310 | } 311 | if h.applicationNameSet { 312 | v.Add("an", h.applicationName) 313 | } 314 | if h.applicationIDSet { 315 | v.Add("aid", h.applicationID) 316 | } 317 | if h.applicationVersionSet { 318 | v.Add("av", h.applicationVersion) 319 | } 320 | if h.applicationInstallerIDSet { 321 | v.Add("aiid", h.applicationInstallerID) 322 | } 323 | if h.productSKUSet { 324 | v.Add("pr"+h.productIndex+"id", h.productSKU) 325 | } 326 | if h.productNameSet { 327 | v.Add("pr"+h.productIndex+"nm", h.productName) 328 | } 329 | if h.productBrandSet { 330 | v.Add("pr"+h.productIndex+"br", h.productBrand) 331 | } 332 | if h.productCategorySet { 333 | v.Add("pr"+h.productIndex+"ca", h.productCategory) 334 | } 335 | if h.productVariantSet { 336 | v.Add("pr"+h.productIndex+"va", h.productVariant) 337 | } 338 | if h.productPriceSet { 339 | v.Add("pr"+h.productIndex+"pr", float2str(h.productPrice)) 340 | } 341 | if h.productQuantitySet { 342 | v.Add("pr"+h.productIndex+"qt", int2str(h.productQuantity)) 343 | } 344 | if h.productCouponCodeSet { 345 | v.Add("pr"+h.productIndex+"cc", h.productCouponCode) 346 | } 347 | if h.productPositionSet { 348 | v.Add("pr"+h.productIndex+"ps", int2str(h.productPosition)) 349 | } 350 | if h.productCustomDimensionSet { 351 | v.Add("pr"+h.productIndex+"cd"+h.dimensionIndex+"", h.productCustomDimension) 352 | } 353 | if h.productCustomMetricSet { 354 | v.Add("pr"+h.productIndex+"cm"+h.metricIndex+"", int2str(h.productCustomMetric)) 355 | } 356 | if h.productActionSet { 357 | v.Add("pa", h.productAction) 358 | } 359 | if h.transactionIDSet { 360 | v.Add("ti", h.transactionID) 361 | } 362 | if h.affiliationSet { 363 | v.Add("ta", h.affiliation) 364 | } 365 | if h.revenueSet { 366 | v.Add("tr", float2str(h.revenue)) 367 | } 368 | if h.taxSet { 369 | v.Add("tt", float2str(h.tax)) 370 | } 371 | if h.shippingSet { 372 | v.Add("ts", float2str(h.shipping)) 373 | } 374 | if h.couponCodeSet { 375 | v.Add("tcc", h.couponCode) 376 | } 377 | if h.productActionListSet { 378 | v.Add("pal", h.productActionList) 379 | } 380 | if h.checkoutStepSet { 381 | v.Add("cos", int2str(h.checkoutStep)) 382 | } 383 | if h.checkoutStepOptionSet { 384 | v.Add("col", h.checkoutStepOption) 385 | } 386 | if h.productImpressionListNameSet { 387 | v.Add("il"+h.listIndex+"nm", h.productImpressionListName) 388 | } 389 | if h.productImpressionSKUSet { 390 | v.Add("il"+h.listIndex+"pi"+h.productIndex+"id", h.productImpressionSKU) 391 | } 392 | if h.productImpressionNameSet { 393 | v.Add("il"+h.listIndex+"pi"+h.productIndex+"nm", h.productImpressionName) 394 | } 395 | if h.productImpressionBrandSet { 396 | v.Add("il"+h.listIndex+"pi"+h.productIndex+"br", h.productImpressionBrand) 397 | } 398 | if h.productImpressionCategorySet { 399 | v.Add("il"+h.listIndex+"pi"+h.productIndex+"ca", h.productImpressionCategory) 400 | } 401 | if h.productImpressionVariantSet { 402 | v.Add("il"+h.listIndex+"pi"+h.productIndex+"va", h.productImpressionVariant) 403 | } 404 | if h.productImpressionPositionSet { 405 | v.Add("il"+h.listIndex+"pi"+h.productIndex+"ps", int2str(h.productImpressionPosition)) 406 | } 407 | if h.productImpressionPriceSet { 408 | v.Add("il"+h.listIndex+"pi"+h.productIndex+"pr", float2str(h.productImpressionPrice)) 409 | } 410 | if h.productImpressionCustomDimensionSet { 411 | v.Add("il"+h.listIndex+"pi"+h.productIndex+"cd"+h.dimensionIndex+"", h.productImpressionCustomDimension) 412 | } 413 | if h.productImpressionCustomMetricSet { 414 | v.Add("il"+h.listIndex+"pi"+h.productIndex+"cm"+h.metricIndex+"", int2str(h.productImpressionCustomMetric)) 415 | } 416 | if h.promotionIDSet { 417 | v.Add("promo"+h.promoIndex+"id", h.promotionID) 418 | } 419 | if h.promotionNameSet { 420 | v.Add("promo"+h.promoIndex+"nm", h.promotionName) 421 | } 422 | if h.promotionCreativeSet { 423 | v.Add("promo"+h.promoIndex+"cr", h.promotionCreative) 424 | } 425 | if h.promotionPositionSet { 426 | v.Add("promo"+h.promoIndex+"ps", h.promotionPosition) 427 | } 428 | if h.promotionActionSet { 429 | v.Add("promoa", h.promotionAction) 430 | } 431 | if h.customDimensionSet { 432 | v.Add("cd"+h.dimensionIndex+"", h.customDimension) 433 | } 434 | if h.customMetricSet { 435 | v.Add("cm"+h.metricIndex+"", int2str(h.customMetric)) 436 | } 437 | if h.experimentIDSet { 438 | v.Add("xid", h.experimentID) 439 | } 440 | if h.experimentVariantSet { 441 | v.Add("xvar", h.experimentVariant) 442 | } 443 | return nil 444 | } 445 | 446 | // The Protocol version. The current value is '1'. This will 447 | // only change when there are changes made that are not backwards 448 | // compatible. 449 | func (h *Client) ProtocolVersion(protocolVersion string) *Client { 450 | h.protocolVersion = protocolVersion 451 | h.protocolVersionSet = true 452 | return h 453 | } 454 | 455 | // When present, the IP address of the sender will be anonymized. 456 | // For example, the IP will be anonymized if any of the following 457 | // parameters are present in the payload: &aip=, &aip=0, or 458 | // &aip=1 459 | func (h *Client) AnonymizeIP(anonymizeIP bool) *Client { 460 | h.anonymizeIP = anonymizeIP 461 | h.anonymizeIPSet = true 462 | return h 463 | } 464 | 465 | // Indicates the data source of the hit. Hits sent from analytics.js 466 | // will have data source set to 'web'; hits sent from one of 467 | // the mobile SDKs will have data source set to 'app'. 468 | func (h *Client) DataSource(dataSource string) *Client { 469 | h.dataSource = dataSource 470 | h.dataSourceSet = true 471 | return h 472 | } 473 | 474 | // Used to collect offline / latent hits. The value represents 475 | // the time delta (in milliseconds) between when the hit being 476 | // reported occurred and the time the hit was sent. The value 477 | // must be greater than or equal to 0. Values greater than 478 | // four hours may lead to hits not being processed. 479 | func (h *Client) QueueTime(queueTime int64) *Client { 480 | h.queueTime = queueTime 481 | h.queueTimeSet = true 482 | return h 483 | } 484 | 485 | // Used to send a random number in GET requests to ensure browsers 486 | // and proxies don't cache hits. It should be sent as the final 487 | // parameter of the request since we've seen some 3rd party 488 | // internet filtering software add additional parameters to 489 | // HTTP requests incorrectly. This value is not used in reporting. 490 | func (h *Client) CacheBuster(cacheBuster string) *Client { 491 | h.cacheBuster = cacheBuster 492 | h.cacheBusterSet = true 493 | return h 494 | } 495 | 496 | // This anonymously identifies a particular user, device, or 497 | // browser instance. For the web, this is generally stored 498 | // as a first-party cookie with a two-year expiration. For 499 | // mobile apps, this is randomly generated for each particular 500 | // instance of an application install. The value of this field 501 | // should be a random UUID (version 4) as described in http://www.ietf.org/rfc/rfc4122.txt 502 | func (h *Client) ClientID(clientID string) *Client { 503 | h.clientID = clientID 504 | h.clientIDSet = true 505 | return h 506 | } 507 | 508 | // This is intended to be a known identifier for a user provided 509 | // by the site owner/tracking library user. It may not itself 510 | // be PII (personally identifiable information). The value 511 | // should never be persisted in GA cookies or other Analytics 512 | // provided storage. 513 | func (h *Client) UserID(userID string) *Client { 514 | h.userID = userID 515 | h.userIDSet = true 516 | return h 517 | } 518 | 519 | // Used to control the session duration. A value of 'start' 520 | // forces a new session to start with this hit and 'end' forces 521 | // the current session to end with this hit. All other values 522 | // are ignored. 523 | func (h *Client) SessionControl(sessionControl string) *Client { 524 | h.sessionControl = sessionControl 525 | h.sessionControlSet = true 526 | return h 527 | } 528 | 529 | // The IP address of the user. This should be a valid IP address 530 | // in IPv4 or IPv6 format. It will always be anonymized just 531 | // as though &aip (anonymize IP) had been used. 532 | func (h *Client) IPOverride(iPOverride string) *Client { 533 | h.iPOverride = iPOverride 534 | h.iPOverrideSet = true 535 | return h 536 | } 537 | 538 | // The User Agent of the browser. Note that Google has libraries 539 | // to identify real user agents. Hand crafting your own agent 540 | // could break at any time. 541 | func (h *Client) UserAgentOverride(userAgentOverride string) *Client { 542 | h.userAgentOverride = userAgentOverride 543 | h.userAgentOverrideSet = true 544 | return h 545 | } 546 | 547 | // The geographical location of the user. The geographical 548 | // ID should be a two letter country code or a criteria ID 549 | // representing a city or region (see http://developers.google.com/analytics/devguides/collection/protocol/v1/geoid). 550 | // This parameter takes precedent over any location derived 551 | // from IP address, including the IP Override parameter. An 552 | // invalid code will result in geographical dimensions to be 553 | // set to '(not set)'. 554 | func (h *Client) GeographicalOverride(geographicalOverride string) *Client { 555 | h.geographicalOverride = geographicalOverride 556 | h.geographicalOverrideSet = true 557 | return h 558 | } 559 | 560 | // Specifies which referral source brought traffic to a website. 561 | // This value is also used to compute the traffic source. The 562 | // format of this value is a URL. 563 | func (h *Client) DocumentReferrer(documentReferrer string) *Client { 564 | h.documentReferrer = documentReferrer 565 | h.documentReferrerSet = true 566 | return h 567 | } 568 | 569 | // Specifies the campaign name. 570 | func (h *Client) CampaignName(campaignName string) *Client { 571 | h.campaignName = campaignName 572 | h.campaignNameSet = true 573 | return h 574 | } 575 | 576 | // Specifies the campaign source. 577 | func (h *Client) CampaignSource(campaignSource string) *Client { 578 | h.campaignSource = campaignSource 579 | h.campaignSourceSet = true 580 | return h 581 | } 582 | 583 | // Specifies the campaign medium. 584 | func (h *Client) CampaignMedium(campaignMedium string) *Client { 585 | h.campaignMedium = campaignMedium 586 | h.campaignMediumSet = true 587 | return h 588 | } 589 | 590 | // Specifies the campaign keyword. 591 | func (h *Client) CampaignKeyword(campaignKeyword string) *Client { 592 | h.campaignKeyword = campaignKeyword 593 | h.campaignKeywordSet = true 594 | return h 595 | } 596 | 597 | // Specifies the campaign content. 598 | func (h *Client) CampaignContent(campaignContent string) *Client { 599 | h.campaignContent = campaignContent 600 | h.campaignContentSet = true 601 | return h 602 | } 603 | 604 | // Specifies the campaign ID. 605 | func (h *Client) CampaignID(campaignID string) *Client { 606 | h.campaignID = campaignID 607 | h.campaignIDSet = true 608 | return h 609 | } 610 | 611 | // Specifies the Google AdWords Id. 612 | func (h *Client) GoogleAdWordsID(googleAdWordsID string) *Client { 613 | h.googleAdWordsID = googleAdWordsID 614 | h.googleAdWordsIDSet = true 615 | return h 616 | } 617 | 618 | // Specifies the Google Display Ads Id. 619 | func (h *Client) GoogleDisplayAdsID(googleDisplayAdsID string) *Client { 620 | h.googleDisplayAdsID = googleDisplayAdsID 621 | h.googleDisplayAdsIDSet = true 622 | return h 623 | } 624 | 625 | // Specifies the screen resolution. 626 | func (h *Client) ScreenResolution(screenResolution string) *Client { 627 | h.screenResolution = screenResolution 628 | h.screenResolutionSet = true 629 | return h 630 | } 631 | 632 | // Specifies the viewable area of the browser / device. 633 | func (h *Client) ViewportSize(viewportSize string) *Client { 634 | h.viewportSize = viewportSize 635 | h.viewportSizeSet = true 636 | return h 637 | } 638 | 639 | // Specifies the character set used to encode the page / document. 640 | func (h *Client) DocumentEncoding(documentEncoding string) *Client { 641 | h.documentEncoding = documentEncoding 642 | h.documentEncodingSet = true 643 | return h 644 | } 645 | 646 | // Specifies the screen color depth. 647 | func (h *Client) ScreenColors(screenColors string) *Client { 648 | h.screenColors = screenColors 649 | h.screenColorsSet = true 650 | return h 651 | } 652 | 653 | // Specifies the language. 654 | func (h *Client) UserLanguage(userLanguage string) *Client { 655 | h.userLanguage = userLanguage 656 | h.userLanguageSet = true 657 | return h 658 | } 659 | 660 | // Specifies whether Java was enabled. 661 | func (h *Client) JavaEnabled(javaEnabled bool) *Client { 662 | h.javaEnabled = javaEnabled 663 | h.javaEnabledSet = true 664 | return h 665 | } 666 | 667 | // Specifies the flash version. 668 | func (h *Client) FlashVersion(flashVersion string) *Client { 669 | h.flashVersion = flashVersion 670 | h.flashVersionSet = true 671 | return h 672 | } 673 | 674 | // Specifies that a hit be considered non-interactive. 675 | func (h *Client) NonInteractionHit(nonInteractionHit bool) *Client { 676 | h.nonInteractionHit = nonInteractionHit 677 | h.nonInteractionHitSet = true 678 | return h 679 | } 680 | 681 | // Use this parameter to send the full URL (document location) 682 | // of the page on which content resides. You can use the &dh 683 | // and &dp parameters to override the hostname and path + query 684 | // portions of the document location, accordingly. The JavaScript 685 | // clients determine this parameter using the concatenation 686 | // of the document.location.origin + document.location.pathname 687 | // + document.location.search browser parameters. Be sure to 688 | // remove any user authentication or other private information 689 | // from the URL if present. For 'pageview' hits, either &dl 690 | // or both &dh and &dp have to be specified for the hit to 691 | // be valid. 692 | func (h *Client) DocumentLocationURL(documentLocationURL string) *Client { 693 | h.documentLocationURL = documentLocationURL 694 | h.documentLocationURLSet = true 695 | return h 696 | } 697 | 698 | // Specifies the hostname from which content was hosted. 699 | func (h *Client) DocumentHostName(documentHostName string) *Client { 700 | h.documentHostName = documentHostName 701 | h.documentHostNameSet = true 702 | return h 703 | } 704 | 705 | // The path portion of the page URL. Should begin with '/'. 706 | // For 'pageview' hits, either &dl or both &dh and &dp have 707 | // to be specified for the hit to be valid. 708 | func (h *Client) DocumentPath(documentPath string) *Client { 709 | h.documentPath = documentPath 710 | h.documentPathSet = true 711 | return h 712 | } 713 | 714 | // The title of the page / document. 715 | func (h *Client) DocumentTitle(documentTitle string) *Client { 716 | h.documentTitle = documentTitle 717 | h.documentTitleSet = true 718 | return h 719 | } 720 | 721 | // If not specified, this will default to the unique URL of 722 | // the page by either using the &dl parameter as-is or assembling 723 | // it from &dh and &dp. App tracking makes use of this for 724 | // the 'Screen Name' of the screenview hit. 725 | func (h *Client) ScreenName(screenName string) *Client { 726 | h.screenName = screenName 727 | h.screenNameSet = true 728 | return h 729 | } 730 | 731 | // The ID of a clicked DOM element, used to disambiguate multiple 732 | // links to the same URL in In-Page Analytics reports when 733 | // Enhanced Link Attribution is enabled for the property. 734 | func (h *Client) LinkID(linkID string) *Client { 735 | h.linkID = linkID 736 | h.linkIDSet = true 737 | return h 738 | } 739 | 740 | // Specifies the application name. 741 | func (h *Client) ApplicationName(applicationName string) *Client { 742 | h.applicationName = applicationName 743 | h.applicationNameSet = true 744 | return h 745 | } 746 | 747 | // Application identifier. 748 | func (h *Client) ApplicationID(applicationID string) *Client { 749 | h.applicationID = applicationID 750 | h.applicationIDSet = true 751 | return h 752 | } 753 | 754 | // Specifies the application version. 755 | func (h *Client) ApplicationVersion(applicationVersion string) *Client { 756 | h.applicationVersion = applicationVersion 757 | h.applicationVersionSet = true 758 | return h 759 | } 760 | 761 | // Application installer identifier. 762 | func (h *Client) ApplicationInstallerID(applicationInstallerID string) *Client { 763 | h.applicationInstallerID = applicationInstallerID 764 | h.applicationInstallerIDSet = true 765 | return h 766 | } 767 | 768 | // The SKU of the product. Product index must be a positive 769 | // integer between 1 and 200, inclusive. For analytics.js the 770 | // Enhanced Ecommerce plugin must be installed before using 771 | // this field. 772 | func (h *Client) ProductSKU(productSKU string) *Client { 773 | h.productSKU = productSKU 774 | h.productSKUSet = true 775 | return h 776 | } 777 | 778 | // The name of the product. Product index must be a positive 779 | // integer between 1 and 200, inclusive. For analytics.js the 780 | // Enhanced Ecommerce plugin must be installed before using 781 | // this field. 782 | func (h *Client) ProductName(productName string) *Client { 783 | h.productName = productName 784 | h.productNameSet = true 785 | return h 786 | } 787 | 788 | // The brand associated with the product. Product index must 789 | // be a positive integer between 1 and 200, inclusive. For 790 | // analytics.js the Enhanced Ecommerce plugin must be installed 791 | // before using this field. 792 | func (h *Client) ProductBrand(productBrand string) *Client { 793 | h.productBrand = productBrand 794 | h.productBrandSet = true 795 | return h 796 | } 797 | 798 | // The category to which the product belongs. Product index 799 | // must be a positive integer between 1 and 200, inclusive. 800 | // The product category parameter can be hierarchical. Use 801 | // / as a delimiter to specify up to 5-levels of hierarchy. 802 | // For analytics.js the Enhanced Ecommerce plugin must be installed 803 | // before using this field. 804 | func (h *Client) ProductCategory(productCategory string) *Client { 805 | h.productCategory = productCategory 806 | h.productCategorySet = true 807 | return h 808 | } 809 | 810 | // The variant of the product. Product index must be a positive 811 | // integer between 1 and 200, inclusive. For analytics.js the 812 | // Enhanced Ecommerce plugin must be installed before using 813 | // this field. 814 | func (h *Client) ProductVariant(productVariant string) *Client { 815 | h.productVariant = productVariant 816 | h.productVariantSet = true 817 | return h 818 | } 819 | 820 | // The price of a product. Product index must be a positive 821 | // integer between 1 and 200, inclusive. For analytics.js the 822 | // Enhanced Ecommerce plugin must be installed before using 823 | // this field. 824 | func (h *Client) ProductPrice(productPrice float64) *Client { 825 | h.productPrice = productPrice 826 | h.productPriceSet = true 827 | return h 828 | } 829 | 830 | // The quantity of a product. Product index must be a positive 831 | // integer between 1 and 200, inclusive. For analytics.js the 832 | // Enhanced Ecommerce plugin must be installed before using 833 | // this field. 834 | func (h *Client) ProductQuantity(productQuantity int64) *Client { 835 | h.productQuantity = productQuantity 836 | h.productQuantitySet = true 837 | return h 838 | } 839 | 840 | // The coupon code associated with a product. Product index 841 | // must be a positive integer between 1 and 200, inclusive. 842 | // For analytics.js the Enhanced Ecommerce plugin must be installed 843 | // before using this field. 844 | func (h *Client) ProductCouponCode(productCouponCode string) *Client { 845 | h.productCouponCode = productCouponCode 846 | h.productCouponCodeSet = true 847 | return h 848 | } 849 | 850 | // The product's position in a list or collection. Product 851 | // index must be a positive integer between 1 and 200, inclusive. 852 | // For analytics.js the Enhanced Ecommerce plugin must be installed 853 | // before using this field. 854 | func (h *Client) ProductPosition(productPosition int64) *Client { 855 | h.productPosition = productPosition 856 | h.productPositionSet = true 857 | return h 858 | } 859 | 860 | // A product-level custom dimension where dimension index is 861 | // a positive integer between 1 and 200, inclusive. Product 862 | // index must be a positive integer between 1 and 200, inclusive. 863 | // For analytics.js the Enhanced Ecommerce plugin must be installed 864 | // before using this field. 865 | func (h *Client) ProductCustomDimension(productCustomDimension string) *Client { 866 | h.productCustomDimension = productCustomDimension 867 | h.productCustomDimensionSet = true 868 | return h 869 | } 870 | 871 | // A product-level custom metric where metric index is a positive 872 | // integer between 1 and 200, inclusive. Product index must 873 | // be a positive integer between 1 and 200, inclusive. For 874 | // analytics.js the Enhanced Ecommerce plugin must be installed 875 | // before using this field. 876 | func (h *Client) ProductCustomMetric(productCustomMetric int64) *Client { 877 | h.productCustomMetric = productCustomMetric 878 | h.productCustomMetricSet = true 879 | return h 880 | } 881 | 882 | // The role of the products included in a hit. If a product 883 | // action is not specified, all product definitions included 884 | // with the hit will be ignored. Must be one of: detail, click, 885 | // add, remove, checkout, checkout_option, purchase, refund. 886 | // For analytics.js the Enhanced Ecommerce plugin must be installed 887 | // before using this field. 888 | func (h *Client) ProductAction(productAction string) *Client { 889 | h.productAction = productAction 890 | h.productActionSet = true 891 | return h 892 | } 893 | 894 | // The transaction ID. This is an additional parameter that 895 | // can be sent when Product Action is set to 'purchase' or 896 | // 'refund'. For analytics.js the Enhanced Ecommerce plugin 897 | // must be installed before using this field. 898 | func (h *Client) TransactionID(transactionID string) *Client { 899 | h.transactionID = transactionID 900 | h.transactionIDSet = true 901 | return h 902 | } 903 | 904 | // The store or affiliation from which this transaction occurred. 905 | // This is an additional parameter that can be sent when Product 906 | // Action is set to 'purchase' or 'refund'. For analytics.js 907 | // the Enhanced Ecommerce plugin must be installed before using 908 | // this field. 909 | func (h *Client) Affiliation(affiliation string) *Client { 910 | h.affiliation = affiliation 911 | h.affiliationSet = true 912 | return h 913 | } 914 | 915 | // The total value of the transaction, including tax and shipping. 916 | // If not sent, this value will be automatically calculated 917 | // using the product quantity and price fields of all products 918 | // in the same hit. This is an additional parameter that can 919 | // be sent when Product Action is set to 'purchase' or 'refund'. 920 | // For analytics.js the Enhanced Ecommerce plugin must be installed 921 | // before using this field. 922 | func (h *Client) Revenue(revenue float64) *Client { 923 | h.revenue = revenue 924 | h.revenueSet = true 925 | return h 926 | } 927 | 928 | // The total tax associated with the transaction. This is an 929 | // additional parameter that can be sent when Product Action 930 | // is set to 'purchase' or 'refund'. For analytics.js the Enhanced 931 | // Ecommerce plugin must be installed before using this field. 932 | func (h *Client) Tax(tax float64) *Client { 933 | h.tax = tax 934 | h.taxSet = true 935 | return h 936 | } 937 | 938 | // The shipping cost associated with the transaction. This 939 | // is an additional parameter that can be sent when Product 940 | // Action is set to 'purchase' or 'refund'. For analytics.js 941 | // the Enhanced Ecommerce plugin must be installed before using 942 | // this field. 943 | func (h *Client) Shipping(shipping float64) *Client { 944 | h.shipping = shipping 945 | h.shippingSet = true 946 | return h 947 | } 948 | 949 | // The transaction coupon redeemed with the transaction. This 950 | // is an additional parameter that can be sent when Product 951 | // Action is set to 'purchase' or 'refund'. For analytics.js 952 | // the Enhanced Ecommerce plugin must be installed before using 953 | // this field. 954 | func (h *Client) CouponCode(couponCode string) *Client { 955 | h.couponCode = couponCode 956 | h.couponCodeSet = true 957 | return h 958 | } 959 | 960 | // The list or collection from which a product action occurred. 961 | // This is an additional parameter that can be sent when Product 962 | // Action is set to 'detail' or 'click'. For analytics.js the 963 | // Enhanced Ecommerce plugin must be installed before using 964 | // this field. 965 | func (h *Client) ProductActionList(productActionList string) *Client { 966 | h.productActionList = productActionList 967 | h.productActionListSet = true 968 | return h 969 | } 970 | 971 | // The step number in a checkout funnel. This is an additional 972 | // parameter that can be sent when Product Action is set to 973 | // 'checkout'. For analytics.js the Enhanced Ecommerce plugin 974 | // must be installed before using this field. 975 | func (h *Client) CheckoutStep(checkoutStep int64) *Client { 976 | h.checkoutStep = checkoutStep 977 | h.checkoutStepSet = true 978 | return h 979 | } 980 | 981 | // Additional information about a checkout step. This is an 982 | // additional parameter that can be sent when Product Action 983 | // is set to 'checkout'. For analytics.js the Enhanced Ecommerce 984 | // plugin must be installed before using this field. 985 | func (h *Client) CheckoutStepOption(checkoutStepOption string) *Client { 986 | h.checkoutStepOption = checkoutStepOption 987 | h.checkoutStepOptionSet = true 988 | return h 989 | } 990 | 991 | // The list or collection to which a product belongs. Impression 992 | // List index must be a positive integer between 1 and 200, 993 | // inclusive. For analytics.js the Enhanced Ecommerce plugin 994 | // must be installed before using this field. 995 | func (h *Client) ProductImpressionListName(productImpressionListName string) *Client { 996 | h.productImpressionListName = productImpressionListName 997 | h.productImpressionListNameSet = true 998 | return h 999 | } 1000 | 1001 | // The product ID or SKU. Impression List index must be a positive 1002 | // integer between 1 and 200, inclusive. Product index must 1003 | // be a positive integer between 1 and 200, inclusive. For 1004 | // analytics.js the Enhanced Ecommerce plugin must be installed 1005 | // before using this field. 1006 | func (h *Client) ProductImpressionSKU(productImpressionSKU string) *Client { 1007 | h.productImpressionSKU = productImpressionSKU 1008 | h.productImpressionSKUSet = true 1009 | return h 1010 | } 1011 | 1012 | // The name of the product. Impression List index must be a 1013 | // positive integer between 1 and 200, inclusive. Product index 1014 | // must be a positive integer between 1 and 200, inclusive. 1015 | // For analytics.js the Enhanced Ecommerce plugin must be installed 1016 | // before using this field. 1017 | func (h *Client) ProductImpressionName(productImpressionName string) *Client { 1018 | h.productImpressionName = productImpressionName 1019 | h.productImpressionNameSet = true 1020 | return h 1021 | } 1022 | 1023 | // The brand associated with the product. Impression List index 1024 | // must be a positive integer between 1 and 200, inclusive. 1025 | // Product index must be a positive integer between 1 and 200, 1026 | // inclusive. For analytics.js the Enhanced Ecommerce plugin 1027 | // must be installed before using this field. 1028 | func (h *Client) ProductImpressionBrand(productImpressionBrand string) *Client { 1029 | h.productImpressionBrand = productImpressionBrand 1030 | h.productImpressionBrandSet = true 1031 | return h 1032 | } 1033 | 1034 | // The category to which the product belongs. Impression List 1035 | // index must be a positive integer between 1 and 200, inclusive. 1036 | // Product index must be a positive integer between 1 and 200, 1037 | // inclusive. For analytics.js the Enhanced Ecommerce plugin 1038 | // must be installed before using this field. 1039 | func (h *Client) ProductImpressionCategory(productImpressionCategory string) *Client { 1040 | h.productImpressionCategory = productImpressionCategory 1041 | h.productImpressionCategorySet = true 1042 | return h 1043 | } 1044 | 1045 | // The variant of the product. Impression List index must be 1046 | // a positive integer between 1 and 200, inclusive. Product 1047 | // index must be a positive integer between 1 and 200, inclusive. 1048 | // For analytics.js the Enhanced Ecommerce plugin must be installed 1049 | // before using this field. 1050 | func (h *Client) ProductImpressionVariant(productImpressionVariant string) *Client { 1051 | h.productImpressionVariant = productImpressionVariant 1052 | h.productImpressionVariantSet = true 1053 | return h 1054 | } 1055 | 1056 | // The product's position in a list or collection. Impression 1057 | // List index must be a positive integer between 1 and 200, 1058 | // inclusive. Product index must be a positive integer between 1059 | // 1 and 200, inclusive. For analytics.js the Enhanced Ecommerce 1060 | // plugin must be installed before using this field. 1061 | func (h *Client) ProductImpressionPosition(productImpressionPosition int64) *Client { 1062 | h.productImpressionPosition = productImpressionPosition 1063 | h.productImpressionPositionSet = true 1064 | return h 1065 | } 1066 | 1067 | // The price of a product. Impression List index must be a 1068 | // positive integer between 1 and 200, inclusive. Product index 1069 | // must be a positive integer between 1 and 200, inclusive. 1070 | // For analytics.js the Enhanced Ecommerce plugin must be installed 1071 | // before using this field. 1072 | func (h *Client) ProductImpressionPrice(productImpressionPrice float64) *Client { 1073 | h.productImpressionPrice = productImpressionPrice 1074 | h.productImpressionPriceSet = true 1075 | return h 1076 | } 1077 | 1078 | // A product-level custom dimension where dimension index is 1079 | // a positive integer between 1 and 200, inclusive. Impression 1080 | // List index must be a positive integer between 1 and 200, 1081 | // inclusive. Product index must be a positive integer between 1082 | // 1 and 200, inclusive. For analytics.js the Enhanced Ecommerce 1083 | // plugin must be installed before using this field. 1084 | func (h *Client) ProductImpressionCustomDimension(productImpressionCustomDimension string) *Client { 1085 | h.productImpressionCustomDimension = productImpressionCustomDimension 1086 | h.productImpressionCustomDimensionSet = true 1087 | return h 1088 | } 1089 | 1090 | // A product-level custom metric where metric index is a positive 1091 | // integer between 1 and 200, inclusive. Impression List index 1092 | // must be a positive integer between 1 and 200, inclusive. 1093 | // Product index must be a positive integer between 1 and 200, 1094 | // inclusive. For analytics.js the Enhanced Ecommerce plugin 1095 | // must be installed before using this field. 1096 | func (h *Client) ProductImpressionCustomMetric(productImpressionCustomMetric int64) *Client { 1097 | h.productImpressionCustomMetric = productImpressionCustomMetric 1098 | h.productImpressionCustomMetricSet = true 1099 | return h 1100 | } 1101 | 1102 | // The promotion ID. Promotion index must be a positive integer 1103 | // between 1 and 200, inclusive. For analytics.js the Enhanced 1104 | // Ecommerce plugin must be installed before using this field. 1105 | func (h *Client) PromotionID(promotionID string) *Client { 1106 | h.promotionID = promotionID 1107 | h.promotionIDSet = true 1108 | return h 1109 | } 1110 | 1111 | // The name of the promotion. Promotion index must be a positive 1112 | // integer between 1 and 200, inclusive. For analytics.js the 1113 | // Enhanced Ecommerce plugin must be installed before using 1114 | // this field. 1115 | func (h *Client) PromotionName(promotionName string) *Client { 1116 | h.promotionName = promotionName 1117 | h.promotionNameSet = true 1118 | return h 1119 | } 1120 | 1121 | // The creative associated with the promotion. Promotion index 1122 | // must be a positive integer between 1 and 200, inclusive. 1123 | // For analytics.js the Enhanced Ecommerce plugin must be installed 1124 | // before using this field. 1125 | func (h *Client) PromotionCreative(promotionCreative string) *Client { 1126 | h.promotionCreative = promotionCreative 1127 | h.promotionCreativeSet = true 1128 | return h 1129 | } 1130 | 1131 | // The position of the creative. Promotion index must be a 1132 | // positive integer between 1 and 200, inclusive. For analytics.js 1133 | // the Enhanced Ecommerce plugin must be installed before using 1134 | // this field. 1135 | func (h *Client) PromotionPosition(promotionPosition string) *Client { 1136 | h.promotionPosition = promotionPosition 1137 | h.promotionPositionSet = true 1138 | return h 1139 | } 1140 | 1141 | // Specifies the role of the promotions included in a hit. 1142 | // If a promotion action is not specified, the default promotion 1143 | // action, 'view', is assumed. To measure a user click on a 1144 | // promotion set this to 'promo_click'. For analytics.js the 1145 | // Enhanced Ecommerce plugin must be installed before using 1146 | // this field. 1147 | func (h *Client) PromotionAction(promotionAction string) *Client { 1148 | h.promotionAction = promotionAction 1149 | h.promotionActionSet = true 1150 | return h 1151 | } 1152 | 1153 | // Each custom dimension has an associated index. There is 1154 | // a maximum of 20 custom dimensions (200 for Premium accounts). 1155 | // The dimension index must be a positive integer between 1 1156 | // and 200, inclusive. 1157 | func (h *Client) CustomDimension(customDimension string) *Client { 1158 | h.customDimension = customDimension 1159 | h.customDimensionSet = true 1160 | return h 1161 | } 1162 | 1163 | // Each custom metric has an associated index. There is a maximum 1164 | // of 20 custom metrics (200 for Premium accounts). The metric 1165 | // index must be a positive integer between 1 and 200, inclusive. 1166 | func (h *Client) CustomMetric(customMetric int64) *Client { 1167 | h.customMetric = customMetric 1168 | h.customMetricSet = true 1169 | return h 1170 | } 1171 | 1172 | // This parameter specifies that this user has been exposed 1173 | // to an experiment with the given ID. It should be sent in 1174 | // conjunction with the Experiment Variant parameter. 1175 | func (h *Client) ExperimentID(experimentID string) *Client { 1176 | h.experimentID = experimentID 1177 | h.experimentIDSet = true 1178 | return h 1179 | } 1180 | 1181 | // This parameter specifies that this user has been exposed 1182 | // to a particular variation of an experiment. It should be 1183 | // sent in conjunction with the Experiment ID parameter. 1184 | func (h *Client) ExperimentVariant(experimentVariant string) *Client { 1185 | h.experimentVariant = experimentVariant 1186 | h.experimentVariantSet = true 1187 | return h 1188 | } 1189 | 1190 | // DimensionIndex is required by other properties 1191 | func (h *Client) DimensionIndex(dimensionIndex string) *Client { 1192 | h.dimensionIndex = dimensionIndex 1193 | h.dimensionIndexSet = true 1194 | return h 1195 | } 1196 | 1197 | // ListIndex is required by other properties 1198 | func (h *Client) ListIndex(listIndex string) *Client { 1199 | h.listIndex = listIndex 1200 | h.listIndexSet = true 1201 | return h 1202 | } 1203 | 1204 | // MetricIndex is required by other properties 1205 | func (h *Client) MetricIndex(metricIndex string) *Client { 1206 | h.metricIndex = metricIndex 1207 | h.metricIndexSet = true 1208 | return h 1209 | } 1210 | 1211 | // ProductIndex is required by other properties 1212 | func (h *Client) ProductIndex(productIndex string) *Client { 1213 | h.productIndex = productIndex 1214 | h.productIndexSet = true 1215 | return h 1216 | } 1217 | 1218 | // PromoIndex is required by other properties 1219 | func (h *Client) PromoIndex(promoIndex string) *Client { 1220 | h.promoIndex = promoIndex 1221 | h.promoIndexSet = true 1222 | return h 1223 | } 1224 | 1225 | func (h *Client) Copy() *Client { 1226 | c := *h 1227 | return &c 1228 | } 1229 | --------------------------------------------------------------------------------