feat: add parameters and restructure code

This commit is contained in:
Kevin Franklin Kim 2024-03-08 09:26:22 +01:00
parent 7100ab858b
commit b45f2c844d
No known key found for this signature in database
27 changed files with 567 additions and 363 deletions

View File

@ -79,25 +79,12 @@ func (p *Publisher) Publish(topic string, messages ...*message.Message) error {
return err
}
values, body, err := mpv2.Marshal(event)
values, body, err := mpv2.Encode(event)
if err != nil {
return err
}
// NOTE: `richsstsse` seems to be last parameter in the query to let's ensure it stays that way
var richsstsse bool
if values.Has("richsstsse") {
richsstsse = true
values.Del("richsstsse")
}
url := fmt.Sprintf("%s?%s", p.url, values.Encode())
if richsstsse {
url += "&richsstsse"
}
req, err := http.NewRequestWithContext(msg.Context(), http.MethodPost, url, body)
req, err := http.NewRequestWithContext(msg.Context(), http.MethodPost, fmt.Sprintf("%s?%s", p.url, mpv2.EncodeValues(values)), body)
if err != nil {
return errors.Wrap(err, "failed to create request")
}

View File

@ -138,7 +138,7 @@ func (s *Subscriber) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// unmarshal event
var event *mpv2.Event
if err := mpv2.UnmarshalURLValues(values, &event); err != nil {
if err := mpv2.Decode(values, &event); err != nil {
keelhttputils.InternalServerError(l, w, r, errors.Wrap(err, "failed to marshal url values"))
return
}

View File

@ -1,6 +1,7 @@
package v2
import (
"fmt"
"io"
"net/http"
"net/url"
@ -129,7 +130,7 @@ func (c *Client) Send(r *http.Request, event *Event) error {
}
func (c *Client) SendRaw(r *http.Request, event *Event) error {
values, body, err := Marshal(event)
values, body, err := Encode(event)
if err != nil {
return errors.Wrap(err, "failed to marshall event")
}
@ -137,7 +138,7 @@ func (c *Client) SendRaw(r *http.Request, event *Event) error {
req, err := http.NewRequestWithContext(
r.Context(),
http.MethodPost,
c.url+"?"+values.Encode(),
fmt.Sprintf("%s?%s", c.url, EncodeValues(values)),
body,
)
if err != nil {

View File

@ -0,0 +1,13 @@
package v2
import (
"regexp"
)
var (
RegexProduct = regexp.MustCompile(`pr([1-9]|[1-9][0-9]|1[0-9]{2}|200)`)
)
const (
ParameterItem = "pr"
)

View File

@ -0,0 +1,3 @@
package v2
type Data map[string]any

View File

@ -0,0 +1,98 @@
package v2
import (
"net/url"
"regexp"
"strings"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
)
func Decode(input url.Values, output interface{}) error {
data := Data{}
// decode values
for k, v := range input {
// handle maps
if ok, err := DecodeMapValue(k, v, data); err != nil {
return err
} else if ok {
continue
}
// handle slices
if ok, err := DecodeRegexValue(k, v, RegexProduct, data, ParameterItem); err != nil {
return err
} else if ok {
continue
}
// default
v, err := url.QueryUnescape(v[0])
if err != nil {
return err
}
data[k] = v
}
if err := mapstructure.WeakDecode(data, output); err != nil {
return errors.Wrap(err, "failed to weakly decode query")
}
return nil
}
func DecodeMapValue(k string, v []string, data Data) (bool, error) {
if strings.Contains(k, ".") {
parts := strings.Split(k, ".")
if _, ok := data[parts[0]]; !ok {
data[parts[0]] = map[string]any{}
}
if value, ok := data[parts[0]].(map[string]any); ok {
v, err := url.QueryUnescape(v[0])
if err != nil {
return false, err
}
value[strings.Join(parts[1:], ".")] = v
}
return true, nil
}
return false, nil
}
// DecodeRegexValue e.g. `pr1=idSKU_123456` = map["pr"][]map["id"]="SKU_123456"
func DecodeRegexValue(k string, v []string, r *regexp.Regexp, data Data, key string) (bool, error) {
if r.MatchString(k) {
value, err := DecodeObjectValue(v[0])
if err != nil {
return false, err
}
if value != nil {
v, ok := data[key].([]map[string]any)
if !ok {
v = []map[string]any{}
}
v = append(v, value)
data[key] = v
return true, nil
}
}
return false, nil
}
// DecodeObjectValue e.g. `idSKU_123456` = map["id"]="SKU_123456"
func DecodeObjectValue(s string) (map[string]any, error) {
if len(s) == 0 {
return nil, nil //nolint:nilnil
}
ret := map[string]any{}
for _, part := range strings.Split(s, "~") {
v, err := url.QueryUnescape(part[2:])
if err != nil {
return nil, err
}
ret[part[0:2]] = v
}
return ret, nil
}

View File

@ -10,7 +10,7 @@ import (
"github.com/stretchr/testify/require"
)
func TestUnmarshalURLValues(t *testing.T) {
func TestDecode(t *testing.T) {
tests := []struct {
name string
args string
@ -32,48 +32,15 @@ func TestUnmarshalURLValues(t *testing.T) {
u, err := url.ParseQuery(tt.args)
require.NoError(t, err)
e := &mpv2.Event{}
if err := mpv2.UnmarshalURLValues(u, e); !assert.ErrorIs(t, err, tt.want) {
t.Errorf("UnmarshalURLValues() = %v, want %v", err, tt.want)
if err := mpv2.Decode(u, e); !assert.ErrorIs(t, err, tt.want) {
t.Errorf("Decode() = %v, want %v", err, tt.want)
t.Log(e)
}
})
}
}
func TestMarshalURLValues(t *testing.T) {
tests := []struct {
name string
args string
want error
}{
{
name: "page_view",
args: "v=2&tid=G-123456&gtm=45je42s0v893689383z8894072534za200&_p=1709286636310&gcd=13l3l3l3l1&npa=0&dma_cps=sypham&dma=1&cid=292234677.1707898933&ul=en-us&sr=3840x1600&uaa=arm&uab=64&uafvl=Chromium&uamb=0&uam=&uap=macOS&uapv=14.3.1&uaw=0&are=1&pae=1&pscdl=noapi&_s=1&sid=1709286452&sct=7&seg=1&dl=https%3A%2F%2Fwww.homemade.ch%2Fprodukt%2Fkuhn-rikon-wiegemesser-900047100%3Fid%3D900047100&dr=https%3A%2F%2Fwww.homemade.ch%2Fmesser-besteck&dt=Wiegemesser&en=page_view&tfd=5682",
want: nil,
},
//{
// name: "add_to_cart",
// args: "v=2&tid=G-123456&gtm=45je42s0v9175354889z89175348963za200&_p=1709297934217&_dbg=1&gcd=13l3l3l3l1&npa=0&dma_cps=sypham&dma=1&cid=1220643501.1708014725&ul=en-us&sr=3840x1600&_fplc=0&ur=DE-BY&uaa=arm&uab=64&uafvl=Chromium&uamb=0&uam=&uap=macOS&uapv=14.3.1&uaw=0&are=1&pscdl=noapi&_eu=IA&sst.uc=DE&sst.etld=google.de&sst.gcsub=region1&sst.gcd=13l3l3l3l1&sst.tft=1709297934217&_s=8&cu=USD&sid=1709296380&sct=7&seg=1&dl=https%3A%2F%2Fsniffer.cloud.bestbytes.net%2F%3Fgtm_debug%3D1709297933868&dr=https%3A%2F%2Ftagassistant.google.com%2F&dt=Server%20Side%20Tracking%20Prototype%20(codename%3A%20sniffer)&en=add_to_cart&pr1=idSKU_12345~nmStan%20and%20Friends%20Tee~afGoogle%20Store~cpSUMMER_FUN~ds2.22~lp5~brGoogle~caApparel~c2Adult~c3Shirts~c4Crew~c5Short%20sleeve~lirelated_products~lnRelated%20products~vagreen~loChIJIQBpAG2ahYAR_6128GcTUEo~pr10.01~qt3&epn.value=30.03&tfd=15129&richsstsse",
// want: nil,
// },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
u, err := url.ParseQuery(tt.args)
require.NoError(t, err)
e := &mpv2.Event{}
require.NoError(t, mpv2.UnmarshalURLValues(u, e))
if !assert.Empty(t, e.Unknown) {
t.Errorf("MarshalURLValues() = %v, want %v", e.Unknown, nil)
}
if out, _, err := mpv2.Marshal(e); assert.NoError(t, err) {
assert.EqualValues(t, u, out)
}
})
}
}
func TestCollect_decodeMapValue(t *testing.T) {
func TestDecodeMapValue(t *testing.T) {
tests := []struct {
name string
args string
@ -99,7 +66,7 @@ func TestCollect_decodeMapValue(t *testing.T) {
require.NoError(t, err)
for k, v := range values {
if got, err := mpv2.DecodeMapValue(k, v, d); assert.NoError(t, err) && got != tt.want {
t.Errorf("decodeMapValue() = %v, want %v", got, tt.want)
t.Errorf("DecodeMapValue() = %v, want %v", got, tt.want)
t.Log(d)
}
}

View File

@ -0,0 +1,102 @@
package v2
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/url"
"sort"
"strings"
"github.com/pkg/errors"
)
func Encode(input *Event) (url.Values, io.Reader, error) {
var richsstsse bool
// NOTE: `richsstsse` seems to be last parameter in the query to let's ensure it stays that way
if input.Richsstsse != nil {
richsstsse = true
input.Richsstsse = nil
}
a, err := json.Marshal(input)
if err != nil {
return nil, nil, err
}
data := Data{}
if err := json.Unmarshal(a, &data); err != nil {
return nil, nil, errors.Wrap(err, "failed to decode into map")
}
ret := url.Values{}
for k, v := range data {
switch t := v.(type) {
case []interface{}:
for key, value := range t {
switch tt := value.(type) {
case map[string]interface{}:
ret[fmt.Sprintf("%s%d", k, key+1)] = []string{EncodeObjectValue(tt)}
default:
panic("unhandled")
}
}
case map[string]string:
for key, value := range t {
ret[fmt.Sprintf("%s.%s", k, key)] = []string{value}
}
case map[string]interface{}:
for key, value := range t {
ret[fmt.Sprintf("%s.%s", k, key)] = []string{fmt.Sprintf("%v", value)}
}
case *string:
ret[k] = []string{*t}
case string:
ret[k] = []string{t}
default:
panic("unhandled")
}
}
var body []string
var reader io.Reader
maxQueryLength := 2048 //
if richsstsse {
maxQueryLength -= len("&richsstsse")
}
for len(ret.Encode()) > maxQueryLength {
for s, i := range ret {
ret.Del(s)
body = append(body, s+"="+i[0])
break
}
}
if richsstsse {
ret.Add("richsstsse", "")
}
if len(body) > 0 {
reader = bytes.NewReader([]byte(strings.Join(body, "&")))
}
return ret, reader, nil
}
// EncodeObjectValue e.g. `idSKU_123456` = map["id"]="SKU_123456"
func EncodeObjectValue(s map[string]any) string {
if len(s) == 0 {
return ""
}
keys := make([]string, 0, len(s))
for k := range s {
keys = append(keys, k)
}
sort.Strings(keys)
ret := make([]string, 0, len(keys))
for _, k := range keys {
ret = append(ret, k+fmt.Sprintf("%s", s[k]))
}
return strings.Join(ret, "~")
}

View File

@ -0,0 +1,43 @@
package v2_test
import (
"net/url"
"testing"
mpv2 "github.com/foomo/sesamy-go/measurementprotocol/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestEncode(t *testing.T) {
tests := []struct {
name string
args string
want error
}{
{
name: "page_view",
args: "v=2&tid=G-123456&gtm=45je42s0v893689383z8894072534za200&_p=1709286636310&gcd=13l3l3l3l1&npa=0&dma_cps=sypham&dma=1&cid=292234677.1707898933&ul=en-us&sr=3840x1600&uaa=arm&uab=64&uafvl=Chromium&uamb=0&uam=&uap=macOS&uapv=14.3.1&uaw=0&are=1&pae=1&pscdl=noapi&_s=1&sid=1709286452&sct=7&seg=1&dl=https%3A%2F%2Fwww.homemade.ch%2Fprodukt%2Fkuhn-rikon-wiegemesser-900047100%3Fid%3D900047100&dr=https%3A%2F%2Fwww.homemade.ch%2Fmesser-besteck&dt=Wiegemesser&en=page_view&tfd=5682",
want: nil,
},
// {
// name: "add_to_cart",
// args: "v=2&tid=G-123456&gtm=45je42s0v9175354889z89175348963za200&_p=1709297934217&_dbg=1&gcd=13l3l3l3l1&npa=0&dma_cps=sypham&dma=1&cid=1220643501.1708014725&ul=en-us&sr=3840x1600&_fplc=0&ur=DE-BY&uaa=arm&uab=64&uafvl=Chromium&uamb=0&uam=&uap=macOS&uapv=14.3.1&uaw=0&are=1&pscdl=noapi&_eu=IA&sst.uc=DE&sst.etld=google.de&sst.gcsub=region1&sst.gcd=13l3l3l3l1&sst.tft=1709297934217&_s=8&cu=USD&sid=1709296380&sct=7&seg=1&dl=https%3A%2F%2Fsniffer.cloud.bestbytes.net%2F%3Fgtm_debug%3D1709297933868&dr=https%3A%2F%2Ftagassistant.google.com%2F&dt=Server%20Side%20Tracking%20Prototype%20(codename%3A%20sniffer)&en=add_to_cart&pr1=idSKU_12345~nmStan%20and%20Friends%20Tee~afGoogle%20Store~cpSUMMER_FUN~ds2.22~lp5~brGoogle~caApparel~c2Adult~c3Shirts~c4Crew~c5Short%20sleeve~lirelated_products~lnRelated%20products~vagreen~loChIJIQBpAG2ahYAR_6128GcTUEo~pr10.01~qt3&epn.value=30.03&tfd=15129&richsstsse",
// want: nil,
// },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
u, err := url.ParseQuery(tt.args)
require.NoError(t, err)
e := &mpv2.Event{}
require.NoError(t, mpv2.Decode(u, e))
if !assert.Empty(t, e.Unknown) {
t.Errorf("Encode() = %v, want %v", e.Unknown, nil)
}
if out, _, err := mpv2.Encode(e); assert.NoError(t, err) {
assert.EqualValues(t, u, out)
}
})
}
}

View File

@ -40,17 +40,17 @@ AddPaymentInfo https://developers.google.com/tag-platform/gtagjs/reference/event
Query: v=2&tid=G-123456&gtm=45je42t1v9177778896z89175355532za200&_p=1709325262551&gcd=13l3l3l3l1&npa=0&dma_cps=sypham&dma=1&cid=1220643501.1708014725&ul=en-us&sr=3840x1600&_fplc=0&ur=DE-BY&uaa=arm&uab=64&uafvl=Chromium%3B122.0.6261.69%7CNot(A%253ABrand%3B24.0.0.0%7CGoogle%2520Chrome%3B122.0.6261.69&uamb=0&uam=&uap=macOS&uapv=14.3.1&uaw=0&are=1&pscdl=noapi&_eu=IA&sst.uc=DE&sst.etld=google.de&sst.gcsub=region1&sst.gcd=13l3l3l3l1&sst.tft=1709325262551&_s=5&cu=USD&sid=1709445696&sct=8&seg=0&dl=https%3A%2F%2Fsniffer.local.bestbytes.net%2F&dt=Server%20Side%20Tracking%20Prototype%20(codename%3A%20sniffer)&en=add_payment_info&_ss=1&pr1=idSKU_12345~nmStan%20and%20Friends%20Tee~afGoogle%20Store~cpSUMMER_FUN~ds2.22~lp5~brGoogle~caApparel~c2Adult~c3Shirts~c4Crew~c5Short%20sleeve~lirelated_products~lnRelated%20products~vagreen~loChIJIQBpAG2ahYAR_6128GcTUEo~pr10.01~qt3&epn.value=30.03&ep.coupon=SUMMER_FUN&ep.payment_type=Credit%20Card&tfd=120434230&richsstsse
*/
type AddPaymentInfo struct {
Currency string
Value float64
Coupon string
PaymentType string
Items []*Item
Currency string `json:"currency,omitempty"`
Value float64 `json:"value,omitempty"`
Coupon string `json:"coupon,omitempty"`
PaymentType string `json:"payment_type,omitempty"`
Items []*Item `json:"items,omitempty"`
}
func (e *AddPaymentInfo) MPv2() *mpv2.Event {
func (e *AddPaymentInfo) MarshalMPv2() (*mpv2.Event, error) {
items := make([]*mpv2.Item, len(e.Items))
for i, item := range e.Items {
items[i] = item.MPv2()
items[i] = item.MarshalMPv2()
}
eventParameter := map[string]string{}
mp.AddStringMap(eventParameter, mpv2.EventParameterCoupon.String(), mp.SetString(e.Coupon))
@ -63,5 +63,5 @@ func (e *AddPaymentInfo) MPv2() *mpv2.Event {
EventParameter: mp.SetStringMap(eventParameter),
EventParameterNumber: mp.SetStringMap(eventParameterNumber),
Items: items,
}
}, nil
}

View File

@ -40,17 +40,17 @@ AddShippingInfo https://developers.google.com/tag-platform/gtagjs/reference/even
Query: v=2&tid=G-PZ5ELRCR31&gtm=45je42t1v9177778896z89175355532za220&_p=1709357665402&_dbg=1&gcd=13l3l3l3l1&npa=0&dma_cps=sypham&dma=1&cid=1220643501.1708014725&ul=en-us&sr=3840x1600&_fplc=0&ur=DE-BY&uaa=arm&uab=64&uafvl=Chromium%3B122.0.6261.69%7CNot(A%253ABrand%3B24.0.0.0%7CGoogle%2520Chrome%3B122.0.6261.69&uamb=0&uam=&uap=macOS&uapv=14.3.1&uaw=0&are=1&pscdl=noapi&_eu=IA&sst.uc=DE&sst.etld=google.de&sst.gcsub=region1&sst.gcd=13l3l3l3l1&sst.tft=1709357665402&_s=10&cu=USD&sid=1709529821&sct=9&seg=0&dl=https%3A%2F%2Fsniffer.local.bestbytes.net%2F%3Fgtm_debug%3D1709357665301&dr=https%3A%2F%2Ftagassistant.google.com%2F&dt=Server%20Side%20Tracking%20Prototype%20(codename%3A%20sniffer)&en=add_payment_info&_ss=1&pr1=idSKU_12345~nmStan%20and%20Friends%20Tee~afGoogle%20Store~cpSUMMER_FUN~ds2.22~lp5~brGoogle~caApparel~c2Adult~c3Shirts~c4Crew~c5Short%20sleeve~lirelated_products~lnRelated%20products~vagreen~loChIJIQBpAG2ahYAR_6128GcTUEo~pr10.01~qt3&epn.value=30.03&ep.coupon=SUMMER_FUN&ep.shipping_tier=Ground&tfd=137238301&richsstsse
*/
type AddShippingInfo struct {
Currency string
Value float64
Coupon string
ShippingTier string
Items []*Item
Currency string `json:"currency,omitempty"`
Value float64 `json:"value,omitempty"`
Coupon string `json:"coupon,omitempty"`
ShippingTier string `json:"shipping_tier,omitempty"`
Items []*Item `json:"items,omitempty"`
}
func (e *AddShippingInfo) MPv2() *mpv2.Event {
func (e *AddShippingInfo) MarshalMPv2() (*mpv2.Event, error) {
items := make([]*mpv2.Item, len(e.Items))
for i, item := range e.Items {
items[i] = item.MPv2()
items[i] = item.MarshalMPv2()
}
eventParameter := map[string]string{}
mp.AddStringMap(eventParameter, mpv2.EventParameterCoupon.String(), mp.SetString(e.Coupon))
@ -63,5 +63,5 @@ func (e *AddShippingInfo) MPv2() *mpv2.Event {
EventParameter: mp.SetStringMap(eventParameter),
EventParameterNumber: mp.SetStringMap(eventParameterNumber),
Items: items,
}
}, nil
}

View File

@ -38,15 +38,15 @@ AddToCart https://developers.google.com/tag-platform/gtagjs/reference/events#add
Query: v=2&tid=G-PZ5ELRCR31&gtm=45je42t1v9177778896z89175355532za200&_p=1709325262551&gcd=13l3l3l3l1&npa=0&dma_cps=sypham&dma=1&cid=1220643501.1708014725&ul=en-us&sr=3840x1600&_fplc=0&ur=DE-BY&uaa=arm&uab=64&uafvl=Chromium%3B122.0.6261.69%7CNot(A%253ABrand%3B24.0.0.0%7CGoogle%2520Chrome%3B122.0.6261.69&uamb=0&uam=&uap=macOS&uapv=14.3.1&uaw=0&are=1&pscdl=noapi&_eu=IA&sst.uc=DE&sst.etld=google.de&sst.gcsub=region1&sst.gcd=13l3l3l3l1&sst.tft=1709325262551&_s=3&cu=USD&sid=1709324719&sct=6&seg=1&dl=https%3A%2F%2Fsniffer.local.bestbytes.net%2F&dt=Server%20Side%20Tracking%20Prototype%20(codename%3A%20sniffer)&en=add_to_cart&pr1=idSKU_12345~nmStan%20and%20Friends%20Tee~afGoogle%20Store~cpSUMMER_FUN~ds2.22~lp5~brGoogle~caApparel~c2Adult~c3Shirts~c4Crew~c5Short%20sleeve~lirelated_products~lnRelated%20products~vagreen~loChIJIQBpAG2ahYAR_6128GcTUEo~pr10.01~qt3&epn.value=30.03&_et=3187&tfd=11387&richsstsse
*/
type AddToCart struct {
Currency string
Value float64
Items []*Item
Currency string `json:"currency,omitempty"`
Value float64 `json:"value,omitempty"`
Items []*Item `json:"items,omitempty"`
}
func (e *AddToCart) MPv2() *mpv2.Event {
func (e *AddToCart) MarshalMPv2() (*mpv2.Event, error) {
items := make([]*mpv2.Item, len(e.Items))
for i, item := range e.Items {
items[i] = item.MPv2()
items[i] = item.MarshalMPv2()
}
eventParameterNumber := map[string]string{}
mp.AddStringMap(eventParameterNumber, mpv2.EventParameterNumberValue.String(), mp.SetFloat64(e.Value))
@ -56,5 +56,5 @@ func (e *AddToCart) MPv2() *mpv2.Event {
EventParameterNumber: mp.SetStringMap(eventParameterNumber),
Items: items,
Richsstsse: mp.Set(""),
}
}, nil
}

View File

@ -38,15 +38,15 @@ AddToWishlist https://developers.google.com/tag-platform/gtagjs/reference/events
Query: v=2&tid=G-PZ5ELRCR31&gtm=45je42t1v9177778896z89175355532za200&_p=1709325262551&gcd=13l3l3l3l1&npa=0&dma_cps=sypham&dma=1&cid=1220643501.1708014725&ul=en-us&sr=3840x1600&_fplc=0&ur=DE-BY&uaa=arm&uab=64&uafvl=Chromium%3B122.0.6261.69%7CNot(A%253ABrand%3B24.0.0.0%7CGoogle%2520Chrome%3B122.0.6261.69&uamb=0&uam=&uap=macOS&uapv=14.3.1&uaw=0&are=1&pscdl=noapi&_eu=IA&sst.uc=DE&sst.etld=google.de&sst.gcsub=region1&sst.gcd=13l3l3l3l1&sst.tft=1709325262551&_s=3&cu=USD&sid=1709324719&sct=6&seg=1&dl=https%3A%2F%2Fsniffer.local.bestbytes.net%2F&dt=Server%20Side%20Tracking%20Prototype%20(codename%3A%20sniffer)&en=add_to_wishlist&pr1=idSKU_12345~nmStan%20and%20Friends%20Tee~afGoogle%20Store~cpSUMMER_FUN~ds2.22~lp5~brGoogle~caApparel~c2Adult~c3Shirts~c4Crew~c5Short%20sleeve~lirelated_products~lnRelated%20products~vagreen~loChIJIQBpAG2ahYAR_6128GcTUEo~pr10.01~qt3&epn.value=30.03&_et=3187&tfd=11387&richsstsse
*/
type AddToWishlist struct {
Currency string
Value float64
Items []*Item
Currency string `json:"currency,omitempty"`
Value float64 `json:"value,omitempty"`
Items []*Item `json:"items,omitempty"`
}
func (e *AddToWishlist) MPv2() *mpv2.Event {
func (e *AddToWishlist) MarshalMPv2() (*mpv2.Event, error) {
items := make([]*mpv2.Item, len(e.Items))
for i, item := range e.Items {
items[i] = item.MPv2()
items[i] = item.MarshalMPv2()
}
eventParameterNumber := map[string]string{}
mp.AddStringMap(eventParameterNumber, mpv2.EventParameterNumberValue.String(), mp.SetFloat64(e.Value))
@ -55,5 +55,5 @@ func (e *AddToWishlist) MPv2() *mpv2.Event {
Currency: mp.SetString(e.Currency),
EventParameterNumber: mp.SetStringMap(eventParameterNumber),
Items: items,
}
}, nil
}

View File

@ -39,16 +39,16 @@ BeginCheckout https://developers.google.com/tag-platform/gtagjs/reference/events
Query: v=2&tid=G-123456&gtm=45je42t1v9177778896z89175355532za200&_p=1709325262551&gcd=13l3l3l3l1&npa=0&dma_cps=sypham&dma=1&cid=1220643501.1708014725&ul=en-us&sr=3840x1600&_fplc=0&ur=DE-BY&uaa=arm&uab=64&uafvl=Chromium%3B122.0.6261.69%7CNot(A%253ABrand%3B24.0.0.0%7CGoogle%2520Chrome%3B122.0.6261.69&uamb=0&uam=&uap=macOS&uapv=14.3.1&uaw=0&are=1&pscdl=noapi&_eu=IA&sst.uc=DE&sst.etld=google.de&sst.gcsub=region1&sst.gcd=13l3l3l3l1&sst.tft=1709325262551&_s=5&cu=USD&sid=1709445696&sct=8&seg=0&dl=https%3A%2F%2Fsniffer.local.bestbytes.net%2F&dt=Server%20Side%20Tracking%20Prototype%20(codename%3A%20sniffer)&en=begin_checkout&_ss=1&pr1=idSKU_12345~nmStan%20and%20Friends%20Tee~afGoogle%20Store~cpSUMMER_FUN~ds2.22~lp5~brGoogle~caApparel~c2Adult~c3Shirts~c4Crew~c5Short%20sleeve~lirelated_products~lnRelated%20products~vagreen~loChIJIQBpAG2ahYAR_6128GcTUEo~pr10.01~qt3&epn.value=30.03&ep.coupon=SUMMER_FUN&tfd=120434230&richsstsse
*/
type BeginCheckout struct {
Currency string
Value float64
Coupon string
Items []*Item
Currency string `json:"currency,omitempty"`
Value float64 `json:"value,omitempty"`
Coupon string `json:"coupon,omitempty"`
Items []*Item `json:"items,omitempty"`
}
func (e *BeginCheckout) MPv2() *mpv2.Event {
func (e *BeginCheckout) MarshalMPv2() (*mpv2.Event, error) {
items := make([]*mpv2.Item, len(e.Items))
for i, item := range e.Items {
items[i] = item.MPv2()
items[i] = item.MarshalMPv2()
}
eventParameter := map[string]string{}
mp.AddStringMap(eventParameter, mpv2.EventParameterCoupon.String(), mp.SetString(e.Coupon))
@ -60,5 +60,5 @@ func (e *BeginCheckout) MPv2() *mpv2.Event {
EventParameter: mp.SetStringMap(eventParameter),
EventParameterNumber: mp.SetStringMap(eventParameterNumber),
Items: items,
}
}, nil
}

View File

@ -6,43 +6,43 @@ import (
)
type Item struct {
ID string
Name string
Brand string
CategoryHierarchy1 string
CategoryHierarchy2 string
CategoryHierarchy3 string
CategoryHierarchy4 string
CategoryHierarchy5 string
Price string
Quantity float64
Variant string
Coupon string
Discount float64
Index int
ListName string
ListID string
Affiliation string
LocationID string
Affiliation string `json:"affiliation,omitempty"`
Coupon string `json:"coupon,omitempty"`
Discount float64 `json:"discount,omitempty"`
Index int `json:"index,omitempty"`
ItemBrand string `json:"item_brand,omitempty"`
ItemCategory string `json:"item_category,omitempty"`
ItemCategory2 string `json:"item_category2,omitempty"`
ItemCategory3 string `json:"item_category3,omitempty"`
ItemCategory4 string `json:"item_category4,omitempty"`
ItemCategory5 string `json:"item_category5,omitempty"`
ItemID string `json:"item_id,omitempty"`
ItemListName string `json:"item_list_name,omitempty"`
ItemName string `json:"item_name,omitempty"`
ItemVariant string `json:"item_variant,omitempty"`
ItemListID string `json:"listId,omitempty"`
LocationID string `json:"location_id,omitempty"`
Price string `json:"price,omitempty"`
Quantity float64 `json:"quantity,omitempty"`
}
func (e *Item) MPv2() *mpv2.Item {
func (e *Item) MarshalMPv2() *mpv2.Item {
return &mpv2.Item{
ID: mp.SetString(e.ID),
Name: mp.SetString(e.Name),
Brand: mp.SetString(e.Brand),
CategoryHierarchy1: mp.SetString(e.CategoryHierarchy1),
CategoryHierarchy2: mp.SetString(e.CategoryHierarchy2),
CategoryHierarchy3: mp.SetString(e.CategoryHierarchy3),
CategoryHierarchy4: mp.SetString(e.CategoryHierarchy4),
CategoryHierarchy5: mp.SetString(e.CategoryHierarchy5),
ID: mp.SetString(e.ItemID),
Name: mp.SetString(e.ItemName),
Brand: mp.SetString(e.ItemBrand),
CategoryHierarchy1: mp.SetString(e.ItemCategory),
CategoryHierarchy2: mp.SetString(e.ItemCategory2),
CategoryHierarchy3: mp.SetString(e.ItemCategory3),
CategoryHierarchy4: mp.SetString(e.ItemCategory4),
CategoryHierarchy5: mp.SetString(e.ItemCategory5),
Price: mp.SetString(e.Price),
Quantity: mp.SetFloat64(e.Quantity),
Variant: mp.SetString(e.Variant),
Variant: mp.SetString(e.ItemVariant),
Coupon: mp.SetString(e.Coupon),
Discount: mp.SetFloat64(e.Discount),
ListName: mp.SetString(e.ListName),
ListID: mp.SetString(e.ListID),
ListName: mp.SetString(e.ItemListName),
ListID: mp.SetString(e.ItemListID),
ListPosition: mp.SetInt(e.Index),
Affiliation: mp.SetString(e.Affiliation),
LocationID: mp.SetString(e.LocationID),

View File

@ -15,14 +15,14 @@ Login - Send this event to signify that a user has logged in to your website or
Reference: https://developers.google.com/tag-platform/gtagjs/reference/events#login
*/
type Login struct {
Method string
Method string `json:"method,omitempty"`
}
func (e *Login) MPv2() *mpv2.Event {
func (e *Login) MarshalMPv2() (*mpv2.Event, error) {
eventParameter := map[string]string{}
mp.AddStringMap(eventParameter, mpv2.EventParameterMethod.String(), mp.SetString(e.Method))
return &mpv2.Event{
EventName: mp.Set(mpv2.EventNameLogin),
EventParameter: mp.SetStringMap(eventParameter),
}
}, nil
}

View File

@ -42,19 +42,19 @@ Purchase https://developers.google.com/tag-platform/gtagjs/reference/events#purc
Query: v=2&tid=G-PZ5ELRCR31&gtm=45he42t1v9177778896z89175355532za220&_p=1709537217366&_dbg=1&gcd=13l3l3l3l1&npa=0&dma_cps=sypham&dma=1&cid=1220643501.1708014725&ul=en-us&sr=3840x1600&_fplc=0&ur=&uaa=arm&uab=64&uafvl=Chromium%3B122.0.6261.69%7CNot(A%253ABrand%3B24.0.0.0%7CGoogle%2520Chrome%3B122.0.6261.69&uamb=0&uam=&uap=macOS&uapv=14.3.1&uaw=0&are=1&pscdl=noapi&_eu=IA&sst.uc=&sst.gcd=13l3l3l3l1&sst.tft=1709537217366&_s=6&cu=USD&sid=1709534872&sct=10&seg=1&dl=https%3A%2F%2Fsniffer.local.bestbytes.net%2F%3Fgtm_debug%3D1709537217296&dr=https%3A%2F%2Ftagassistant.google.com%2F&dt=Server%20Side%20Tracking%20Prototype%20(codename%3A%20sniffer)&en=purchase&_c=1&pr1=idSKU_12345~nmStan%20and%20Friends%20Tee~afGoogle%20Store~cpSUMMER_FUN~ds2.22~lp5~brGoogle~caApparel~c2Adult~c3Shirts~c4Crew~c5Short%20sleeve~lirelated_products~lnRelated%20products~vagreen~loChIJIQBpAG2ahYAR_6128GcTUEo~pr10.01~qt3&epn.value=30.03&ep.transaction_id=T_12345&ep.coupon=SUMMER_FUN&epn.shipping=3.33&epn.tax=1.11&tfd=184622&richsstsse
*/
type Purchase struct {
Currency string
Value float64
TransactionID string
Coupon string
Shipping float64
Tax float64
Items []*Item
Currency string `json:"currency,omitempty"`
Value float64 `json:"value,omitempty"`
TransactionID string `json:"transaction_id,omitempty"`
Coupon string `json:"coupon,omitempty"`
Shipping float64 `json:"shipping,omitempty"`
Tax float64 `json:"tax,omitempty"`
Items []*Item `json:"items,omitempty"`
}
func (e *Purchase) MPv2() *mpv2.Event {
func (e *Purchase) MarshalMPv2() (*mpv2.Event, error) {
items := make([]*mpv2.Item, len(e.Items))
for i, item := range e.Items {
items[i] = item.MPv2()
items[i] = item.MarshalMPv2()
}
eventParameter := map[string]string{}
mp.AddStringMap(eventParameter, mpv2.EventParameterTransactionID.String(), mp.SetString(e.TransactionID))
@ -69,5 +69,5 @@ func (e *Purchase) MPv2() *mpv2.Event {
EventParameter: mp.SetStringMap(eventParameter),
EventParameterNumber: mp.SetStringMap(eventParameterNumber),
Items: items,
}
}, nil
}

View File

@ -42,19 +42,19 @@ Refund https://developers.google.com/tag-platform/gtagjs/reference/events#refund
Query: v=2&tid=G-PZ5ELRCR31&gtm=45he42t1v9177778896z89175355532za220&_p=1709537217366&_dbg=1&gcd=13l3l3l3l1&npa=0&dma_cps=sypham&dma=1&cid=1220643501.1708014725&ul=en-us&sr=3840x1600&_fplc=0&ur=&uaa=arm&uab=64&uafvl=Chromium%3B122.0.6261.69%7CNot(A%253ABrand%3B24.0.0.0%7CGoogle%2520Chrome%3B122.0.6261.69&uamb=0&uam=&uap=macOS&uapv=14.3.1&uaw=0&are=1&pscdl=noapi&_eu=IA&sst.uc=&sst.gcd=13l3l3l3l1&sst.tft=1709537217366&_s=6&cu=USD&sid=1709534872&sct=10&seg=1&dl=https%3A%2F%2Fsniffer.local.bestbytes.net%2F%3Fgtm_debug%3D1709537217296&dr=https%3A%2F%2Ftagassistant.google.com%2F&dt=Server%20Side%20Tracking%20Prototype%20(codename%3A%20sniffer)&en=refund&_c=1&pr1=idSKU_12345~nmStan%20and%20Friends%20Tee~afGoogle%20Store~cpSUMMER_FUN~ds2.22~lp5~brGoogle~caApparel~c2Adult~c3Shirts~c4Crew~c5Short%20sleeve~lirelated_products~lnRelated%20products~vagreen~loChIJIQBpAG2ahYAR_6128GcTUEo~pr10.01~qt3&epn.value=30.03&ep.transaction_id=T_12345&ep.coupon=SUMMER_FUN&epn.shipping=3.33&epn.tax=1.11&tfd=184622&richsstsse
*/
type Refund struct {
Currency string
Value float64
TransactionID string
Coupon string
Shipping float64
Tax float64
Items []*Item
Currency string `json:"currency,omitempty"`
Value float64 `json:"value,omitempty"`
TransactionID string `json:"transaction_id,omitempty"`
Coupon string `json:"coupon,omitempty"`
Shipping float64 `json:"shipping,omitempty"`
Tax float64 `json:"tax,omitempty"`
Items []*Item `json:"items,omitempty"`
}
func (e *Refund) MPv2() *mpv2.Event {
func (e *Refund) MarshalMPv2() (*mpv2.Event, error) {
items := make([]*mpv2.Item, len(e.Items))
for i, item := range e.Items {
items[i] = item.MPv2()
items[i] = item.MarshalMPv2()
}
eventParameter := map[string]string{}
mp.AddStringMap(eventParameter, mpv2.EventParameterTransactionID.String(), mp.SetString(e.TransactionID))
@ -69,5 +69,5 @@ func (e *Refund) MPv2() *mpv2.Event {
EventParameter: mp.SetStringMap(eventParameter),
EventParameterNumber: mp.SetStringMap(eventParameterNumber),
Items: items,
}
}, nil
}

View File

@ -38,15 +38,15 @@ RemoveFromCart https://developers.google.com/tag-platform/gtagjs/reference/event
Query: v=2&tid=G-PZ5ELRCR31&gtm=45je42t1v9177778896z89175355532za200&_p=1709325262551&gcd=13l3l3l3l1&npa=0&dma_cps=sypham&dma=1&cid=1220643501.1708014725&ul=en-us&sr=3840x1600&_fplc=0&ur=DE-BY&uaa=arm&uab=64&uafvl=Chromium%3B122.0.6261.69%7CNot(A%253ABrand%3B24.0.0.0%7CGoogle%2520Chrome%3B122.0.6261.69&uamb=0&uam=&uap=macOS&uapv=14.3.1&uaw=0&are=1&pscdl=noapi&_eu=IA&sst.uc=DE&sst.etld=google.de&sst.gcsub=region1&sst.gcd=13l3l3l3l1&sst.tft=1709325262551&_s=3&cu=USD&sid=1709324719&sct=6&seg=1&dl=https%3A%2F%2Fsniffer.local.bestbytes.net%2F&dt=Server%20Side%20Tracking%20Prototype%20(codename%3A%20sniffer)&en=remove_from_cart&pr1=idSKU_12345~nmStan%20and%20Friends%20Tee~afGoogle%20Store~cpSUMMER_FUN~ds2.22~lp5~brGoogle~caApparel~c2Adult~c3Shirts~c4Crew~c5Short%20sleeve~lirelated_products~lnRelated%20products~vagreen~loChIJIQBpAG2ahYAR_6128GcTUEo~pr10.01~qt3&epn.value=30.03&_et=3187&tfd=11387&richsstsse
*/
type RemoveFromCart struct {
Currency string
Value float64
Items []*Item
Currency string `json:"currency,omitempty"`
Value float64 `json:"value,omitempty"`
Items []*Item `json:"items,omitempty"`
}
func (e *RemoveFromCart) MPv2() *mpv2.Event {
func (e *RemoveFromCart) MarshalMPv2() (*mpv2.Event, error) {
items := make([]*mpv2.Item, len(e.Items))
for i, item := range e.Items {
items[i] = item.MPv2()
items[i] = item.MarshalMPv2()
}
eventParameterNumber := map[string]string{}
mp.AddStringMap(eventParameterNumber, mpv2.EventParameterNumberValue.String(), mp.SetFloat64(e.Value))
@ -55,5 +55,5 @@ func (e *RemoveFromCart) MPv2() *mpv2.Event {
Currency: mp.SetString(e.Currency),
EventParameterNumber: mp.SetStringMap(eventParameterNumber),
Items: items,
}
}, nil
}

View File

@ -15,14 +15,14 @@ Search https://developers.google.com/tag-platform/gtagjs/reference/events#search
Query: v=2&tid=G-PZ5ELRCR31&gtm=45je42t1v9177778896z89175355532za200&_p=1709325262551&gcd=13l3l3l3l1&npa=0&dma_cps=sypham&dma=1&cid=1220643501.1708014725&ul=en-us&sr=3840x1600&_fplc=0&ur=DE-BY&uaa=arm&uab=64&uafvl=Chromium%3B122.0.6261.69%7CNot(A%253ABrand%3B24.0.0.0%7CGoogle%2520Chrome%3B122.0.6261.69&uamb=0&uam=&uap=macOS&uapv=14.3.1&uaw=0&are=1&pscdl=noapi&_eu=IA&sst.uc=DE&sst.etld=google.de&sst.gcsub=region1&sst.gcd=13l3l3l3l1&sst.tft=1709325262551&_s=3&cu=USD&sid=1709324719&sct=6&seg=1&dl=https%3A%2F%2Fsniffer.local.bestbytes.net%2F&dt=Server%20Side%20Tracking%20Prototype%20(codename%3A%20sniffer)&en=search&pr1=idSKU_12345~nmStan%20and%20Friends%20Tee~afGoogle%20Store~cpSUMMER_FUN~ds2.22~lp5~brGoogle~caApparel~c2Adult~c3Shirts~c4Crew~c5Short%20sleeve~lirelated_products~lnRelated%20products~vagreen~loChIJIQBpAG2ahYAR_6128GcTUEo~pr10.01~qt3&epn.value=30.03&_et=3187&tfd=11387&richsstsse
*/
type Search struct {
SearchTerm string
SearchTerm string `json:"search_term,omitempty"`
}
func (e *Search) MPv2() *mpv2.Event {
func (e *Search) MarshalMPv2() (*mpv2.Event, error) {
eventParameter := map[string]string{}
mp.AddStringMap(eventParameter, mpv2.EventParameterSearchTerm.String(), mp.SetString(e.SearchTerm))
return &mpv2.Event{
EventName: mp.Set(mpv2.EventNameSearch),
EventParameter: mp.SetStringMap(eventParameter),
}
}, nil
}

View File

@ -15,14 +15,14 @@ SignUp - Send this event to signify that a user has logged in to your website or
Reference: https://developers.google.com/tag-platform/gtagjs/reference/events#sign_up
*/
type SignUp struct {
Method string
Method string `json:"method,omitempty"`
}
func (e *SignUp) MPv2() *mpv2.Event {
func (e *SignUp) MarshalMPv2() (*mpv2.Event, error) {
eventParameter := map[string]string{}
mp.AddStringMap(eventParameter, mpv2.EventParameterMethod.String(), mp.SetString(e.Method))
return &mpv2.Event{
EventName: mp.Set(mpv2.EventNameSignUp),
EventParameter: mp.SetStringMap(eventParameter),
}
}, nil
}

View File

@ -1,14 +1,162 @@
package v2
// EventParameter as string
// See https://support.google.com/analytics/table/13594742?sjid=7861230991468479976-EU
type EventParameter string
const (
// EventParameterAppVersion The mobile app's versionName (Android) or short bundle version (iOS) in which an event occurred
EventParameterAppVersion EventParameter = "app_version"
// EventParameterCancellationReason The reason a cancellation occurred
EventParameterCancellationReason EventParameter = "cancellation_reason"
// EventParameterFirebaseErrorValue Additional details corresponding to the error code reported by the Firebase SDK
EventParameterFirebaseErrorValue EventParameter = "firebase_error_value"
// EventParameterFirebaseScreen The current screens name, as provided by the developer
EventParameterFirebaseScreen EventParameter = "firebase_screen"
// EventParameterFirebaseScreenClass The current screens class name
EventParameterFirebaseScreenClass EventParameter = "firebase_screen_class"
// EventParameterFirebaseScreenId A random identifier for the current screen
EventParameterFirebaseScreenId EventParameter = "firebase_screen_id"
// EventParameterMessageId The Firebase Cloud Messaging or Firebase In-App Messaging message identifier, which is unique per message campaign
EventParameterMessageId EventParameter = "message_id"
// EventParameterMessageName The Firebase Cloud Messaging or Firebase In-App Messaging message name
EventParameterMessageName EventParameter = "message_name"
// EventParameterMessageType The Firebase Cloud Messaging message notification type
EventParameterMessageType EventParameter = "message_type"
// EventParameterPreviousAppVersion For the app_update event, the parameter signifies the previous application version
EventParameterPreviousAppVersion EventParameter = "previous_app_version"
// EventParameterPreviousOsVersion For the os_update event, the parameter signifies the previous OS version
EventParameterPreviousOsVersion EventParameter = "previous_os_version"
// EventParameterCampaignContent The ad content that was associated with the start of a session
EventParameterCampaignContent EventParameter = "campaign_content"
// EventParameterCampaignMedium The method for acquiring users to your website or application
EventParameterCampaignMedium EventParameter = "campaign_medium"
// EventParameterCampaignSource A representation of the publisher or inventory source from which traffic originated. For example, users who return to your website from Google Search show as "google" in the Session source dimension
EventParameterCampaignSource EventParameter = "campaign_source"
// EventParameterCampaignTerm The term that was associated with the start of a session
EventParameterCampaignTerm EventParameter = "campaign_term"
// EventParameterCoupon The coupon name or code associated with an event
EventParameterCoupon EventParameter = "coupon"
// EventParameterCurrency The currency used in an event, in 3-letter ISO 4217 format. For example, the currency used in a purchase
EventParameterCurrency EventParameter = "currency"
// EventParameterShippingTier The shipping tier selected for delivery of a purchased item
EventParameterShippingTier EventParameter = "shipping_tier"
// EventParameterTransactionId The unique identifier of a transaction
EventParameterTransactionId EventParameter = "transaction_id"
// EventParameterValue The monetary value of the event
EventParameterValue EventParameter = "value"
// EventParameterAffiliation A product affiliation to designate a supplying company or brick and mortar store location
EventParameterAffiliation EventParameter = "affiliation"
// EventParameterCreativeName The name of a creative used in a promotion. Example value: summer_banner
EventParameterCreativeName EventParameter = "creative_name"
// EventParameterCreativeSlot The name of the promotional creative slot associated with an event. Example value: featured_app_1
EventParameterCreativeSlot EventParameter = "creative_slot"
// EventParameterDiscount The value of a discount value associated with a purchased item
EventParameterDiscount EventParameter = "discount"
// EventParameterIndex The index of the item in a list
EventParameterIndex EventParameter = "index"
// EventParameterItemBrand The brand of an item
EventParameterItemBrand EventParameter = "item_brand"
// EventParameterItemCategory The category of an item. If used as part of a category hierarchy or taxonomy, then this is the first category
EventParameterItemCategory EventParameter = "item_category"
// EventParameterItemCategory2 The second hierarchical category in which you classified an item
EventParameterItemCategory2 EventParameter = "item_category2"
// EventParameterItemCategory3 The third hierarchical category in which you classified an item
EventParameterItemCategory3 EventParameter = "item_category3"
// EventParameterItemCategory4 The fourth hierarchical category in which you classified an item
EventParameterItemCategory4 EventParameter = "Item_category4"
// EventParameterItemCategory5 The fifth hierarchical category in which you classified an item
EventParameterItemCategory5 EventParameter = "Item_category5"
// EventParameterItemId The ID that you specify for an item
EventParameterItemId EventParameter = "item_id"
// EventParameterItemListId The name of the list in which an item was presented to a user
EventParameterItemListId EventParameter = "item_list_id"
// EventParameterItemListName The ID of the list in which an item was presented to a user
EventParameterItemListName EventParameter = "item_list_name"
// EventParameterItemName The name of the event that contains the parameter group
EventParameterItemName EventParameter = "item_name"
// EventParameterItemVariant The item variant or unique code or description (e.g., XS, S, M, L for size; Red, Blue, Green, Black for color) for additional item details or options
EventParameterItemVariant EventParameter = "item_variant"
// EventParameterLocationId The physical location associated with the item (e.g. the physical store location)
EventParameterLocationId EventParameter = "location_id"
// EventParameterPromotionId The ID of the promotion associated with an event
EventParameterPromotionId EventParameter = "promotion_id"
// EventParameterPromotionName The name of the promotion associated with an event
EventParameterPromotionName EventParameter = "promotion_name"
// EventParameterAchievementId The ID of an achievement that was unlocked in a game
EventParameterAchievementId EventParameter = "achievement_id"
// EventParameterCharacter The name of a character in a game
EventParameterCharacter EventParameter = "character"
// EventParameterLevelName The name of the level in a game
EventParameterLevelName EventParameter = "level_name"
// EventParameterVirtualCurrencyName The name of a virtual currency
EventParameterVirtualCurrencyName EventParameter = "virtual_currency_name"
// EventParameterFileExtension The extension of a file download
EventParameterFileExtension EventParameter = "file_extension"
// EventParameterFileName The page path of a file download
EventParameterFileName EventParameter = "file_name"
// EventParameterFormDestination The URL to which a form is being submitted
EventParameterFormDestination EventParameter = "form_destination"
// EventParameterFormId The HTML id attribution of the <form> DOM element
EventParameterFormId EventParameter = "form_id"
// EventParameterFormName The HTML name attribute of the <form> DOM element
EventParameterFormName EventParameter = "form_name"
// EventParameterFormSubmitText The text of the submit button, if present
EventParameterFormSubmitText EventParameter = "form_submit_text"
// EventParameterGroupId The ID of a group
EventParameterGroupId EventParameter = "group_id"
// EventParameterLanguage The language setting of a users browser or device, displayed as the ISO 639 language code
EventParameterLanguage EventParameter = "language"
// EventParameterPercentScrolled The percentage down the page that the user scrolled
EventParameterPercentScrolled EventParameter = "percent_scrolled"
// EventParameterSearchTerm The strings or keywords used in a search
EventParameterSearchTerm EventParameter = "search_term"
// EventParameterLinkClasses The HTML class attribute for an outbound link or file download
EventParameterLinkClasses EventParameter = "link_classes"
// EventParameterLinkDomain The destination domain of an outbound link or file download
EventParameterLinkDomain EventParameter = "link_domain"
// EventParameterLinkId The ID for an outbound link or file download
EventParameterLinkId EventParameter = "link_id"
// EventParameterLinkUrl The full URL for an outbound link or file download
EventParameterLinkUrl EventParameter = "link_url"
// EventParameterOutbound Indicates whether a click was on an outbound link
EventParameterOutbound EventParameter = "outbound"
// EventParameterContentGroup The content group associated with a page or screen
EventParameterContentGroup EventParameter = "content_group"
// EventParameterContentId An ID for an article of content that a user interacted with
EventParameterContentId EventParameter = "content_id"
// EventParameterContentType The type of content that a user interacted with
EventParameterContentType EventParameter = "content_type"
// EventParameterPageLocation The complete URL of the webpage that someone visited on your website
EventParameterPageLocation EventParameter = "page_location"
// EventParameterPageReferrer The referring URL, which is the user's previous URL and can be your website's domain or other domains
EventParameterPageReferrer EventParameter = "page_referrer"
// EventParameterPageTitle The HTML page title that you set on your website
EventParameterPageTitle EventParameter = "page_title"
// EventParameterScreenResolution The resolution of a device, in the format (Width)x(Height)
EventParameterScreenResolution EventParameter = "screen_resolution"
// EventParameterAdFormat The format of an advertisement in an app
EventParameterAdFormat EventParameter = "ad_format"
// EventParameterAdPlatform The platform used to surface an advertisement in an app
EventParameterAdPlatform EventParameter = "ad_platform"
// EventParameterAdSource The source network that served an advertisement
EventParameterAdSource EventParameter = "ad_source"
// EventParameterAdUnitId The unique identifier for an ad unit
EventParameterAdUnitId EventParameter = "ad_unit_id"
// EventParameterAdUnitName The name you choose for an ad unit
EventParameterAdUnitName EventParameter = "ad_unit_name"
// EventParameterVideoProvider The source of an embedded video
EventParameterVideoProvider EventParameter = "video_provider"
// EventParameterVideoTitle The title of an embedded video
EventParameterVideoTitle EventParameter = "video_title"
// EventParameterVideoUrl The url of an embedded video
EventParameterVideoUrl EventParameter = "video_url"
// Additional undocumented parameters
EventParameterMethod EventParameter = "method"
EventParameterCoupon EventParameter = "coupon"
EventParameterPaymentType EventParameter = "payment_type"
EventParameterShippingTier EventParameter = "shipping_tier"
EventParameterTransactionID EventParameter = "transaction_id"
EventParameterSearchTerm EventParameter = "search_term"
)
func (s EventParameter) String() string {

View File

@ -1,11 +1,23 @@
package v2
// EventParameterNumber as number
// See https://support.google.com/analytics/table/13594742?sjid=7861230991468479976-EU
type EventParameterNumber string
const (
EventParameterNumberValue EventParameterNumber = "value"
EventParameterNumberValue EventParameterNumber = "value"
// EventParameterNumberFirebaseError The error code reported by the Firebase SDK.
EventParameterNumberFirebaseError EventParameterNumber = "firebase_error"
// EventParameterNumberFreeTrial Signifies that an in-app purchase is a free trial
EventParameterNumberFreeTrial EventParameterNumber = "free_trial"
// EventParameterNumberMessageDeviceTime The Firebase Cloud Messaging or Firebase In-App Messaging delivery epoch timestamp in UTC. (None)
EventParameterNumberMessageDeviceTime EventParameterNumber = "message_device_time"
// EventParameterNumberMessageTime The Firebase Cloud Messaging message notification epoch timestamp in UTC. (None)
EventParameterNumberMessageTime EventParameterNumber = "message_time"
// EventParameterNumberShipping The shipping cost associated with a transaction
EventParameterNumberShipping EventParameterNumber = "shipping"
EventParameterNumberTax EventParameterNumber = "tax"
// EventParameterNumberTax The tax cost associated with a transaction
EventParameterNumberTax EventParameterNumber = "tax"
)
func (s EventParameterNumber) String() string {

View File

@ -0,0 +1,5 @@
package v2
type Marshler interface {
MarshalMPv2() (*Event, error)
}

View File

@ -1,202 +0,0 @@
package v2
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/url"
"regexp"
"sort"
"strings"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
)
var (
RegexProduct = regexp.MustCompile(`pr([1-9]|[1-9][0-9]|1[0-9]{2}|200)`)
)
const (
ParameterItem = "pr"
)
type Data map[string]any
func Marshal(input *Event) (url.Values, io.Reader, error) {
var richsstsse bool
// NOTE: `richsstsse` seems to be last parameter in the query to let's ensure it stays that way
if input.Richsstsse != nil {
richsstsse = true
input.Richsstsse = nil
}
a, err := json.Marshal(input)
if err != nil {
return nil, nil, err
}
data := Data{}
if err := json.Unmarshal(a, &data); err != nil {
return nil, nil, errors.Wrap(err, "failed to decode into map")
}
ret := url.Values{}
for k, v := range data {
switch t := v.(type) {
case []interface{}:
for key, value := range t {
switch tt := value.(type) {
case map[string]interface{}:
ret[fmt.Sprintf("%s%d", k, key+1)] = []string{EncodeObjectValue(tt)}
default:
panic("unhandled")
}
}
case map[string]string:
for key, value := range t {
ret[fmt.Sprintf("%s.%s", k, key)] = []string{value}
}
case map[string]interface{}:
for key, value := range t {
ret[fmt.Sprintf("%s.%s", k, key)] = []string{fmt.Sprintf("%v", value)}
}
case *string:
ret[k] = []string{*t}
case string:
ret[k] = []string{t}
default:
panic("unhandled")
}
}
var body []string
var reader io.Reader
maxQueryLength := 2048 //
if richsstsse {
maxQueryLength -= len("&richsstsse")
}
for len(ret.Encode()) > maxQueryLength {
for s, i := range ret {
ret.Del(s)
body = append(body, s+"="+i[0])
break
}
}
if richsstsse {
ret.Add("richsstsse", "")
}
if len(body) > 0 {
reader = bytes.NewReader([]byte(strings.Join(body, "&")))
}
return ret, reader, nil
}
func UnmarshalURLValues(input url.Values, output interface{}) error {
data := Data{}
// decode values
for k, v := range input {
// handle maps
if ok, err := DecodeMapValue(k, v, data); err != nil {
return err
} else if ok {
continue
}
// handle slices
if ok, err := DecodeRegexValue(k, v, RegexProduct, data, ParameterItem); err != nil {
return err
} else if ok {
continue
}
// default
v, err := url.QueryUnescape(v[0])
if err != nil {
return err
}
data[k] = v
}
if err := mapstructure.WeakDecode(data, output); err != nil {
return errors.Wrap(err, "failed to weakly decode query")
}
return nil
}
func DecodeMapValue(k string, v []string, data Data) (bool, error) {
if strings.Contains(k, ".") {
parts := strings.Split(k, ".")
if _, ok := data[parts[0]]; !ok {
data[parts[0]] = map[string]any{}
}
if value, ok := data[parts[0]].(map[string]any); ok {
v, err := url.QueryUnescape(v[0])
if err != nil {
return false, err
}
value[strings.Join(parts[1:], ".")] = v
}
return true, nil
}
return false, nil
}
// DecodeRegexValue e.g. `pr1=idSKU_123456` = map["pr"][]map["id"]="SKU_123456"
func DecodeRegexValue(k string, v []string, r *regexp.Regexp, data Data, key string) (bool, error) {
if r.MatchString(k) {
value, err := DecodeObjectValue(v[0])
if err != nil {
return false, err
}
if value != nil {
v, ok := data[key].([]map[string]any)
if !ok {
v = []map[string]any{}
}
v = append(v, value)
data[key] = v
return true, nil
}
}
return false, nil
}
// DecodeObjectValue e.g. `idSKU_123456` = map["id"]="SKU_123456"
func DecodeObjectValue(s string) (map[string]any, error) {
if len(s) == 0 {
return nil, nil //nolint:nilnil
}
ret := map[string]any{}
for _, part := range strings.Split(s, "~") {
v, err := url.QueryUnescape(part[2:])
if err != nil {
return nil, err
}
ret[part[0:2]] = v
}
return ret, nil
}
// EncodeObjectValue e.g. `idSKU_123456` = map["id"]="SKU_123456"
func EncodeObjectValue(s map[string]any) string {
if len(s) == 0 {
return ""
}
keys := make([]string, 0, len(s))
for k := range s {
keys = append(keys, k)
}
sort.Strings(keys)
ret := make([]string, 0, len(keys))
for _, k := range keys {
ret = append(ret, k+fmt.Sprintf("%s", s[k]))
}
return strings.Join(ret, "~")
}

View File

@ -0,0 +1,5 @@
package v2
type Unmarshler interface {
UnmarshalMPv2(*Event) error
}

View File

@ -0,0 +1,22 @@
package v2
import (
"net/url"
)
// EncodeValues
// NOTE: `richsstsse` seems to be last parameter in the query to let's ensure it stays that way
func EncodeValues(values url.Values) string {
var richsstsse bool
if values.Has("richsstsse") {
values.Del("richsstsse")
richsstsse = true
}
ret := values.Encode()
if richsstsse {
ret += "&richsstsse"
}
return ret
}