mirror of
https://github.com/foomo/sesamy-go.git
synced 2025-10-16 12:35:43 +00:00
feat: add parameters and restructure code
This commit is contained in:
parent
7100ab858b
commit
b45f2c844d
@ -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")
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
13
measurementprotocol/v2/contants.go
Normal file
13
measurementprotocol/v2/contants.go
Normal 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"
|
||||
)
|
||||
3
measurementprotocol/v2/data.go
Normal file
3
measurementprotocol/v2/data.go
Normal file
@ -0,0 +1,3 @@
|
||||
package v2
|
||||
|
||||
type Data map[string]any
|
||||
98
measurementprotocol/v2/decode.go
Normal file
98
measurementprotocol/v2/decode.go
Normal 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
|
||||
}
|
||||
@ -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>m=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>m=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)
|
||||
}
|
||||
}
|
||||
102
measurementprotocol/v2/encode.go
Normal file
102
measurementprotocol/v2/encode.go
Normal 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, "~")
|
||||
}
|
||||
43
measurementprotocol/v2/encode_test.go
Normal file
43
measurementprotocol/v2/encode_test.go
Normal 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>m=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>m=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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -40,17 +40,17 @@ AddPaymentInfo https://developers.google.com/tag-platform/gtagjs/reference/event
|
||||
Query: v=2&tid=G-123456>m=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
|
||||
}
|
||||
|
||||
@ -40,17 +40,17 @@ AddShippingInfo https://developers.google.com/tag-platform/gtagjs/reference/even
|
||||
Query: v=2&tid=G-PZ5ELRCR31>m=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
|
||||
}
|
||||
|
||||
@ -38,15 +38,15 @@ AddToCart https://developers.google.com/tag-platform/gtagjs/reference/events#add
|
||||
Query: v=2&tid=G-PZ5ELRCR31>m=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
|
||||
}
|
||||
|
||||
@ -38,15 +38,15 @@ AddToWishlist https://developers.google.com/tag-platform/gtagjs/reference/events
|
||||
Query: v=2&tid=G-PZ5ELRCR31>m=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
|
||||
}
|
||||
|
||||
@ -39,16 +39,16 @@ BeginCheckout https://developers.google.com/tag-platform/gtagjs/reference/events
|
||||
Query: v=2&tid=G-123456>m=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
|
||||
}
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -42,19 +42,19 @@ Purchase https://developers.google.com/tag-platform/gtagjs/reference/events#purc
|
||||
Query: v=2&tid=G-PZ5ELRCR31>m=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
|
||||
}
|
||||
|
||||
@ -42,19 +42,19 @@ Refund https://developers.google.com/tag-platform/gtagjs/reference/events#refund
|
||||
Query: v=2&tid=G-PZ5ELRCR31>m=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
|
||||
}
|
||||
|
||||
@ -38,15 +38,15 @@ RemoveFromCart https://developers.google.com/tag-platform/gtagjs/reference/event
|
||||
Query: v=2&tid=G-PZ5ELRCR31>m=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
|
||||
}
|
||||
|
||||
@ -15,14 +15,14 @@ Search https://developers.google.com/tag-platform/gtagjs/reference/events#search
|
||||
Query: v=2&tid=G-PZ5ELRCR31>m=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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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 screen’s name, as provided by the developer
|
||||
EventParameterFirebaseScreen EventParameter = "firebase_screen"
|
||||
// EventParameterFirebaseScreenClass The current screen’s 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 user’s 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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
5
measurementprotocol/v2/marshaler.go
Normal file
5
measurementprotocol/v2/marshaler.go
Normal file
@ -0,0 +1,5 @@
|
||||
package v2
|
||||
|
||||
type Marshler interface {
|
||||
MarshalMPv2() (*Event, error)
|
||||
}
|
||||
@ -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, "~")
|
||||
}
|
||||
5
measurementprotocol/v2/unmarshaler.go
Normal file
5
measurementprotocol/v2/unmarshaler.go
Normal file
@ -0,0 +1,5 @@
|
||||
package v2
|
||||
|
||||
type Unmarshler interface {
|
||||
UnmarshalMPv2(*Event) error
|
||||
}
|
||||
22
measurementprotocol/v2/values.go
Normal file
22
measurementprotocol/v2/values.go
Normal 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
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user