Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement Google EU Consent #51

Merged
merged 4 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
"scripts": {
"build": "rollup --config rollup.config.js",
"watch": "rollup --config rollup.config.js -w",
"test": "node test/boilerplate/test-karma.js"
"test": "node test/boilerplate/test-karma.js",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is out of scope, but we should add a follow up item to remove the boilerplate tests in favor of the most recent test framework. The boilerplate framework is known to fail silently at times and I would rather we brought Braze in line with the rest of the kits.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add this work item to the rollup.test.config.js since I can't add comments to a json file.

https://go.mparticle.com/work/SQDSDKS-6875

"watch:tests": "ENVIRONMENT=production rollup --config rollup.test.config.js -w",
"test:debug": "npm run build && npm run build:test && DEBUG=true karma start test/karma.config.js"
},
"devDependencies": {
"@rollup/plugin-commonjs": "22.0.1",
Expand Down
22 changes: 22 additions & 0 deletions rollup.test.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// https://go.mparticle.com/work/SQDSDKS-6875
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';

export default [
{
input: 'test/tests.js',
output: {
file: 'test/test-bundle.js',
format: 'iife',
exports: 'named',
name: 'mpBrazeKit',
strict: false,
},
plugins: [
resolve({
browser: true,
}),
commonjs(),
],
},
];
130 changes: 130 additions & 0 deletions src/BrazeKit-dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ var constructor = function () {
forwarderSettings,
options = {},
reportingService,
hasConsentMappings,
parsedConsentMappings,
mpCustomFlags;

self.name = name;
Expand Down Expand Up @@ -72,6 +74,13 @@ var constructor = function () {
var bundleCommerceEventData = false;
var forwardSkuAsProductName = false;

var brazeConsentKeys = [
'$google_ad_user_data',
'$google_ad_personalization'
];

var latestUserBrazeConsentString;

// A purchase event can either log a single event with all products
// or multiple purchase events (one per product)
function logPurchaseEvent(event) {
Expand Down Expand Up @@ -364,6 +373,7 @@ var constructor = function () {
/**************************/
function processEvent(event) {
var reportEvent = false;
maybeSetConsentBeforeEventLogged(event);
rmi22186 marked this conversation as resolved.
Show resolved Hide resolved

if (event.EventDataType == MessageType.Commerce) {
reportEvent = logCommerceEvent(event);
Expand Down Expand Up @@ -709,6 +719,110 @@ var constructor = function () {
}
}

function prepareInitialConsent(user) {
var userConsentState = getUserConsentState(user);

var currentConsentPayload = generateBrazeConsentStatePayload(
userConsentState
);

if (!isEmpty(currentConsentPayload)) {
latestUserBrazeConsentString = JSON.stringify(
currentConsentPayload
);

setConsentOnBraze(currentConsentPayload);
}
}

function setConsentOnBraze(currentConsentPayload) {
for (var key in currentConsentPayload) {
braze
.getUser()
.setCustomUserAttribute(key, currentConsentPayload[key]);
}
}

function maybeSetConsentBeforeEventLogged(event) {
if (latestUserBrazeConsentString && !isEmpty(parsedConsentMappings)) {
var eventConsentState = getEventConsentState(event.ConsentState);

if (!isEmpty(eventConsentState)) {
rmi22186 marked this conversation as resolved.
Show resolved Hide resolved
var eventBrazeConsent = generateBrazeConsentStatePayload(
eventConsentState
);
var eventBrazeConsentAsString = JSON.stringify(
eventBrazeConsent
);

if (
eventBrazeConsentAsString !== latestUserBrazeConsentString
) {
setConsentOnBraze(eventBrazeConsent);
latestUserBrazeConsentString = eventBrazeConsentAsString;
}
}
}
}

function getEventConsentState(eventConsentState) {
return eventConsentState && eventConsentState.getGDPRConsentState
? eventConsentState.getGDPRConsentState()
: {};
}

function generateBrazeConsentStatePayload(consentState) {
if (!parsedConsentMappings) return {};

var payload = {};

// These are Braze's consent constants for Braze's Audience Sync to Google
// https://www.braze.com/docs/partners/canvas_steps/google_audience_sync

var googleToBrazeConsentMap = {
google_ad_user_data: '$google_ad_user_data',
google_ad_personalization: '$google_ad_personalization',
};

for (var i = 0; i <= parsedConsentMappings.length - 1; i++) {
var mappingEntry = parsedConsentMappings[i];
// Although consent purposes can be inputted into the UI in any casing
// the SDK will automatically lowercase them to prevent pseudo-duplicate
// consent purposes, so we call `toLowerCase` on the consentMapping purposes here
var mpMappedConsentName = mappingEntry.map.toLowerCase();
// that mappingEntry.value returned from the server does not have a $ appended, so we have to add it
var brazeMappedConsentName =
googleToBrazeConsentMap[mappingEntry.value];

if (
consentState[mpMappedConsentName] &&
brazeMappedConsentName &&
brazeConsentKeys.indexOf(brazeMappedConsentName) !== -1
) {
payload[brazeMappedConsentName] =
consentState[mpMappedConsentName].Consented;
}
}

return payload;
}

function getUserConsentState(user) {
var userConsentState = {};

var consentState = user.getConsentState();

if (consentState && consentState.getGDPRConsentState) {
userConsentState = consentState.getGDPRConsentState();
}

return userConsentState;
}

function parseConsentSettingsString(consentMappingString) {
return JSON.parse(consentMappingString.replace(/&quot;/g, '"'));
}

function initForwarder(
settings,
service,
Expand Down Expand Up @@ -755,6 +869,15 @@ var constructor = function () {
forwarderSettings.serviceWorkerLocation;
}

if (forwarderSettings.consentMappingSDK) {
parsedConsentMappings = parseConsentSettingsString(
forwarderSettings.consentMappingSDK
);
if (parsedConsentMappings.length) {
hasConsentMappings = true;
}
}

var cluster =
forwarderSettings.cluster ||
forwarderSettings.dataCenterLocation;
Expand Down Expand Up @@ -804,6 +927,9 @@ var constructor = function () {

if (currentUser && mpid) {
onUserIdentified(currentUser);
if (hasConsentMappings) {
prepareInitialConsent(currentUser);
}
rmi22186 marked this conversation as resolved.
Show resolved Hide resolved
}

openSession(forwarderSettings);
Expand Down Expand Up @@ -992,6 +1118,10 @@ function isObject(val) {
);
}

function isEmpty(value) {
return value == null || !(Object.keys(value) || value).length;
}

module.exports = {
register: register,
getVersion: function () {
Expand Down
Loading
Loading