Skip to content

Commit

Permalink
Update browser-plugin-ga-cookies to include options for Google Analyt…
Browse files Browse the repository at this point in the history
…ics 4 cookies
  • Loading branch information
igneel64 committed Aug 15, 2023
1 parent f717666 commit b4cd43f
Show file tree
Hide file tree
Showing 12 changed files with 362 additions and 90 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@snowplow/browser-plugin-ga-cookies",
"comment": "Update to include options for Google Analytics 4 cookies",
"type": "none"
}
],
"packageName": "@snowplow/browser-plugin-ga-cookies"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@snowplow/javascript-tracker",
"comment": "Add further options for the gaCookies context",
"type": "none"
}
],
"packageName": "@snowplow/javascript-tracker"
}
18 changes: 15 additions & 3 deletions plugins/browser-plugin-ga-cookies/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

Browser Plugin to be used with `@snowplow/browser-tracker`.

Adds GA Cookies to your Snowplow tracking.
Adds Universal Analytics and Google Analytics 4 cookies to your Snowplow tracking.

## Maintainer quick start

Expand Down Expand Up @@ -36,14 +36,26 @@ Initialize your tracker with the GaCookiesPlugin:
import { newTracker } from '@snowplow/browser-tracker';
import { GaCookiesPlugin } from '@snowplow/browser-plugin-ga-cookies';

newTracker('sp1', '{{collector}}', { plugins: [ GaCookiesPlugin() ] }); // Also stores reference at module level
newTracker('sp1', '{{collector}}', { plugins: [ GaCookiesPlugin(
/* pluginOptions */
) ] });

/*
* Available plugin options `GACookiesPluginOptions`:
* {
* ua: Send Universal Analytics specific cookie values. Defaults to true.
* ga4: Send Google Analytics 4 specific cookie values. Defaults to false.
* ga4MeasurementId: Measurement id/ids to search the Google Analytics 4 session cookie. Can be a single measurement id as a string or an array of measurement id strings. The cookie has the form of <cookie_prefix>_ga_<container-id> where <container-id> is the data stream container id and <cookie_prefix> is the optional cookie_prefix option of the gtag.js tracker.
* cookiePrefix: Cookie prefix set on the Google Analytics 4 cookies using the cookie_prefix option of the gtag.js tracker.
* }
*/
```

## Copyright and license

Licensed and distributed under the [BSD 3-Clause License](LICENSE) ([An OSI Approved License][osi]).

Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang.
Copyright (c) 2023 Snowplow Analytics Ltd.

All rights reserved.

Expand Down
5 changes: 5 additions & 0 deletions plugins/browser-plugin-ga-cookies/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
preset: 'ts-jest',
reporters: ['jest-standard-reporter'],
testEnvironment: 'jest-environment-jsdom-global',
};
2 changes: 1 addition & 1 deletion plugins/browser-plugin-ga-cookies/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
],
"scripts": {
"build": "rollup -c --silent --failAfterWarnings",
"test": ""
"test": "jest"
},
"dependencies": {
"@snowplow/browser-tracker-core": "workspace:*",
Expand Down
42 changes: 0 additions & 42 deletions plugins/browser-plugin-ga-cookies/src/contexts.ts

This file was deleted.

114 changes: 72 additions & 42 deletions plugins/browser-plugin-ga-cookies/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,84 @@
/*
* Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import { SelfDescribingJson } from '@snowplow/tracker-core';
import { BrowserPlugin, cookie } from '@snowplow/browser-tracker-core';
import { Cookies } from './contexts';
import { GA4Cookies, GA4SessionCookies, UACookies } from './types';
import { GOOGLE_ANALYTICS_4_COOKIES_SCHEMA, UNIVERSAL_ANALYTICS_COOKIES_SCHEMA } from './schemata';

interface GACookiesPluginOptions {
ua?: boolean;
ga4: boolean;
ga4MeasurementId: string | string[];
cookiePrefix?: string;
}

const defaultPluginOptions: GACookiesPluginOptions = {
ua: true,
ga4: false,
ga4MeasurementId: '',
cookiePrefix: '',
};

/**
* Captures the GA cookies on a page and sends as context on each event
* @param pluginOptions.ua - Send Universal Analytics specific cookie values.
* @param pluginOptions.ga4 - Send Google Analytics 4 specific cookie values.
* @param pluginOptions.ga4MeasurementId - Measurement id/ids to search the Google Analytics 4 session cookie. Can be a single measurement id as a string or an array of measurement id strings. The cookie has the form of _ga_<container-id> where <container-id> is the data stream container id.
* @param pluginOptions.cookiePrefix - Cookie prefix set on the Google Analytics 4 cookies.
*/
export function GaCookiesPlugin(): BrowserPlugin {
export function GaCookiesPlugin(pluginOptions: GACookiesPluginOptions = defaultPluginOptions): BrowserPlugin {
return {
contexts: () => {
const gaCookieData: SelfDescribingJson<Cookies> = {
schema: 'iglu:com.google.analytics/cookies/jsonschema/1-0-0',
data: {},
};
['__utma', '__utmb', '__utmc', '__utmv', '__utmz', '_ga'].forEach(function (cookieType) {
var value = cookie(cookieType);
if (value) {
gaCookieData.data[cookieType] = value;
const contexts: SelfDescribingJson<Record<string, unknown>>[] = [];
const { ga4, ga4MeasurementId, ua, cookiePrefix } = { ...defaultPluginOptions, ...pluginOptions };
const GA_USER_COOKIE = '_ga';
const GA4_MEASUREMENT_ID_PREFIX = 'G-';
const GA4_COOKIE_PREFIX = '_ga_';

if (ua) {
const uaCookiesContext: SelfDescribingJson<UACookies> = {
schema: UNIVERSAL_ANALYTICS_COOKIES_SCHEMA,
data: {},
};
['__utma', '__utmb', '__utmc', '__utmv', '__utmz', GA_USER_COOKIE].forEach(function (cookieType) {
var value = cookie(cookieType);
if (value) {
uaCookiesContext.data[cookieType] = value;
}
});
contexts.push(uaCookiesContext);
}

if (ga4) {
const ga4CookiesContext: SelfDescribingJson<GA4Cookies> = {
schema: GOOGLE_ANALYTICS_4_COOKIES_SCHEMA,
data: {
_ga: cookie(cookiePrefix + GA_USER_COOKIE),
session_cookies: undefined,
},
};

if (ga4MeasurementId) {
const measurementIdentifiers = Array.isArray(ga4MeasurementId) ? [...ga4MeasurementId] : [ga4MeasurementId];
const sessionCookies: GA4SessionCookies[] = [];

measurementIdentifiers.forEach(function (cookieIdentifier) {
const sessionCookieValue = cookie(
cookiePrefix + cookieIdentifier.replace(GA4_MEASUREMENT_ID_PREFIX, GA4_COOKIE_PREFIX)
);
if (sessionCookieValue) {
sessionCookies.push({
measurement_id: cookieIdentifier,
session_cookie: sessionCookieValue,
});
}
});

ga4CookiesContext.data.session_cookies = sessionCookies;
}
});
return [gaCookieData];

contexts.push(ga4CookiesContext);
}

return contexts;
},
};
}
2 changes: 2 additions & 0 deletions plugins/browser-plugin-ga-cookies/src/schemata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const UNIVERSAL_ANALYTICS_COOKIES_SCHEMA = 'iglu:com.google.analytics/cookies/jsonschema/1-0-0';
export const GOOGLE_ANALYTICS_4_COOKIES_SCHEMA = 'iglu:com.google.ga4/cookies/jsonschema/1-0-0';
20 changes: 20 additions & 0 deletions plugins/browser-plugin-ga-cookies/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export interface UACookies {
__utma?: string;
__utmb?: string;
__utmc?: string;
__utmv?: string;
__utmz?: string;
_ga?: string;
[key: string]: string | undefined;
}

export interface GA4Cookies {
_ga?: string;
session_cookies?: GA4SessionCookies[];
[key: string]: unknown;
}

export interface GA4SessionCookies {
measurement_id: string;
session_cookie?: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`GA Cookies plugin Correctly returns values for Google Analytics 4 cookies for multiple measurement ids 1`] = `
Array [
Object {
"data": Object {
"_ga": "1234",
"session_cookies": Array [
Object {
"measurement_id": "G-1234",
"session_cookie": "567",
},
Object {
"measurement_id": "G-5678",
"session_cookie": "789",
},
],
},
"schema": "iglu:com.google.ga4/cookies/jsonschema/1-0-0",
},
]
`;

exports[`GA Cookies plugin Correctly returns values for Google Analytics 4 cookies with cookie prefix 1`] = `
Array [
Object {
"data": Object {
"_ga": "1234",
"session_cookies": Array [
Object {
"measurement_id": "G-1234",
"session_cookie": "567",
},
],
},
"schema": "iglu:com.google.ga4/cookies/jsonschema/1-0-0",
},
]
`;

exports[`GA Cookies plugin Only returns values for Google Analytics 4 user cookies without measurement id/s and GA4 enabled 1`] = `
Array [
Object {
"data": Object {
"_ga": "1234",
"session_cookies": undefined,
},
"schema": "iglu:com.google.ga4/cookies/jsonschema/1-0-0",
},
]
`;

exports[`GA Cookies plugin Returns values for Google Analytics 4 cookies and Universal Analytics cookies 1`] = `
Array [
Object {
"data": Object {
"__utma": "567",
"_ga": "1234",
},
"schema": "iglu:com.google.analytics/cookies/jsonschema/1-0-0",
},
Object {
"data": Object {
"_ga": "1234",
"session_cookies": Array [
Object {
"measurement_id": "G-1234",
"session_cookie": "567",
},
],
},
"schema": "iglu:com.google.ga4/cookies/jsonschema/1-0-0",
},
]
`;

exports[`GA Cookies plugin Returns values for Google Analytics 4 cookies and not Universal Analytics cookies 1`] = `
Array [
Object {
"data": Object {
"_ga": "1234",
"session_cookies": Array [
Object {
"measurement_id": "G-1234",
"session_cookie": "567",
},
],
},
"schema": "iglu:com.google.ga4/cookies/jsonschema/1-0-0",
},
]
`;

exports[`GA Cookies plugin Returns values for Universal Analytics cookies by default 1`] = `
Array [
Object {
"data": Object {
"__utma": "567",
"_ga": "1234",
},
"schema": "iglu:com.google.analytics/cookies/jsonschema/1-0-0",
},
]
`;

0 comments on commit b4cd43f

Please sign in to comment.