From 2e0411855cb1dd5a321820e36242ea6d222f8d87 Mon Sep 17 00:00:00 2001 From: Kiko Ruiz Date: Tue, 17 Dec 2024 11:55:58 +0100 Subject: [PATCH 1/2] feat(packages/sui-segment-wrapper): send init event once per session --- packages/sui-segment-wrapper/src/index.js | 4 -- .../src/repositories/googleRepository.js | 40 +++++++++++++++---- .../sui-segment-wrapper/src/segmentWrapper.js | 6 +-- packages/sui-segment-wrapper/test/stubs.js | 6 +-- 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/packages/sui-segment-wrapper/src/index.js b/packages/sui-segment-wrapper/src/index.js index 40a850eff..b770031a2 100644 --- a/packages/sui-segment-wrapper/src/index.js +++ b/packages/sui-segment-wrapper/src/index.js @@ -12,8 +12,6 @@ import initTcfTracking from './tcf.js' import {getUserDataAndNotify} from './universalId.js' import {loadGoogleAnalytics, getCampaignDetails} from './repositories/googleRepository.js' -const DEFAULT_GA_INIT_EVENT = 'sui' - // Initialize TCF Tracking with Segment initTcfTracking() @@ -36,7 +34,6 @@ const addMiddlewares = () => { if (isClient && window.analytics) { // Initialize Google Analtyics if needed const googleAnalyticsMeasurementId = getConfig('googleAnalyticsMeasurementId') - const googleAnalyticsInitEvent = getConfig('googleAnalyticsInitEvent') ?? DEFAULT_GA_INIT_EVENT if (googleAnalyticsMeasurementId) { const googleAnalyticsConfig = getConfig('googleAnalyticsConfig') @@ -55,7 +52,6 @@ if (isClient && window.analytics) { ...googleAnalyticsConfig, ...getCampaignDetails() }) - window.gtag('event', googleAnalyticsInitEvent) loadGoogleAnalytics().catch(error => { console.error(error) diff --git a/packages/sui-segment-wrapper/src/repositories/googleRepository.js b/packages/sui-segment-wrapper/src/repositories/googleRepository.js index e9b2055c1..5dc8d074c 100644 --- a/packages/sui-segment-wrapper/src/repositories/googleRepository.js +++ b/packages/sui-segment-wrapper/src/repositories/googleRepository.js @@ -5,13 +5,11 @@ const FIELDS = { clientId: 'client_id', sessionId: 'session_id' } - const STC = { QUERY: 'stc', SPLIT_SYMBOL: '-', CAMPAIGN_SPLIT_SYMBOL: ':' } - const STC_MEDIUM_TRANSFORMATIONS = { aff: 'affiliate', dis: 'display', @@ -24,8 +22,8 @@ const STC_MEDIUM_TRANSFORMATIONS = { pn: 'push-notification', cs: 'cross-sites' } - const STC_INVALID_CONTENT = 'na' +const DEFAULT_GA_INIT_EVENT = 'sui' const loadScript = async src => new Promise(function (resolve, reject) { @@ -48,14 +46,36 @@ export const loadGoogleAnalytics = async () => { return loadScript(gtagScript) } +// Trigger GA init event just once per session. +const triggerGoogleAnalyticsInitEvent = sessionId => { + const eventName = getConfig('googleAnalyticsInitEvent') ?? DEFAULT_GA_INIT_EVENT + const eventPrefix = `ga_event_${eventName}_` + const eventKey = `${eventPrefix}${sessionId}` + + if (!localStorage.getItem(eventKey)) { + window.gtag('event', eventName) + console.log(`Sending GA4 event "${eventName}" for the session "${sessionId}"`) + + // Save new GA session hit. + localStorage.setItem(eventKey, 'true') + } + + // Clean old GA sessions hits. + Object.keys(localStorage).forEach(key => { + if (key.startsWith(eventPrefix) && key !== eventKey) { + localStorage.removeItem(key) + } + }) +} + const getGoogleField = async field => { const googleAnalyticsMeasurementId = getConfig('googleAnalyticsMeasurementId') - // If `googleAnalyticsMeasurementId` is not present, don't load anything + // If `googleAnalyticsMeasurementId` is not present, don't load anything. if (!googleAnalyticsMeasurementId) return Promise.resolve() return new Promise(resolve => { - // if not, get it from the `GoogleAnalytics` tag + // If it is, get it from `gtag`. window.gtag?.('get', googleAnalyticsMeasurementId, field, resolve) }) } @@ -83,8 +103,14 @@ export const getCampaignDetails = ({needsTransformation = true} = {}) => { } } -export const getGoogleClientID = () => getGoogleField(FIELDS.clientId) -export const getGoogleSessionID = () => getGoogleField(FIELDS.sessionId) +export const getGoogleClientId = async () => getGoogleField(FIELDS.clientId) +export const getGoogleSessionId = async () => { + const sessionId = await getGoogleField(FIELDS.sessionId) + + triggerGoogleAnalyticsInitEvent(sessionId) + + return sessionId +} export const setGoogleUserId = userId => { const googleAnalyticsMeasurementId = getConfig('googleAnalyticsMeasurementId') diff --git a/packages/sui-segment-wrapper/src/segmentWrapper.js b/packages/sui-segment-wrapper/src/segmentWrapper.js index f990e39fe..13699a69e 100644 --- a/packages/sui-segment-wrapper/src/segmentWrapper.js +++ b/packages/sui-segment-wrapper/src/segmentWrapper.js @@ -1,7 +1,7 @@ // @ts-check import {getAdobeMCVisitorID} from './repositories/adobeRepository.js' -import {getGoogleClientID, getGoogleSessionID, setGoogleUserId} from './repositories/googleRepository.js' +import {getGoogleClientId, getGoogleSessionId, setGoogleUserId} from './repositories/googleRepository.js' import {getConfig} from './config.js' import {checkAnalyticsGdprIsAccepted, getGdprPrivacyValue} from './tcf.js' import {getXandrId} from './repositories/xandrRepository.js' @@ -42,8 +42,8 @@ const getTrackIntegrations = async ({gdprPrivacyValue, event}) => { try { ;[marketingCloudVisitorId, clientId, sessionId] = await Promise.all([ getAdobeMCVisitorID(), - getGoogleClientID(), - getGoogleSessionID() + getGoogleClientId(), + getGoogleSessionId() ]) } catch (error) { console.error(error) diff --git a/packages/sui-segment-wrapper/test/stubs.js b/packages/sui-segment-wrapper/test/stubs.js index 748ab3ca7..c387f2256 100644 --- a/packages/sui-segment-wrapper/test/stubs.js +++ b/packages/sui-segment-wrapper/test/stubs.js @@ -43,18 +43,18 @@ export const stubFetch = ({responses = [{urlRe: /^http/, fetchResponse: {}}]} = } export const stubGoogleAnalytics = () => { - const savedFields = { + const fakeFields = { client_id: 'fakeClientId', session_id: 'fakeSessionId' } window.gtag = (key, id, field, done) => { if (key === 'get') { - return done(savedFields?.[field]) + return done(fakeFields?.[field]) } if (key === 'set') { - savedFields[id] = field + fakeFields[id] = field } } } From edd7b699a0a9702e52c98e654b6238a7ced5edf6 Mon Sep 17 00:00:00 2001 From: Kiko Ruiz Date: Tue, 17 Dec 2024 12:15:35 +0100 Subject: [PATCH 2/2] refactor(packages/sui-segment-wrapper): add comments --- .../src/repositories/googleRepository.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/sui-segment-wrapper/src/repositories/googleRepository.js b/packages/sui-segment-wrapper/src/repositories/googleRepository.js index 5dc8d074c..0e588ec57 100644 --- a/packages/sui-segment-wrapper/src/repositories/googleRepository.js +++ b/packages/sui-segment-wrapper/src/repositories/googleRepository.js @@ -52,15 +52,17 @@ const triggerGoogleAnalyticsInitEvent = sessionId => { const eventPrefix = `ga_event_${eventName}_` const eventKey = `${eventPrefix}${sessionId}` + // Check if the event has already been sent in this session. if (!localStorage.getItem(eventKey)) { + // If not, send it. window.gtag('event', eventName) console.log(`Sending GA4 event "${eventName}" for the session "${sessionId}"`) - // Save new GA session hit. + // And then save a new GA session hit in local storage. localStorage.setItem(eventKey, 'true') } - // Clean old GA sessions hits. + // Clean old GA sessions hits from the storage. Object.keys(localStorage).forEach(key => { if (key.startsWith(eventPrefix) && key !== eventKey) { localStorage.removeItem(key)