Last active
October 31, 2023 10:05
-
-
Save sperand-io/4725e248a35d5005d68d810d8a8f7b29 to your computer and use it in GitHub Desktop.
Example of using analytics.js conditional loading with TrustArc Consent Manager
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// To get started, make sure you're using the latest version of the analytics.js snippet (4.1.0 or above) | |
// and remove the analytics.load("YOUR_WRITE_KEY"); call (so that you can conditionally manage the loading process). | |
// then "build" this file with something like rollup, parcel, browserify etc targeting whatever browsers you need! | |
import fetch from 'isomorphic-fetch' | |
import {flatten, sortedUniqBy, sortBy} from 'lodash' | |
import inEU from '@segment/in-eu' | |
// CONFIG — EDIT ME! | |
const YOUR_DOMAIN = 'domain.com' | |
const WEBSITE_WRITE_KEY = 'writeKey' | |
const OTHER_WRITE_KEYS = [] // any other ones you want to load integrations from | |
// gets enabled destination configurations from across your source (and optionally other sources in workspace) | |
const destinations = await fetchDestinations([WEBSITE_WRITE_KEY, ...OTHER_WRITE_KEYS]) | |
// KEY LOGIC HERE | |
// eg. could instead use trustArc's other APIs such as getConsentCategories | |
// and map between the returned vendor domains to specific Segment Integrationss, etc | |
// review this logic carefully, and edit per your business requirements | |
// | |
// in this case, we go with all when customer provides full consent | |
// remove Advertising tools when the customer provides functional | |
// and mark Segment as required (for data to flow to warehouse etc— if you make that clear in your policy) | |
// note that though Segment will receive data we wont forward (even server side) to other sources because we | |
// decorate the calls with the full set of integrations you have enabled with "false." | |
// | |
// no preference here goes with all on — you may want to flip that! | |
const { consentDecision } = truste.cma.callApi("getConsentDecision", YOUR_DOMAIN); | |
const destinationPreferences = destinations.map(function (dest) { | |
if (consentDecision === 3) return { [dest.name]: true }; | |
if (consentDecision === 2) return { [dest.name]: dest.categories.includes('Advertising') ? false : true }; | |
if (consentDecision === 1) return { [dest.name]: dest.name === 'Segment.io' ? true : false }; // segment only for require (warehouses etc, note messages will still be de) | |
if (consentDecision === 0) return { [dest.name]: true } // IMPORTANT - this is default value for "no preference set" | |
}) | |
conditionallyLoadAnalytics({ | |
WEBSITE_WRITE_KEY, | |
destinations, | |
destinationPreferences, | |
true // or replace with custom isConsentRequired func, eg. inEU() from above! | |
}) | |
// registers a listener for consent changes, will reload window if they've denied to reset tracking | |
window.top.postMessage(JSON.stringify({ | |
PrivacyManagerAPI: { | |
action: "getConsent", | |
timestamp: new Date().getTime(), | |
self: YOUR_DOMAIN | |
} | |
}), "*"); | |
window.addEventListener("message", function reload(e) { | |
const response = JSON.parse(e.data) | |
if (response.PrivacyManagerAPI.consent === "denied") { | |
return window.location.reload(); | |
} | |
// otherwise approved... carry on! | |
}, false); | |
function conditionallyLoadAnalytics({ | |
writeKey, | |
destinations, | |
destinationPreferences, | |
isConsentRequired, | |
shouldReload = true // change if you dont want to reload on consent changes | |
}) { | |
const integrations = {All: false, 'Segment.io': true} | |
let isAnythingEnabled = false | |
if (!destinationPreferences) { | |
if (isConsentRequired) { | |
return | |
} | |
// Load a.js normally when consent isn't required and there's no preferences | |
if (!window.analytics.initialized) { | |
window.analytics.load(writeKey) | |
} | |
return | |
} | |
for (const destination of destinations) { | |
const isEnabled = Boolean(destinationPreferences[destination.id]) | |
if (isEnabled) { | |
isAnythingEnabled = true | |
} | |
integrations[destination.id] = isEnabled | |
} | |
// Reload the page if the trackers have already been initialised so that | |
// the user's new preferences can take affect | |
if (window.analytics.initialized) { | |
if (shouldReload) { | |
window.location.reload() | |
} | |
return | |
} | |
// Don't load a.js at all if nothing has been enabled | |
if (isAnythingEnabled) { | |
window.analytics.load(writeKey, {integrations}) | |
} | |
} | |
async function fetchDestinationForWriteKey(writeKey) { | |
const res = await fetch( | |
`https://cdn.segment.com/v1/projects/${writeKey}/integrations` | |
) | |
if (!res.ok) { | |
throw new Error( | |
`Failed to fetch integrations for write key ${writeKey}: HTTP ${ | |
res.status | |
} ${res.statusText}` | |
) | |
} | |
const destinations = await res.json() | |
// Rename creationName to id to abstract the weird data model | |
for (const destination of destinations) { | |
destination.id = destination.creationName | |
delete destination.creationName | |
} | |
return destinations | |
} | |
async function fetchDestinations(writeKeys) { | |
const destinationsRequests = [] | |
for (const writeKey of writeKeys) { | |
destinationsRequests.push(fetchDestinationForWriteKey(writeKey)) | |
} | |
let destinations = flatten(await Promise.all(destinationsRequests)) | |
// Remove the dummy Repeater destination | |
destinations = destinations.filter(d => d.id !== 'Repeater') | |
destinations = sortBy(destinations, ['id']) | |
destinations = sortedUniqBy(destinations, 'id') | |
return destinations | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment