fix: lint errors

This commit is contained in:
Kevin Franklin Kim 2024-03-04 15:10:28 +01:00
parent d80faf60d8
commit fbb743cb96
No known key found for this signature in database
21 changed files with 147 additions and 164 deletions

View File

@ -77,7 +77,7 @@ linters:
- predeclared # find code that shadows one of Go's predeclared identifiers [fast: true, auto-fix: false] - predeclared # find code that shadows one of Go's predeclared identifiers [fast: true, auto-fix: false]
- promlinter # Check Prometheus metrics naming via promlint [fast: true, auto-fix: false] - promlinter # Check Prometheus metrics naming via promlint [fast: true, auto-fix: false]
- revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. [fast: false, auto-fix: false] - revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. [fast: false, auto-fix: false]
- tagliatelle # Checks the struct tags. [fast: true, auto-fix: false] #- tagliatelle # Checks the struct tags. [fast: true, auto-fix: false]
- testpackage # linter that makes you use a separate _test package [fast: true, auto-fix: false] - testpackage # linter that makes you use a separate _test package [fast: true, auto-fix: false]
- thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers [fast: false, auto-fix: false] - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers [fast: false, auto-fix: false]
- unconvert # Remove unnecessary type conversions [fast: false, auto-fix: false] - unconvert # Remove unnecessary type conversions [fast: false, auto-fix: false]

View File

@ -1,6 +1,6 @@
package gtm package gtm
const ( const (
HeaderUUID = "Message-Uuid" HeaderUUID = "Message-Uuid"
ProviderName = "gtm" ProviderName = "gtm"
) )

View File

@ -1,12 +1,9 @@
package gtm package gtm
import ( import (
"bytes"
"encoding/json" "encoding/json"
"io"
"net/http" "net/http"
"github.com/ThreeDotsLabs/watermill"
"github.com/ThreeDotsLabs/watermill/message" "github.com/ThreeDotsLabs/watermill/message"
mpv2 "github.com/foomo/sesamy/measurementprotocol/v2" mpv2 "github.com/foomo/sesamy/measurementprotocol/v2"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -19,7 +16,6 @@ var (
type ( type (
Publisher struct { Publisher struct {
//l watermill.LoggerAdapter
url string url string
client *http.Client client *http.Client
marshalMessageFunc MarshalMessageFunc marshalMessageFunc MarshalMessageFunc
@ -77,7 +73,6 @@ func (p *Publisher) Publish(topic string, messages ...*message.Message) error {
} }
for _, msg := range messages { for _, msg := range messages {
var event *mpv2.Event var event *mpv2.Event
if err := json.Unmarshal(msg.Payload, &event); err != nil { if err := json.Unmarshal(msg.Payload, &event); err != nil {
return err return err
@ -88,32 +83,40 @@ func (p *Publisher) Publish(topic string, messages ...*message.Message) error {
return err return err
} }
u := p.url + "?" + values.Encode() + "&richsstsse" var richsstsse bool
if values.Has("richsstsse") {
values.Del("richsstsse")
richsstsse = true
}
req, err := http.NewRequestWithContext(msg.Context(), "POST", u, body) u := p.url + "?"
if richsstsse {
u += "&richsstsse"
}
req, err := http.NewRequestWithContext(msg.Context(), http.MethodPost, u, body)
if err != nil {
return errors.Wrap(err, "failed to create request")
}
for s, s2 := range msg.Metadata { for s, s2 := range msg.Metadata {
req.Header.Set(s, s2) req.Header.Set(s, s2)
} }
//req, err := p.marshalMessageFunc(topic, msg) // logFields := watermill.LogFields{
if err != nil { // "uuid": msg.UUID,
return errors.Wrapf(err, "cannot marshal message %s", msg.UUID) // "provider": ProviderName,
} // }
logFields := watermill.LogFields{ // p.l.Trace("Publishing message", logFields)
"uuid": msg.UUID,
"provider": ProviderName,
}
//p.l.Trace("Publishing message", logFields)
resp, err := p.client.Do(req) resp, err := p.client.Do(req)
if err != nil { if err != nil {
return errors.Wrapf(err, "publishing message %s failed", msg.UUID) return errors.Wrapf(err, "publishing message %s failed", msg.UUID)
} }
if err = p.handleResponseBody(resp, logFields); err != nil { if err = p.handleResponseBody(resp); err != nil {
return err return err
} }
@ -121,7 +124,7 @@ func (p *Publisher) Publish(topic string, messages ...*message.Message) error {
return errors.Wrap(ErrErrorResponse, resp.Status) return errors.Wrap(ErrErrorResponse, resp.Status)
} }
//p.l.Trace("Message published", logFields) // p.l.Trace("Message published", logFields)
} }
return nil return nil
@ -140,40 +143,26 @@ func (p *Publisher) Close() error {
// ~ Private methods // ~ Private methods
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
func (p *Publisher) handleResponseBody(resp *http.Response, logFields watermill.LogFields) error { func (p *Publisher) handleResponseBody(resp *http.Response) error {
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode < http.StatusBadRequest { if resp.StatusCode < http.StatusBadRequest {
return nil return nil
} }
body, err := io.ReadAll(resp.Body) // body, err := io.ReadAll(resp.Body)
if err != nil { // if err != nil {
return errors.Wrap(err, "could not read response body") // return errors.Wrap(err, "could not read response body")
} // }
logFields = logFields.Add(watermill.LogFields{ // logFields = logFields.Add(watermill.LogFields{
"http_status": resp.StatusCode, // "http_status": resp.StatusCode,
"http_response": string(body), // "http_response": string(body),
}) // })
//p.l.Info("Server responded with error", logFields) // p.l.Info("Server responded with error", logFields)
return nil return nil
} }
// MarshalMessageFunc transforms the message into a HTTP request to be sent to the specified url. // MarshalMessageFunc transforms the message into a HTTP request to be sent to the specified url.
type MarshalMessageFunc func(url string, msg *message.Message) (*http.Request, error) type MarshalMessageFunc func(url string, msg *message.Message) (*http.Request, error)
// DefaultMarshalMessageFunc transforms the message into a HTTP POST request.
// It encodes the UUID and Metadata in request headers.
func DefaultMarshalMessageFunc(url string, msg *message.Message) (*http.Request, error) {
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(msg.Payload))
if err != nil {
return nil, err
}
req.Header.Set(HeaderUUID, msg.UUID)
return req, nil
}

View File

@ -28,11 +28,11 @@ var (
type ( type (
Subscriber struct { Subscriber struct {
l *zap.Logger l *zap.Logger
uuidFunc func() string uuidFunc func() string
messages chan *message.Message messages chan *message.Message
middlewares []SubscriberMiddleware middlewares []SubscriberMiddleware
closed bool closed bool
} }
SubscriberOption func(*Subscriber) SubscriberOption func(*Subscriber)
SubscriberHandler func(l *zap.Logger, r *http.Request, event *mpv2.Event) error SubscriberHandler func(l *zap.Logger, r *http.Request, event *mpv2.Event) error
@ -60,7 +60,7 @@ func SubscriberWithLogger(fields ...zap.Field) SubscriberOption {
o.middlewares = append(o.middlewares, func(next SubscriberHandler) SubscriberHandler { o.middlewares = append(o.middlewares, func(next SubscriberHandler) SubscriberHandler {
return func(l *zap.Logger, r *http.Request, event *mpv2.Event) error { return func(l *zap.Logger, r *http.Request, event *mpv2.Event) error {
fields = append(fields, zap.String("event_name", mp.GetDefault(event.EventName, "-").String())) fields = append(fields, zap.String("event_name", mp.GetDefault(event.EventName, "-").String()))
if labeler, ok := log.LabelerFromRequest(r);ok { if labeler, ok := log.LabelerFromRequest(r); ok {
labeler.Add(fields...) labeler.Add(fields...)
} }
return next(l.With(fields...), r, event) return next(l.With(fields...), r, event)

View File

@ -83,7 +83,7 @@ func Get[T any](v *T) T {
} }
func GetDefault[T any](v *T, fallback T) T { func GetDefault[T any](v *T, fallback T) T {
if v == nil { if v == nil {
return fallback return fallback
} }
return *v return *v

View File

@ -57,7 +57,6 @@ func (c *Client) HTTPClient() *http.Client {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
func (c *Client) Send(ctx context.Context, event *Event) error { func (c *Client) Send(ctx context.Context, event *Event) error {
values, body, err := Marshal(event) values, body, err := Marshal(event)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to marshall event") return errors.Wrap(err, "failed to marshall event")
@ -90,4 +89,3 @@ func (c *Client) Send(ctx context.Context, event *Event) error {
return nil return nil
} }

View File

@ -146,7 +146,7 @@ type Event struct {
// Defines a parameter for the current Event // Defines a parameter for the current Event
// Example: epn.plays_count: 42 // Example: epn.plays_count: 42
EventParameterNumber map[string]string `json:"epn,omitempty" mapstructure:"epn,omitempty"` EventParameterNumber map[string]string `json:"epn,omitempty" mapstructure:"epn,omitempty"`
// If the current event is set as a conversion on the admin interace the evfent will have this value present // If the current event is set as a conversion on the admin interacted the evfent will have this value present
// Example: 1 // Example: 1
IsConversion *string `json:"_c,omitempty" mapstructure:"_c,omitempty"` IsConversion *string `json:"_c,omitempty" mapstructure:"_c,omitempty"`
// External Event // External Event
@ -181,7 +181,7 @@ type Event struct {
// If the "_ga_THYNGSTER" cookie last session time value is older than 1800 seconds, the current event will have this value present. This will internally create a new "session_start" event on GA4. If this event is also a conversion the value will be "2" if not, will be "1" // If the "_ga_THYNGSTER" cookie last session time value is older than 1800 seconds, the current event will have this value present. This will internally create a new "session_start" event on GA4. If this event is also a conversion the value will be "2" if not, will be "1"
// Example: 1|2 // Example: 1|2
SessionStart *string `json:"_ss,omitempty" mapstructure:"_ss,omitempty"` SessionStart *string `json:"_ss,omitempty" mapstructure:"_ss,omitempty"`
// This seems to be related to the ServerSide hits, it's 0 if the FPLC Cookie is not present and to the current value if it's comming from a Cross Domain linker // This seems to be related to the ServerSide hits, it's 0 if the FPLC Cookie is not present and to the current value if it's coming from a Cross Domain linker
// Example: bVhVicbfiSXaGNxeawKaPlDQc9QXPD6bKcsn36Elden6wZNb7Q5X1iXlkTVP5iP3H3y76cgM3UIgHCaRsYfPoyLGlbiIYMPRjvnUU7KWbdWLagodzxjrlPnvaRZJkw // Example: bVhVicbfiSXaGNxeawKaPlDQc9QXPD6bKcsn36Elden6wZNb7Q5X1iXlkTVP5iP3H3y76cgM3UIgHCaRsYfPoyLGlbiIYMPRjvnUU7KWbdWLagodzxjrlPnvaRZJkw
FirstPartyLinkerCookie *string `json:"_fplc,omitempty" mapstructure:"_fplc,omitempty"` FirstPartyLinkerCookie *string `json:"_fplc,omitempty" mapstructure:"_fplc,omitempty"`
// If the current user has a GA4 session cookie, but not a GA (_ga) client id cookie, this parameter will be added to the hit // If the current user has a GA4 session cookie, but not a GA (_ga) client id cookie, this parameter will be added to the hit
@ -202,7 +202,7 @@ type Event struct {
// Example: JPY // Example: JPY
Currency *string `json:"cu,omitempty" mapstructure:"cu,omitempty"` Currency *string `json:"cu,omitempty" mapstructure:"cu,omitempty"`
// Example: // Example:
Items []Item `json:"pr,omitempty" mapstructure:"pr,omitempty"` Items []*Item `json:"pr,omitempty" mapstructure:"pr,omitempty"`
// Promotion Impression/Click Tracking. Promotion Id // Promotion Impression/Click Tracking. Promotion Id
// Example: summer-offer // Example: summer-offer
PromotionID *string `json:"pi,omitempty" mapstructure:"pi,omitempty"` PromotionID *string `json:"pi,omitempty" mapstructure:"pi,omitempty"`
@ -211,10 +211,10 @@ type Event struct {
PromotionName *string `json:"pn,omitempty" mapstructure:"pn,omitempty"` PromotionName *string `json:"pn,omitempty" mapstructure:"pn,omitempty"`
// Promotion Impression/Click Tracking. Creative Name // Promotion Impression/Click Tracking. Creative Name
// Example: red-car // Example: red-car
CreativeName *string `json:"cn,omitempty" mapstructure:"cn,omitempty"` // CreativeName *string `json:"cn,omitempty" mapstructure:"cn,omitempty"`
// Promotion Impression/Click Tracking. Promotion Slot / Position // Promotion Impression/Click Tracking. Promotion Slot / Position
// Example: slide-3 // Example: slide-3
CreativeSlot *string `json:"cs,omitempty" mapstructure:"cs,omitempty"` // CreativeSlot *string `json:"cs,omitempty" mapstructure:"cs,omitempty"`
// Google Place ID: Refer to: https://developers.google.com/maps/documentation/places/web-service/place-id . Seems to be inherited from Firebase, not sure about the current use on GA4 // Google Place ID: Refer to: https://developers.google.com/maps/documentation/places/web-service/place-id . Seems to be inherited from Firebase, not sure about the current use on GA4
// Example: ChIJiyj437sx3YAR9kUWC8QkLzQ // Example: ChIJiyj437sx3YAR9kUWC8QkLzQ
LocationID *string `json:"lo,omitempty" mapstructure:"lo,omitempty"` LocationID *string `json:"lo,omitempty" mapstructure:"lo,omitempty"`
@ -254,12 +254,10 @@ type Event struct {
PrivacySandboxCookieDeprecationLabel *string `json:"pscdl,omitempty" mapstructure:"pscdl,omitempty"` PrivacySandboxCookieDeprecationLabel *string `json:"pscdl,omitempty" mapstructure:"pscdl,omitempty"`
// A timestamp measuring the difference between the moment this parameter gets populated and the moment the navigation started on that particular page. // A timestamp measuring the difference between the moment this parameter gets populated and the moment the navigation started on that particular page.
TFD *string `json:"tfd,omitempty" mapstructure:"tfd,omitempty"` TFD *string `json:"tfd,omitempty" mapstructure:"tfd,omitempty"`
SST *SST `json:"sst,omitempty" mapstructure:"sst,omitempty"` SST *SST `json:"sst,omitempty" mapstructure:"sst,omitempty"`
PAE *string `json:"pae,omitempty" mapstructure:"pae,omitempty"` PAE *string `json:"pae,omitempty" mapstructure:"pae,omitempty"`
// --- Unresolved --- // --- Unresolved ---
Unknown map[string]any `json:"-" mapstructure:",remain"` Unknown map[string]any `json:"-" mapstructure:",remain"`
} }

View File

@ -48,14 +48,14 @@ type AddPaymentInfo struct {
} }
func (e *AddPaymentInfo) MPv2() *mpv2.Event { func (e *AddPaymentInfo) MPv2() *mpv2.Event {
items := make([]mpv2.Item, len(e.Items)) items := make([]*mpv2.Item, len(e.Items))
for i, item := range e.Items { for i, item := range e.Items {
items[i] = *item.MPv2() items[i] = item.MPv2()
} }
return &mpv2.Event{ return &mpv2.Event{
Currency: mp.SetString(e.Currency), Currency: mp.SetString(e.Currency),
EventParameter: map[string]string{ EventParameter: map[string]string{
mpv2.EventParameterCoupon.String(): e.Coupon, mpv2.EventParameterCoupon.String(): e.Coupon,
mpv2.EventParameterPaymentType.String(): e.PaymentType, mpv2.EventParameterPaymentType.String(): e.PaymentType,
}, },
EventParameterNumber: map[string]string{ EventParameterNumber: map[string]string{

View File

@ -40,22 +40,22 @@ 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 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 { type AddShippingInfo struct {
Currency string Currency string
Value float64 Value float64
Coupon string Coupon string
ShippingTier string ShippingTier string
Items []*Item Items []*Item
} }
func (e *AddShippingInfo) MPv2() *mpv2.Event { func (e *AddShippingInfo) MPv2() *mpv2.Event {
items := make([]mpv2.Item, len(e.Items)) items := make([]*mpv2.Item, len(e.Items))
for i, item := range e.Items { for i, item := range e.Items {
items[i] = *item.MPv2() items[i] = item.MPv2()
} }
return &mpv2.Event{ return &mpv2.Event{
Currency: mp.SetString(e.Currency), Currency: mp.SetString(e.Currency),
EventParameter: map[string]string{ EventParameter: map[string]string{
mpv2.EventParameterCoupon.String(): e.Coupon, mpv2.EventParameterCoupon.String(): e.Coupon,
mpv2.EventParameterShippingTier.String(): e.ShippingTier, mpv2.EventParameterShippingTier.String(): e.ShippingTier,
}, },
EventParameterNumber: map[string]string{ EventParameterNumber: map[string]string{

View File

@ -44,9 +44,9 @@ type AddToCart struct {
} }
func (e *AddToCart) MPv2() *mpv2.Event { func (e *AddToCart) MPv2() *mpv2.Event {
items := make([]mpv2.Item, len(e.Items)) items := make([]*mpv2.Item, len(e.Items))
for i, item := range e.Items { for i, item := range e.Items {
items[i] = *item.MPv2() items[i] = item.MPv2()
} }
return &mpv2.Event{ return &mpv2.Event{
Currency: mp.SetString(e.Currency), Currency: mp.SetString(e.Currency),

View File

@ -44,9 +44,9 @@ type AddToWishlist struct {
} }
func (e *AddToWishlist) MPv2() *mpv2.Event { func (e *AddToWishlist) MPv2() *mpv2.Event {
items := make([]mpv2.Item, len(e.Items)) items := make([]*mpv2.Item, len(e.Items))
for i, item := range e.Items { for i, item := range e.Items {
items[i] = *item.MPv2() items[i] = item.MPv2()
} }
return &mpv2.Event{ return &mpv2.Event{
Currency: mp.SetString(e.Currency), Currency: mp.SetString(e.Currency),

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 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 { type BeginCheckout struct {
Currency string Currency string
Value float64 Value float64
Coupon string Coupon string
Items []*Item Items []*Item
} }
func (e *BeginCheckout) MPv2() *mpv2.Event { func (e *BeginCheckout) MPv2() *mpv2.Event {
items := make([]mpv2.Item, len(e.Items)) items := make([]*mpv2.Item, len(e.Items))
for i, item := range e.Items { for i, item := range e.Items {
items[i] = *item.MPv2() items[i] = item.MPv2()
} }
return &mpv2.Event{ return &mpv2.Event{
Currency: mp.SetString(e.Currency), Currency: mp.SetString(e.Currency),

View File

@ -6,45 +6,45 @@ import (
) )
type Item struct { type Item struct {
ID string ID string
Name string Name string
Brand string Brand string
CategoryHierarchy1 string CategoryHierarchy1 string
CategoryHierarchy2 string CategoryHierarchy2 string
CategoryHierarchy3 string CategoryHierarchy3 string
CategoryHierarchy4 string CategoryHierarchy4 string
CategoryHierarchy5 string CategoryHierarchy5 string
Price string Price string
Quantity string Quantity string
Variant string Variant string
Coupon string Coupon string
Discount float64 Discount float64
Index int Index int
ListName string ListName string
ListID string ListID string
Affiliation string Affiliation string
LocationID string LocationID string
} }
func (e *Item) MPv2() *mpv2.Item { func (e *Item) MPv2() *mpv2.Item {
return &mpv2.Item{ return &mpv2.Item{
ID: mp.SetString(e.ID), ID: mp.SetString(e.ID),
Name: mp.SetString(e.Name), Name: mp.SetString(e.Name),
Brand: mp.SetString(e.Brand), Brand: mp.SetString(e.Brand),
CategoryHierarchy1: mp.SetString(e.CategoryHierarchy1), CategoryHierarchy1: mp.SetString(e.CategoryHierarchy1),
CategoryHierarchy2: mp.SetString(e.CategoryHierarchy2), CategoryHierarchy2: mp.SetString(e.CategoryHierarchy2),
CategoryHierarchy3: mp.SetString(e.CategoryHierarchy3), CategoryHierarchy3: mp.SetString(e.CategoryHierarchy3),
CategoryHierarchy4: mp.SetString(e.CategoryHierarchy4), CategoryHierarchy4: mp.SetString(e.CategoryHierarchy4),
CategoryHierarchy5: mp.SetString(e.CategoryHierarchy5), CategoryHierarchy5: mp.SetString(e.CategoryHierarchy5),
Price: mp.SetString(e.Price), Price: mp.SetString(e.Price),
Quantity: mp.SetString(e.Quantity), Quantity: mp.SetString(e.Quantity),
Variant: mp.SetString(e.Variant), Variant: mp.SetString(e.Variant),
Coupon: mp.SetString(e.Coupon), Coupon: mp.SetString(e.Coupon),
Discount: mp.SetFloat64(e.Discount), Discount: mp.SetFloat64(e.Discount),
ListName: mp.SetString(e.ListName), ListName: mp.SetString(e.ListName),
ListID: mp.SetString(e.ListID), ListID: mp.SetString(e.ListID),
ListPosition: mp.SetInt(e.Index), ListPosition: mp.SetInt(e.Index),
Affiliation: mp.SetString(e.Affiliation), Affiliation: mp.SetString(e.Affiliation),
LocationID: mp.SetString(e.LocationID), LocationID: mp.SetString(e.LocationID),
} }
} }

View File

@ -48,13 +48,13 @@ type Purchase struct {
Coupon string Coupon string
Shipping float64 Shipping float64
Tax float64 Tax float64
Items []Item Items []*Item
} }
func (e *Purchase) MPv2() *mpv2.Event { func (e *Purchase) MPv2() *mpv2.Event {
items := make([]mpv2.Item, len(e.Items)) items := make([]*mpv2.Item, len(e.Items))
for i, item := range e.Items { for i, item := range e.Items {
items[i] = *item.MPv2() items[i] = item.MPv2()
} }
return &mpv2.Event{ return &mpv2.Event{
Currency: mp.SetString(e.Currency), Currency: mp.SetString(e.Currency),

View File

@ -48,13 +48,13 @@ type Refund struct {
Coupon string Coupon string
Shipping float64 Shipping float64
Tax float64 Tax float64
Items []Item Items []*Item
} }
func (e *Refund) MPv2() *mpv2.Event { func (e *Refund) MPv2() *mpv2.Event {
items := make([]mpv2.Item, len(e.Items)) items := make([]*mpv2.Item, len(e.Items))
for i, item := range e.Items { for i, item := range e.Items {
items[i] = *item.MPv2() items[i] = item.MPv2()
} }
return &mpv2.Event{ return &mpv2.Event{
Currency: mp.SetString(e.Currency), Currency: mp.SetString(e.Currency),

View File

@ -44,9 +44,9 @@ type RemoveFromCart struct {
} }
func (e *RemoveFromCart) MPv2() *mpv2.Event { func (e *RemoveFromCart) MPv2() *mpv2.Event {
items := make([]mpv2.Item, len(e.Items)) items := make([]*mpv2.Item, len(e.Items))
for i, item := range e.Items { for i, item := range e.Items {
items[i] = *item.MPv2() items[i] = item.MPv2()
} }
return &mpv2.Event{ return &mpv2.Event{
Currency: mp.SetString(e.Currency), Currency: mp.SetString(e.Currency),

View File

@ -3,15 +3,14 @@ package v2
type EventParameter string type EventParameter string
const ( const (
EventParameterMethod EventParameter = "method" EventParameterMethod EventParameter = "method"
EventParameterCoupon EventParameter = "coupon" EventParameterCoupon EventParameter = "coupon"
EventParameterPaymentType EventParameter = "payment_type" EventParameterPaymentType EventParameter = "payment_type"
EventParameterShippingTier EventParameter = "shipping_tier" EventParameterShippingTier EventParameter = "shipping_tier"
EventParameterTransactionID EventParameter = "transaction_id" EventParameterTransactionID EventParameter = "transaction_id"
EventParameterSearchTerm EventParameter = "search_term" EventParameterSearchTerm EventParameter = "search_term"
) )
func (s EventParameter) String() string { func (s EventParameter) String() string {
return string(s) return string(s)
} }

View File

@ -3,9 +3,9 @@ package v2
type EventParameterNumber string type EventParameterNumber string
const ( const (
EventParameterNumberValue EventParameterNumber = "value" EventParameterNumberValue EventParameterNumber = "value"
EventParameterNumberShipping EventParameterNumber = "shipping" EventParameterNumberShipping EventParameterNumber = "shipping"
EventParameterNumberTax EventParameterNumber = "tax" EventParameterNumberTax EventParameterNumber = "tax"
) )
func (s EventParameterNumber) String() string { func (s EventParameterNumber) String() string {

View File

@ -1,45 +1,48 @@
package v2 package v2
/**
promotion_id: "pi", /*
promotion_name: "pn", *
creative_name: "cn",
creative_slot: "cs", promotion_id: "pi",
*/ promotion_name: "pn",
creative_name: "cn",
creative_slot: "cs",
*/
type Item struct { type Item struct {
// Exmaple: 12345 // Example: 12345
ID *string `json:"id,omitempty" mapstructure:"id,omitempty"` ID *string `json:"id,omitempty" mapstructure:"id,omitempty"`
// Example: Stan and Friends Tee // Example: Stan and Friends Tee
Name *string `json:"nm,omitempty" mapstructure:"nm,omitempty"` Name *string `json:"nm,omitempty" mapstructure:"nm,omitempty"`
// Exmaple: Google // Example: Google
Brand *string `json:"br,omitempty" mapstructure:"br,omitempty"` Brand *string `json:"br,omitempty" mapstructure:"br,omitempty"`
// Exmaple: men // Example: men
CategoryHierarchy1 *string `json:"ca,omitempty" mapstructure:"ca,omitempty"` CategoryHierarchy1 *string `json:"ca,omitempty" mapstructure:"ca,omitempty"`
// Exmaple: t-shirts // Example: t-shirts
CategoryHierarchy2 *string `json:"c2,omitempty" mapstructure:"c2,omitempty"` CategoryHierarchy2 *string `json:"c2,omitempty" mapstructure:"c2,omitempty"`
// Exmaple: men // Example: men
CategoryHierarchy3 *string `json:"c3,omitempty" mapstructure:"c3,omitempty"` CategoryHierarchy3 *string `json:"c3,omitempty" mapstructure:"c3,omitempty"`
// Exmaple: men // Example: men
CategoryHierarchy4 *string `json:"c4,omitempty" mapstructure:"c4,omitempty"` CategoryHierarchy4 *string `json:"c4,omitempty" mapstructure:"c4,omitempty"`
// Exmaple: men // Example: men
CategoryHierarchy5 *string `json:"c5,omitempty" mapstructure:"c5,omitempty"` CategoryHierarchy5 *string `json:"c5,omitempty" mapstructure:"c5,omitempty"`
// Exmaple: Yellow // Example: Yellow
Variant *string `json:"va,omitempty" mapstructure:"va,omitempty"` Variant *string `json:"va,omitempty" mapstructure:"va,omitempty"`
// Exmaple: 123.45 // Example: 123.45
Price *string `json:"pr,omitempty" mapstructure:"pr,omitempty"` Price *string `json:"pr,omitempty" mapstructure:"pr,omitempty"`
// Exmaple: 1 // Example: 1
Quantity *string `json:"qt,omitempty" mapstructure:"qt,omitempty"` Quantity *string `json:"qt,omitempty" mapstructure:"qt,omitempty"`
// Exmaple: 50%OFF // Example: 50%OFF
Coupon *string `json:"cp,omitempty" mapstructure:"cp,omitempty"` Coupon *string `json:"cp,omitempty" mapstructure:"cp,omitempty"`
// Exmaple: cross-selling: mens // Example: cross-selling: mens
ListName *string `json:"ln,omitempty" mapstructure:"ln,omitempty"` ListName *string `json:"ln,omitempty" mapstructure:"ln,omitempty"`
// Exmaple: 10 // Example: 10
ListPosition *string `json:"lp,omitempty" mapstructure:"lp,omitempty"` ListPosition *string `json:"lp,omitempty" mapstructure:"lp,omitempty"`
// Exmaple: id-mens-123 // Example: id-mens-123
ListID *string `json:"li,omitempty" mapstructure:"li,omitempty"` ListID *string `json:"li,omitempty" mapstructure:"li,omitempty"`
// Exmaple: 10.00 // Example: 10.00
Discount *string `json:"ds,omitempty" mapstructure:"ds,omitempty"` Discount *string `json:"ds,omitempty" mapstructure:"ds,omitempty"`
// Exmaple: Foo Marketplace // Example: Foo Marketplace
Affiliation *string `json:"af,omitempty" mapstructure:"af,omitempty"` Affiliation *string `json:"af,omitempty" mapstructure:"af,omitempty"`
// Example: ChIJIQBpAG2ahYAR_6128GcTUEo // Example: ChIJIQBpAG2ahYAR_6128GcTUEo
LocationID *string `json:"lo,omitempty" mapstructure:"lo,omitempty"` LocationID *string `json:"lo,omitempty" mapstructure:"lo,omitempty"`
} }

View File

@ -25,7 +25,6 @@ const (
type Data map[string]any type Data map[string]any
func Marshal(input *Event) (url.Values, io.Reader, error) { func Marshal(input *Event) (url.Values, io.Reader, error) {
a, err := json.Marshal(input) a, err := json.Marshal(input)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -81,7 +80,6 @@ func Marshal(input *Event) (url.Values, io.Reader, error) {
return ret, reader, nil return ret, reader, nil
} }
func UnmarshalURLValues(input url.Values, output interface{}) error { func UnmarshalURLValues(input url.Values, output interface{}) error {
data := Data{} data := Data{}
@ -147,7 +145,7 @@ func DecodeRegexValue(k string, v []string, r *regexp.Regexp, data Data, key str
v = []map[string]any{} v = []map[string]any{}
} }
v = append(v, value) v = append(v, value)
data[key] = value data[key] = v
return true, nil return true, nil
} }
} }
@ -157,7 +155,7 @@ func DecodeRegexValue(k string, v []string, r *regexp.Regexp, data Data, key str
// DecodeObjectValue e.g. `idSKU_123456` = map["id"]="SKU_123456" // DecodeObjectValue e.g. `idSKU_123456` = map["id"]="SKU_123456"
func DecodeObjectValue(s string) (map[string]any, error) { func DecodeObjectValue(s string) (map[string]any, error) {
if len(s) == 0 { if len(s) == 0 {
return nil, nil return nil, nil //nolint:nilnil
} }
ret := map[string]any{} ret := map[string]any{}
for _, part := range strings.Split(s, "~") { for _, part := range strings.Split(s, "~") {
@ -180,7 +178,7 @@ func EncodeObjectValue(s map[string]any) string {
keys = append(keys, k) keys = append(keys, k)
} }
sort.Strings(keys) sort.Strings(keys)
var ret []string ret := make([]string, 0, len(keys))
for _, k := range keys { for _, k := range keys {
ret = append(ret, k+fmt.Sprintf("%s", s[k])) ret = append(ret, k+fmt.Sprintf("%s", s[k]))
} }

View File

@ -10,7 +10,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestUnmarshalURLValues(t *testing.T) { func TestUnmarshalURLValues(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
@ -56,7 +55,7 @@ func TestMarshalURLValues(t *testing.T) {
// name: "add_to_cart", // 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", // 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, // want: nil,
//}, // },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
@ -67,14 +66,13 @@ func TestMarshalURLValues(t *testing.T) {
if !assert.Empty(t, e.Unknown) { if !assert.Empty(t, e.Unknown) {
t.Errorf("MarshalURLValues() = %v, want %v", e.Unknown, nil) t.Errorf("MarshalURLValues() = %v, want %v", e.Unknown, nil)
} }
if out, _, err := mpv2.Marshal(e); assert.NoError(t, err){ if out, _, err := mpv2.Marshal(e); assert.NoError(t, err) {
assert.EqualValues(t, u, out) assert.EqualValues(t, u, out)
} }
}) })
} }
} }
func TestCollect_decodeMapValue(t *testing.T) { func TestCollect_decodeMapValue(t *testing.T) {
tests := []struct { tests := []struct {
name string name string