diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1df0b56..7f44726 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,7 @@ jobs: version: latest args: --timeout=5m - - run: make test + - run: GO_TEST_TAGS=-skip go test -coverprofile=coverage.out -race -json ./... | gotestfmt - uses: coverallsapp/github-action@v2 with: diff --git a/pkg/encoding/mpv2/userdata.go b/pkg/encoding/mpv2/userdata.go index fd16bc0..660cc1b 100644 --- a/pkg/encoding/mpv2/userdata.go +++ b/pkg/encoding/mpv2/userdata.go @@ -2,7 +2,7 @@ package mpv2 // UserData https://developers.google.com/analytics/devguides/collection/ga4/uid-data type UserData struct { - SHA256EmailAddress []SHA256Hash `json:"sha256_email_address,omitempty"` - SHA256PhoneNumber []SHA256Hash `json:"sha256_phone_number,omitempty"` - Address []UserDataAddress `json:"address,omitempty"` + SHA256EmailAddress SHA256Hash `json:"sha256_email_address,omitempty"` + SHA256PhoneNumber SHA256Hash `json:"sha256_phone_number,omitempty"` + Address UserDataAddress `json:"address,omitempty"` } diff --git a/pkg/http/mpv2/middleware.go b/pkg/http/mpv2/middleware.go index 3ad1bd4..8fbdddb 100644 --- a/pkg/http/mpv2/middleware.go +++ b/pkg/http/mpv2/middleware.go @@ -38,11 +38,25 @@ func MiddlewareSessionID(measurementID string) Middleware { return func(next MiddlewareHandler) MiddlewareHandler { return func(l *zap.Logger, w http.ResponseWriter, r *http.Request, payload *mpv2.Payload[any]) error { if payload.SessionID == "" { - value, err := session.ParseGASessionID(r, measurementID) + id, err := session.ParseGASessionID(r, measurementID) if err != nil && !errors.Is(err, http.ErrNoCookie) { return err } - payload.SessionID = value + + number, err := session.ParseGASessionNumber(r, measurementID) + if err != nil && !errors.Is(err, http.ErrNoCookie) { + return err + } + + payload.SessionID = id + for i, event := range payload.Events { + if value, ok := event.Params.(map[string]any); ok { + value["ga_session_id"] = id + value["ga_session_number"] = number + event.Params = value + } + payload.Events[i] = event + } } return next(l, w, r, payload) } @@ -100,9 +114,12 @@ func MiddlewareUserAgent(next MiddlewareHandler) MiddlewareHandler { if userAgent := r.Header.Get("User-Agent"); userAgent != "" { for i, event := range payload.Events { if value, ok := event.Params.(map[string]any); ok { - value["user_agent"] = userAgent - payload.Events[i] = event + if value["user_agent"] == nil { + value["user_agent"] = userAgent + } + event.Params = value } + payload.Events[i] = event } } return next(l, w, r, payload) @@ -121,24 +138,51 @@ func MiddlewareIPOverride(next MiddlewareHandler) MiddlewareHandler { if ipOverride != "" { for i, event := range payload.Events { if value, ok := event.Params.(map[string]any); ok { - value["ip_override"] = ipOverride - payload.Events[i] = event + if value["ip_override"] == nil { + value["ip_override"] = ipOverride + } + event.Params = value } + payload.Events[i] = event } } return next(l, w, r, payload) } } +func MiddlewareEngagementTime(next MiddlewareHandler) MiddlewareHandler { + return func(l *zap.Logger, w http.ResponseWriter, r *http.Request, payload *mpv2.Payload[any]) error { + for i, event := range payload.Events { + if value, ok := event.Params.(map[string]any); ok { + if value["engagement_time_msec"] == nil { + value["engagement_time_msec"] = 100 + } + event.Params = value + } + payload.Events[i] = event + } + return next(l, w, r, payload) + } +} func MiddlewarePageLocation(next MiddlewareHandler) MiddlewareHandler { return func(l *zap.Logger, w http.ResponseWriter, r *http.Request, payload *mpv2.Payload[any]) error { - if referrer := r.Header.Get("Referer"); referrer != "" { - for i, event := range payload.Events { - if value, ok := event.Params.(map[string]any); ok { - value["page_location"] = referrer - payload.Events[i] = event + pageTitle := r.Header.Get("X-Page-Title") + pageLocation := r.Header.Get("Referer") + pageReferrer := r.Header.Get("X-Page-Referrer") + for i, event := range payload.Events { + if value, ok := event.Params.(map[string]any); ok { + if value["page_title"] == nil && pageTitle != "" { + value["page_title"] = pageTitle } + if value["page_referrer"] == nil && pageReferrer != "" { + value["page_referrer"] = pageReferrer + } + if value["page_location"] == nil && pageLocation != "" { + value["page_location"] = pageLocation + } + event.Params = value } + payload.Events[i] = event } return next(l, w, r, payload) } diff --git a/pkg/provider/cookiebot/client/mpv2middleware.go b/pkg/provider/cookiebot/client/mpv2middleware.go index 06e2830..21d5ac6 100644 --- a/pkg/provider/cookiebot/client/mpv2middleware.go +++ b/pkg/provider/cookiebot/client/mpv2middleware.go @@ -6,7 +6,6 @@ import ( "net/url" "strings" - "github.com/davecgh/go-spew/spew" "github.com/foomo/sesamy-go/pkg/client" "github.com/foomo/sesamy-go/pkg/encoding/mpv2" "github.com/foomo/sesamy-go/pkg/provider/cookiebot" @@ -39,7 +38,6 @@ func MPv2MiddlewarConsent(l *zap.Logger) client.MPv2Middleware { l.With(zap.Error(err), zap.String("value", data)).Warn("failed to unmarshal cookie bot cookie") return next(r, payload) } - spew.Dump(value) consent := func(b bool) *mpv2.Consent { ret := mpv2.ConsentDenied @@ -58,7 +56,6 @@ func MPv2MiddlewarConsent(l *zap.Logger) client.MPv2Middleware { FunctionalityStorage: consent(value.Necessary), SecurityStorage: consent(value.Necessary), } - spew.Dump(payload) return next(r, payload) } diff --git a/pkg/session/ga.go b/pkg/session/ga.go index bf51b7f..f9d4741 100644 --- a/pkg/session/ga.go +++ b/pkg/session/ga.go @@ -38,3 +38,19 @@ func ParseGASessionID(r *http.Request, id string) (string, error) { return parts[2], nil } + +func ParseGASessionNumber(r *http.Request, id string) (string, error) { + cookie, err := r.Cookie("_ga_" + id) + if err != nil { + return "", errors.Wrap(err, "failed to retrieve _ga cookie") + } + + parts := strings.Split(cookie.Value, ".") + + // validate + if !strings.HasPrefix(cookie.Value, "GS1.1") || len(parts) < 4 { + return "", errors.New("invalid _ga cookie value") + } + + return parts[3], nil +}