diff --git a/packages/browser-destinations/destinations/google-analytics-4-web/src/__tests__/setConfigurationFields.test.ts b/packages/browser-destinations/destinations/google-analytics-4-web/src/__tests__/setConfigurationFields.test.ts new file mode 100644 index 0000000000..6b04b8c956 --- /dev/null +++ b/packages/browser-destinations/destinations/google-analytics-4-web/src/__tests__/setConfigurationFields.test.ts @@ -0,0 +1,618 @@ +import googleAnalytics4Web, { destination } from '../index' +import { Analytics, Context } from '@segment/analytics-next' +import { Subscription } from '@segment/browser-destination-runtime/types' +import type { Settings } from '../generated-types' + +let mockGtag: GA +let setConfigurationEvent: any +const subscriptions: Subscription[] = [ + { + partnerAction: 'setConfigurationFields', + name: 'Set Configuration Fields', + enabled: true, + subscribe: 'type = "page"', + mapping: { + ads_storage_consent_state: { + '@path': '$.properties.ads_storage_consent_state' + }, + analytics_storage_consent_state: { + '@path': '$.properties.analytics_storage_consent_state' + }, + screen_resolution: { + '@path': '$.properties.screen_resolution' + }, + user_id: { + '@path': '$.properties.user_id' + }, + page_title: { + '@path': '$.properties.page_title' + }, + page_referrer: { + '@path': '$.properties.page_referrer' + }, + language: { + '@path': '$.properties.language' + }, + content_group: { + '@path': '$.properties.content_group' + }, + campaign_content: { + '@path': '$.properties.campaign_content' + }, + campaign_id: { + '@path': '$.properties.campaign_id' + }, + campaign_medium: { + '@path': '$.properties.campaign_medium' + }, + campaign_name: { + '@path': '$.properties.campaign_name' + }, + campaign_source: { + '@path': '$.properties.campaign_source' + }, + campaign_term: { + '@path': '$.properties.campaign_term' + }, + ad_user_data_consent_state: { + '@path': '$.properties.ad_user_data_consent_state' + }, + ad_personalization_consent_state: { + '@path': '$.properties.ad_personalization_consent_state' + } + } + } +] + +describe('Set Configuration Fields action', () => { + const defaultSettings: Settings = { + enableConsentMode: false, + measurementID: 'G-XXXXXXXXXX', + allowAdPersonalizationSignals: false, + allowGoogleSignals: false, + cookieDomain: 'auto', + cookieExpirationInSeconds: 63072000, + cookieUpdate: true, + pageView: true + } + beforeEach(async () => { + jest.restoreAllMocks() + + const [setConfigurationEventPlugin] = await googleAnalytics4Web({ + ...defaultSettings, + subscriptions + }) + setConfigurationEvent = setConfigurationEventPlugin + + jest.spyOn(destination, 'initialize').mockImplementation(() => { + mockGtag = jest.fn() + return Promise.resolve(mockGtag) + }) + await setConfigurationEventPlugin.load(Context.system(), {} as Analytics) + }) + + it('should configure consent when consent mode is enabled', async () => { + defaultSettings.enableConsentMode = true + + const [setConfigurationEventPlugin] = await googleAnalytics4Web({ + ...defaultSettings, + subscriptions + }) + setConfigurationEvent = setConfigurationEventPlugin + await setConfigurationEventPlugin.load(Context.system(), {} as Analytics) + + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: { + ads_storage_consent_state: 'granted', + analytics_storage_consent_state: 'denied' + } + }) + + setConfigurationEvent.page?.(context) + + expect(mockGtag).toHaveBeenCalledWith('consent', 'update', { + ad_storage: 'granted', + analytics_storage: 'denied' + }) + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false + }) + }) + it('should configure cookie expiry time other then default value', async () => { + const settings = { + ...defaultSettings, + cookieExpirationInSeconds: 500 + } + const [setConfigurationEventPlugin] = await googleAnalytics4Web({ + ...settings, + subscriptions + }) + setConfigurationEvent = setConfigurationEventPlugin + await setConfigurationEventPlugin.load(Context.system(), {} as Analytics) + + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: {} + }) + + setConfigurationEvent.page?.(context) + + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false, + cookie_expires: 500 + }) + }) + it('should configure cookie domain other then default value', async () => { + const settings = { + ...defaultSettings, + cookieDomain: 'example.com' + } + + const [setConfigurationEventPlugin] = await googleAnalytics4Web({ + ...settings, + subscriptions + }) + setConfigurationEvent = setConfigurationEventPlugin + await setConfigurationEventPlugin.load(Context.system(), {} as Analytics) + + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: {} + }) + + setConfigurationEvent.page?.(context) + + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false, + cookie_domain: 'example.com' + }) + }) + it('should configure cookie prefix other then default value', async () => { + const settings = { + ...defaultSettings, + cookiePrefix: 'stage' + } + const [setConfigurationEventPlugin] = await googleAnalytics4Web({ + ...settings, + subscriptions + }) + setConfigurationEvent = setConfigurationEventPlugin + await setConfigurationEventPlugin.load(Context.system(), {} as Analytics) + + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: {} + }) + + setConfigurationEvent.page?.(context) + + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false, + cookie_prefix: 'stage' + }) + }) + it('should configure cookie path other then default value', async () => { + const settings = { + ...defaultSettings, + cookiePath: '/home' + } + + const [setConfigurationEventPlugin] = await googleAnalytics4Web({ + ...settings, + subscriptions + }) + setConfigurationEvent = setConfigurationEventPlugin + await setConfigurationEventPlugin.load(Context.system(), {} as Analytics) + + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: {} + }) + + setConfigurationEvent.page?.(context) + + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false, + cookie_path: '/home' + }) + }) + it('should configure cookie update other then default value', async () => { + const settings = { + ...defaultSettings, + cookieUpdate: false + } + const [setConfigurationEventPlugin] = await googleAnalytics4Web({ + ...settings, + subscriptions + }) + setConfigurationEvent = setConfigurationEventPlugin + await setConfigurationEventPlugin.load(Context.system(), {} as Analytics) + + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: {} + }) + + setConfigurationEvent.page?.(context) + + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false, + cookie_update: false + }) + }) + it('should not configure consent when consent mode is disabled', async () => { + const settings = { + ...defaultSettings, + enableConsentMode: false + } + const [setConfigurationEventPlugin] = await googleAnalytics4Web({ + ...settings, + subscriptions + }) + setConfigurationEvent = setConfigurationEventPlugin + await setConfigurationEventPlugin.load(Context.system(), {} as Analytics) + + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: {} + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false + }) + }) + it('should update config if payload has screen resolution', () => { + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: { + screen_resolution: '800*600' + } + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false, + screen_resolution: '800*600' + }) + }) + it('should update config if payload has user_id', () => { + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: { + user_id: 'segment-123' + } + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false, + user_id: 'segment-123' + }) + }) + it('should update config if payload has page_title', () => { + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: { + page_title: 'User Registration Page' + } + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false, + page_title: 'User Registration Page' + }) + }) + it('should update config if payload has language', () => { + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: { + language: 'EN' + } + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false, + language: 'EN' + }) + }) + it('should update config if payload has content_group', () => { + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: { + content_group: '/home/login' + } + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false, + content_group: '/home/login' + }) + }) + it('should update config if payload has campaign_term', () => { + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: { + campaign_term: 'running+shoes' + } + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false, + campaign_term: 'running+shoes' + }) + }) + it('should update config if payload has campaign_source', () => { + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: { + campaign_source: 'google' + } + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false, + campaign_source: 'google' + }) + }) + it('should update config if payload has campaign_name', () => { + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: { + campaign_name: 'spring_sale' + } + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false, + campaign_name: 'spring_sale' + }) + }) + it('should update config if payload has campaign_medium', () => { + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: { + campaign_medium: 'cpc' + } + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false, + campaign_medium: 'cpc' + }) + }) + it('should update config if payload has campaign_id', () => { + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: { + campaign_id: 'abc.123' + } + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false, + campaign_id: 'abc.123' + }) + }) + it('should update config if payload has campaign_content', () => { + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: { + campaign_content: 'logolink' + } + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false, + campaign_content: 'logolink' + }) + }) + + it('should update config if payload has send_page_view is true', async () => { + const settings = { + ...defaultSettings, + pageView: true + } + + const [setConfigurationEventPlugin] = await googleAnalytics4Web({ + ...settings, + subscriptions + }) + setConfigurationEvent = setConfigurationEventPlugin + await setConfigurationEventPlugin.load(Context.system(), {} as Analytics) + + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: {} + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false + }) + }) + it('should update config if payload has send_page_view is false', async () => { + const settings = { + ...defaultSettings, + pageView: false + } + + const [setConfigurationEventPlugin] = await googleAnalytics4Web({ + ...settings, + subscriptions + }) + setConfigurationEvent = setConfigurationEventPlugin + await setConfigurationEventPlugin.load(Context.system(), {} as Analytics) + + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: {} + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false, + send_page_view: false + }) + }) + it('should update config if payload has send_page_view is undefined', async () => { + const settings = { + ...defaultSettings, + pageView: undefined + } + + const [setConfigurationEventPlugin] = await googleAnalytics4Web({ + ...settings, + subscriptions + }) + setConfigurationEvent = setConfigurationEventPlugin + await setConfigurationEventPlugin.load(Context.system(), {} as Analytics) + + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: {} + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('config', 'G-XXXXXXXXXX', { + allow_ad_personalization_signals: false, + allow_google_signals: false, + send_page_view: true + }) + }) + + it('should update consent if payload has ad_user_data_consent_state granted', () => { + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: { + ad_user_data_consent_state: 'granted' + } + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('consent', 'update', { + ad_user_data: 'granted' + }) + }) + + it('should update consent if payload has ad_user_data_consent_state denied', () => { + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: { + ad_user_data_consent_state: 'denied' + } + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('consent', 'update', { + ad_user_data: 'denied' + }) + }) + + it('should update consent if payload has ad_user_data_consent_state undefined', () => { + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: { + ad_user_data_consent_state: undefined + } + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('consent', 'update', {}) + }) + + it('should update consent if payload has ad_personalization_consent_state granted', () => { + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: { + ad_personalization_consent_state: 'granted' + } + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('consent', 'update', { + ad_personalization: 'granted' + }) + }) + it('should update consent if payload has ad_personalization_consent_state denied', () => { + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: { + ad_personalization_consent_state: 'denied' + } + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('consent', 'update', { + ad_personalization: 'denied' + }) + }) + it('should update consent if payload has ad_personalization_consent_state undefined', () => { + const context = new Context({ + event: 'setConfigurationFields', + type: 'page', + properties: { + ad_personalization_consent_state: undefined + } + }) + + setConfigurationEvent.page?.(context) + expect(mockGtag).toHaveBeenCalledWith('consent', 'update', {}) + }) +}) diff --git a/packages/browser-destinations/destinations/google-analytics-4-web/src/generated-types.ts b/packages/browser-destinations/destinations/google-analytics-4-web/src/generated-types.ts index 37712dfed4..3582face62 100644 --- a/packages/browser-destinations/destinations/google-analytics-4-web/src/generated-types.ts +++ b/packages/browser-destinations/destinations/google-analytics-4-web/src/generated-types.ts @@ -26,9 +26,9 @@ export interface Settings { */ cookieFlags?: string[] /** - * Specifies the subpath used to store the analytics cookie. + * Specifies the subpath used to store the analytics cookie. We recommend to add a forward slash, / , in the first field as it is the Default Value for GA4. */ - cookiePath?: string[] + cookiePath?: string /** * Specifies a prefix to prepend to the analytics cookie name. */ @@ -49,6 +49,14 @@ export interface Settings { * The default value for analytics cookies consent state. This is only used if Enable Consent Mode is on. Set to “granted” if it is not explicitly set. Consent state can be updated for each user in the Set Configuration Fields action. */ defaultAnalyticsStorageConsentState?: string + /** + * Consent state indicated by the user for ad cookies. Value must be "granted" or "denied." This is only used if the Enable Consent Mode setting is on. + */ + adUserDataConsentState?: string + /** + * Consent state indicated by the user for ad cookies. Value must be "granted" or "denied." This is only used if the Enable Consent Mode setting is on. + */ + adPersonalizationConsentState?: string /** * If your CMP loads asynchronously, it might not always run before the Google tag. To handle such situations, specify a millisecond value to control how long to wait before the consent state update is sent. Please input the wait_for_update in milliseconds. */ diff --git a/packages/browser-destinations/destinations/google-analytics-4-web/src/index.ts b/packages/browser-destinations/destinations/google-analytics-4-web/src/index.ts index fd36d916fa..22400aa327 100644 --- a/packages/browser-destinations/destinations/google-analytics-4-web/src/index.ts +++ b/packages/browser-destinations/destinations/google-analytics-4-web/src/index.ts @@ -70,35 +70,40 @@ export const destination: BrowserDestinationDefinition = { cookieDomain: { description: 'Specifies the domain used to store the analytics cookie. Set to “auto” by default.', label: 'Cookie Domain', - type: 'string' + type: 'string', + default: 'auto' }, cookieExpirationInSeconds: { description: `Every time a hit is sent to GA4, the analytics cookie expiration time is updated to be the current time plus the value of this field. The default value is two years (63072000 seconds). Please input the expiration value in seconds. More information in [Google Documentation](https://developers.google.com/analytics/devguides/collection/ga4/reference/config#)`, label: 'Cookie Expiration In Seconds', - type: 'number' + type: 'number', + default: 63072000 }, cookieFlags: { description: `Appends additional flags to the analytics cookie. See [write a new cookie](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie#write_a_new_cookie) for some examples of flags to set.`, label: 'Cookie Flag', type: 'string', + default: undefined, multiple: true }, cookiePath: { - description: `Specifies the subpath used to store the analytics cookie.`, + description: `Specifies the subpath used to store the analytics cookie. We recommend to add a forward slash, / , in the first field as it is the Default Value for GA4.`, label: 'Cookie Path', type: 'string', - multiple: true + default: '/' }, cookiePrefix: { description: `Specifies a prefix to prepend to the analytics cookie name.`, label: 'Cookie Prefix', type: 'string', + default: undefined, multiple: true }, cookieUpdate: { description: `Set to false to not update cookies on each page load. This has the effect of cookie expiration being relative to the first time a user visited. Set to true by default so update cookies on each page load.`, label: 'Cookie Update', - type: 'boolean' + type: 'boolean', + default: true }, enableConsentMode: { description: `Set to true to enable Google’s [Consent Mode](https://support.google.com/analytics/answer/9976101?hl=en). Set to false by default.`, @@ -128,6 +133,28 @@ export const destination: BrowserDestinationDefinition = { ], default: 'granted' }, + adUserDataConsentState: { + description: + 'Consent state indicated by the user for ad cookies. Value must be "granted" or "denied." This is only used if the Enable Consent Mode setting is on.', + label: 'Ad User Data Consent State', + type: 'string', + choices: [ + { label: 'Granted', value: 'granted' }, + { label: 'Denied', value: 'denied' } + ], + default: undefined + }, + adPersonalizationConsentState: { + description: + 'Consent state indicated by the user for ad cookies. Value must be "granted" or "denied." This is only used if the Enable Consent Mode setting is on.', + label: 'Ad Personalization Consent State', + type: 'string', + choices: [ + { label: 'Granted', value: 'granted' }, + { label: 'Denied', value: 'denied' } + ], + default: undefined + }, waitTimeToUpdateConsentStage: { description: 'If your CMP loads asynchronously, it might not always run before the Google tag. To handle such situations, specify a millisecond value to control how long to wait before the consent state update is sent. Please input the wait_for_update in milliseconds.', @@ -151,11 +178,24 @@ export const destination: BrowserDestinationDefinition = { window.gtag('js', new Date()) if (settings.enableConsentMode) { - window.gtag('consent', 'default', { + const consent: { + ad_storage: ConsentParamsArg + analytics_storage: ConsentParamsArg + wait_for_update: number | undefined + ad_user_data?: ConsentParamsArg + ad_personalization?: ConsentParamsArg + } = { ad_storage: settings.defaultAdsStorageConsentState as ConsentParamsArg, analytics_storage: settings.defaultAnalyticsStorageConsentState as ConsentParamsArg, wait_for_update: settings.waitTimeToUpdateConsentStage - }) + } + if (settings.adUserDataConsentState) { + consent.ad_user_data = settings.adUserDataConsentState as ConsentParamsArg + } + if (settings.adPersonalizationConsentState) { + consent.ad_personalization = settings.adPersonalizationConsentState as ConsentParamsArg + } + gtag('consent', 'default', consent) } const script = `https://www.googletagmanager.com/gtag/js?id=${settings.measurementID}` await deps.loadScript(script) diff --git a/packages/browser-destinations/destinations/google-analytics-4-web/src/setConfigurationFields/generated-types.ts b/packages/browser-destinations/destinations/google-analytics-4-web/src/setConfigurationFields/generated-types.ts index 31201fd899..fd09195a3d 100644 --- a/packages/browser-destinations/destinations/google-analytics-4-web/src/setConfigurationFields/generated-types.ts +++ b/packages/browser-destinations/destinations/google-analytics-4-web/src/setConfigurationFields/generated-types.ts @@ -19,6 +19,14 @@ export interface Payload { * Consent state indicated by the user for ad cookies. Value must be “granted” or “denied.” This is only used if the Enable Consent Mode setting is on. */ analytics_storage_consent_state?: string + /** + * Consent state indicated by the user for ad cookies. Value must be "granted" or "denied." This is only used if the Enable Consent Mode setting is on. + */ + ad_user_data_consent_state?: string + /** + * Consent state indicated by the user for ad cookies. Value must be "granted" or "denied." This is only used if the Enable Consent Mode setting is on. + */ + ad_personalization_consent_state?: string /** * Use campaign content to differentiate ads or links that point to the same URL. Setting this value will override the utm_content query parameter. */ diff --git a/packages/browser-destinations/destinations/google-analytics-4-web/src/setConfigurationFields/index.ts b/packages/browser-destinations/destinations/google-analytics-4-web/src/setConfigurationFields/index.ts index 76ed7439e4..fb95992353 100644 --- a/packages/browser-destinations/destinations/google-analytics-4-web/src/setConfigurationFields/index.ts +++ b/packages/browser-destinations/destinations/google-analytics-4-web/src/setConfigurationFields/index.ts @@ -4,6 +4,8 @@ import type { Payload } from './generated-types' import { user_id, user_properties, params } from '../ga4-properties' type ConsentParamsArg = 'granted' | 'denied' | undefined +const defaultCookieExpiryInSecond = 63072000 +const defaultCookieDomain = 'auto' // Change from unknown to the partner SDK types const action: BrowserActionDefinition = { title: 'Set Configuration Fields', @@ -26,6 +28,28 @@ const action: BrowserActionDefinition = { label: 'Analytics Storage Consent State', type: 'string' }, + ad_user_data_consent_state: { + description: + 'Consent state indicated by the user for ad cookies. Value must be "granted" or "denied." This is only used if the Enable Consent Mode setting is on.', + label: 'Ad User Data Consent State', + type: 'string', + choices: [ + { label: 'Granted', value: 'granted' }, + { label: 'Denied', value: 'denied' } + ], + default: undefined + }, + ad_personalization_consent_state: { + description: + 'Consent state indicated by the user for ad cookies. Value must be "granted" or "denied." This is only used if the Enable Consent Mode setting is on.', + label: 'Ad Personalization Consent State', + type: 'string', + choices: [ + { label: 'Granted', value: 'granted' }, + { label: 'Denied', value: 'denied' } + ], + default: undefined + }, campaign_content: { description: 'Use campaign content to differentiate ads or links that point to the same URL. Setting this value will override the utm_content query parameter.', @@ -95,11 +119,29 @@ const action: BrowserActionDefinition = { params: params }, perform: (gtag, { payload, settings }) => { + const checkCookiePathDefaultValue = + settings.cookiePath != undefined && settings.cookiePath?.length !== 1 && settings.cookiePath !== '/' + if (settings.enableConsentMode) { - window.gtag('consent', 'update', { - ad_storage: payload.ads_storage_consent_state as ConsentParamsArg, - analytics_storage: payload.analytics_storage_consent_state as ConsentParamsArg - }) + const consentParams: { + ad_storage?: ConsentParamsArg + analytics_storage?: ConsentParamsArg + ad_user_data?: ConsentParamsArg + ad_personalization?: ConsentParamsArg + } = {} + if (payload.ads_storage_consent_state) { + consentParams.ad_storage = payload.ads_storage_consent_state as ConsentParamsArg + } + if (payload.analytics_storage_consent_state) { + consentParams.analytics_storage = payload.analytics_storage_consent_state as ConsentParamsArg + } + if (payload.ad_user_data_consent_state) { + consentParams.ad_user_data = payload.ad_user_data_consent_state as ConsentParamsArg + } + if (payload.ad_personalization_consent_state) { + consentParams.ad_personalization = payload.ad_personalization_consent_state as ConsentParamsArg + } + gtag('consent', 'update', consentParams) } type ConfigType = { [key: string]: unknown } @@ -109,23 +151,26 @@ const action: BrowserActionDefinition = { ...payload.params } - if (settings.cookieUpdate) { - config.cookie_update = settings.cookieUpdate + if (settings.cookieUpdate != true) { + config.cookie_update = false } - if (settings.cookieDomain) { + if (settings.cookieDomain != defaultCookieDomain) { config.cookie_domain = settings.cookieDomain } if (settings.cookiePrefix) { config.cookie_prefix = settings.cookiePrefix } - if (settings.cookieExpirationInSeconds) { + if (settings.cookieExpirationInSeconds != defaultCookieExpiryInSecond) { config.cookie_expires = settings.cookieExpirationInSeconds } - if (settings.cookiePath) { + if (checkCookiePathDefaultValue) { config.cookie_path = settings.cookiePath } - if (settings.pageView) { - config.send_page_view = settings.pageView + if (settings.pageView != true) { + config.send_page_view = settings.pageView ?? true + } + if (settings.cookieFlags) { + config.cookie_flags = settings.cookieFlags } if (payload.screen_resolution) { @@ -170,9 +215,6 @@ const action: BrowserActionDefinition = { if (payload.campaign_content) { config.campaign_content = payload.campaign_content } - if (settings.pageView != true) { - config.send_page_view = settings.pageView - } gtag('config', settings.measurementID, config) } diff --git a/packages/browser-destinations/package.json b/packages/browser-destinations/package.json index e55a019b42..69460a82e1 100644 --- a/packages/browser-destinations/package.json +++ b/packages/browser-destinations/package.json @@ -34,7 +34,7 @@ "@babel/preset-env": "^7.13.10", "@babel/preset-typescript": "^7.13.0", "@size-limit/preset-big-lib": "^11.0.1", - "@types/gtag.js": "^0.0.13", + "@types/gtag.js": "^0.0.19", "@types/jest": "^27.0.0", "compression-webpack-plugin": "^7.1.2", "concurrently": "^6.3.0", diff --git a/yarn.lock b/yarn.lock index 91a80a028c..f2808469a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3838,10 +3838,10 @@ dependencies: "@types/node" "*" -"@types/gtag.js@^0.0.13": - version "0.0.13" - resolved "https://registry.yarnpkg.com/@types/gtag.js/-/gtag.js-0.0.13.tgz#54d746635e09fa61242e05b574b1ac068e6a90dd" - integrity sha512-yOXFkfnt1DQr1v9B4ERulJOGnbdVqnPHV8NG4nkQhnu4qbrJecQ06DlaKmSjI3nzIwBj5U9/X61LY4sTc2KbaQ== +"@types/gtag.js@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@types/gtag.js/-/gtag.js-0.0.19.tgz#40ebbef85c8b2915df164d16304d3fbdfdaa1a41" + integrity sha512-KHoDzrf9rSd0mooKN576PjExpdk/XRrNu4RQnmigsScSTSidwyOUe9kDrHz9UPKjiBrx2QEsSkexbJSgS0j72w== "@types/http-cache-semantics@*": version "4.0.1"