From 615b57f387cd23980dc650b5458583049092869a Mon Sep 17 00:00:00 2001 From: Kevin Franklin Kim Date: Wed, 20 Nov 2024 09:30:12 +0100 Subject: [PATCH] feat: add consent --- .../watermill/gtag/messagehandler_test.go | 2 +- pkg/encoding/gtag/consent.go | 39 +++++++++++++++++++ pkg/encoding/gtag/payload.go | 5 --- pkg/encoding/gtagencode/mpv2.go | 11 ++++++ pkg/encoding/mpv2/consent.go | 7 ++++ 5 files changed, 58 insertions(+), 6 deletions(-) diff --git a/integration/watermill/gtag/messagehandler_test.go b/integration/watermill/gtag/messagehandler_test.go index 5aa4085..43c00e6 100644 --- a/integration/watermill/gtag/messagehandler_test.go +++ b/integration/watermill/gtag/messagehandler_test.go @@ -99,7 +99,7 @@ func TestMPv2MessageHandler(t *testing.T) { var done atomic.Bool router.AddHandler("gtag", "in", pubSub, "out", pubSub, gtag.MPv2MessageHandler) router.AddNoPublisherHandler("mpv2", "out", pubSub, func(msg *message.Message) error { - expected := `{"client_id":"C123456","events":[{"name":"add_to_cart","params":{}}],"debug_mode":true}` + expected := `{"client_id":"C123456","consent":{"ad_user_data":"GRANTED","ad_personalization":"GRANTED","analytics_storage":"GRANTED"},"events":[{"name":"add_to_cart","params":{"page_location":"https://foomo.org","page_title":"Home"}}],"debug_mode":true}` if !assert.JSONEq(t, expected, string(msg.Payload)) { fmt.Println(string(msg.Payload)) } diff --git a/pkg/encoding/gtag/consent.go b/pkg/encoding/gtag/consent.go index c1ab4d8..7808b38 100644 --- a/pkg/encoding/gtag/consent.go +++ b/pkg/encoding/gtag/consent.go @@ -1,9 +1,11 @@ package gtag import ( + "slices" "strings" ) +// See https://developers.google.com/tag-platform/security/concepts/consent-mode type Consent struct { // Current Google Consent Status. Format 'G1'+'AdsStorageBoolStatus'`+'AnalyticsStorageBoolStatus' // Example: G101 @@ -17,6 +19,11 @@ type Consent struct { // Will be added with the value "1" if the Google Consent had a default value before getting an update // Example: G111 GoogleConsentDefault *string `json:"google_consent_default,omitempty" gtag:"gcd,omitempty"` + // Example: 1 + // DigitalMarketAct *string `json:"digital_market_act,omitempty" gtag:"dma,omitempty"` + // Example: sypham + // DigitalMarketActParameters *string `json:"digital_market_act_parameters,omitempty" gtag:"dma_cps,omitempty"` + // Example: noapi | denied } // ------------------------------------------------------------------------------------------------ @@ -24,6 +31,12 @@ type Consent struct { // ------------------------------------------------------------------------------------------------ func (c Consent) AdStorage() bool { + if c.GoogleConsentDefault != nil { + gcd := strings.Split(*c.GoogleConsentDefault, "") + if len(gcd) > 3 { + return slices.Contains([]string{"l", "t", "r", "n", "u", "v"}, gcd[2]) + } + } if c.GoogleConsentUpdate != nil { gcs := *c.GoogleConsentUpdate if strings.HasPrefix(gcs, "G1") && len(gcs) == 4 { @@ -35,6 +48,12 @@ func (c Consent) AdStorage() bool { } func (c Consent) AnalyticsStorage() bool { + if c.GoogleConsentDefault != nil { + gcd := strings.Split(*c.GoogleConsentDefault, "") + if len(gcd) > 5 { + return slices.Contains([]string{"l", "t", "r", "n", "u", "v"}, gcd[4]) + } + } if c.GoogleConsentUpdate != nil { gcs := *c.GoogleConsentUpdate if strings.HasPrefix(gcs, "G1") && len(gcs) == 4 { @@ -44,3 +63,23 @@ func (c Consent) AnalyticsStorage() bool { } return true } + +func (c Consent) AdUserData() bool { + if c.GoogleConsentDefault != nil { + gcd := strings.Split(*c.GoogleConsentDefault, "") + if len(gcd) > 7 { + return slices.Contains([]string{"l", "t", "r", "n", "u", "v"}, gcd[6]) + } + } + return c.AdStorage() +} + +func (c Consent) AdPersonalization() bool { + if c.GoogleConsentDefault != nil { + gcd := strings.Split(*c.GoogleConsentDefault, "") + if len(gcd) > 9 { + return slices.Contains([]string{"l", "t", "r", "n", "u", "v"}, gcd[8]) + } + } + return c.AdStorage() +} diff --git a/pkg/encoding/gtag/payload.go b/pkg/encoding/gtag/payload.go index bdaffdd..6438ebd 100644 --- a/pkg/encoding/gtag/payload.go +++ b/pkg/encoding/gtag/payload.go @@ -151,11 +151,6 @@ type Payload struct { NonPersonalizedAds *string `json:"non_personalized_ads,omitempty" gtag:"npa,omitempty"` // Example: 1 // ARE *string `json:"are,omitempty" gtag:"are,omitempty"` - // Example: 1 - // DigitalMarketAct *string `json:"digital_market_act,omitempty" gtag:"dma,omitempty"` - // Example: sypham - // DigitalMarketActParameters *string `json:"digital_market_act_parameters,omitempty" gtag:"dma_cps,omitempty"` - // Example: noapi | denied // PrivacySandboxCookieDeprecationLabel *string `json:"privacy_sandbox_cookie_deprecation_label,omitempty" gtag:"pscdl,omitempty"` // 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" gtag:"tfd,omitempty"` diff --git a/pkg/encoding/gtagencode/mpv2.go b/pkg/encoding/gtagencode/mpv2.go index cbe7f28..547b1da 100644 --- a/pkg/encoding/gtagencode/mpv2.go +++ b/pkg/encoding/gtagencode/mpv2.go @@ -7,6 +7,7 @@ import ( "strconv" "github.com/foomo/sesamy-go/pkg/encoding/gtag" + "github.com/foomo/sesamy-go/pkg/encoding/mpv2" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" ) @@ -26,10 +27,20 @@ func MPv2(source gtag.Payload, target any) error { targetData := map[string]any{ "client_id": source.ClientID, "user_id": source.UserID, + "session_id": source.SessionID, "non_personalized_ads": source.NonPersonalizedAds, "debug_mode": source.IsDebug, } + // consent + targetConsentData := map[string]any{ + "add_storage": mpv2.ConsentText(source.AdStorage()), + "ad_user_data": mpv2.ConsentText(source.AdUserData()), + "ad_personalization": mpv2.ConsentText(source.AdPersonalization()), + "analytics_storage": mpv2.ConsentText(source.AnalyticsStorage()), + } + targetData["consent"] = targetConsentData + // combine user properties targetUserProperties := map[string]any{} if node, ok := sourceData["user_property"].(map[string]string); ok { diff --git a/pkg/encoding/mpv2/consent.go b/pkg/encoding/mpv2/consent.go index 2a78551..a770e01 100644 --- a/pkg/encoding/mpv2/consent.go +++ b/pkg/encoding/mpv2/consent.go @@ -6,3 +6,10 @@ const ( ConsentDenied Consent = "DENIED" ConsentGranted Consent = "GRANTED" ) + +func ConsentText(v bool) Consent { + if v { + return ConsentGranted + } + return ConsentDenied +}