Skip to content

Commit

Permalink
feat: Add support for Consent State
Browse files Browse the repository at this point in the history
  • Loading branch information
alexs-mparticle committed Feb 22, 2024
1 parent c823e6f commit b90025b
Show file tree
Hide file tree
Showing 5 changed files with 719 additions and 1 deletion.
14 changes: 13 additions & 1 deletion packages/GA4Client/src/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// in place when sending to data layer.
// https://support.google.com/analytics/answer/11202874?sjid=7958830619827381593-NA

var ConsentHandler = require('./consent');

var EVENT_NAME_MAX_LENGTH = 40;
var EVENT_ATTRIBUTE_KEY_MAX_LENGTH = 40;
var EVENT_ATTRIBUTE_VAL_MAX_LENGTH = 100;
Expand Down Expand Up @@ -33,7 +35,13 @@ function isEmpty(value) {
return value == null || !(Object.keys(value) || value).length;
}

function Common() {}
function Common() {
this.consentMappings = {};
this.consentPayloadDefaults = {};
this.consentPayloadAsString = '';

this.consentHandler = new ConsentHandler(this);
}

Common.prototype.forwarderSettings = null;

Expand Down Expand Up @@ -305,4 +313,8 @@ Common.prototype.getUserId = function (
}
};

Common.prototype.cloneObject = function (obj) {
return JSON.parse(JSON.stringify(obj));
};

module.exports = Common;
107 changes: 107 additions & 0 deletions packages/GA4Client/src/consent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
var googleConsentValues = {
// Server Integration uses 'Unspecified' as a value when the setting is 'not set'.
// However, this is not used by Google's Web SDK. We are referencing it here as a comment
// as a record of this distinction and for posterity.
// If Google ever adds this for web, the line can just be uncommented to support this.
//
// Docs:
// Web: https://developers.google.com/tag-platform/gtagjs/reference#consent
// S2S: https://developers.google.com/google-ads/api/reference/rpc/v15/ConsentStatusEnum.ConsentStatus
//
// Unspecified: 'unspecified',
Denied: 'denied',
Granted: 'granted',
};

var googleConsentProperties = [
'ad_storage',
'ad_user_data',
'ad_personalization',
'analytics_storage',
];

function ConsentHandler(common) {
this.common = common || {};
}

ConsentHandler.prototype.getUserConsentState = function () {
var userConsentState = null;

if (mParticle.Identity && mParticle.Identity.getCurrentUser) {
var currentUser = mParticle.Identity.getCurrentUser();

if (!currentUser) {
return null;
}

var consentState =
mParticle.Identity.getCurrentUser().getConsentState();

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

return userConsentState;
};

ConsentHandler.prototype.getEventConsentState = function (eventConsentState) {
return eventConsentState && eventConsentState.getGDPRConsentState
? eventConsentState.getGDPRConsentState()
: null;
};

ConsentHandler.prototype.getConsentSettings = function () {
var consentSettings = {};

var googleToMpConsentSettingsMapping = {
ad_storage: 'adStorageConsentWeb',
ad_user_data: 'adUserDataConsentWeb',
ad_personalization: 'adPersonalizationConsentWeb',
analytics_storage: 'analyticsStorageConsentWeb',
};

var forwarderSettings = this.common.forwarderSettings;

Object.keys(googleToMpConsentSettingsMapping).forEach(function (
googleConsentKey
) {
var mpConsentSettingKey =
googleToMpConsentSettingsMapping[googleConsentKey];
var googleConsentValuesKey = forwarderSettings[mpConsentSettingKey];

if (googleConsentValuesKey && mpConsentSettingKey) {
consentSettings[googleConsentKey] =
googleConsentValues[googleConsentValuesKey];
}
});

return consentSettings;
};

ConsentHandler.prototype.generateConsentStatePayloadFromMappings = function (
consentState,
mappings
) {
var payload = this.common.cloneObject(this.common.consentPayloadDefaults);

for (var i = 0; i <= mappings.length - 1; i++) {
var mappingEntry = mappings[i];
var mpMappedConsentName = mappingEntry.map;
var googleMappedConsentName = mappingEntry.value;

if (
consentState[mpMappedConsentName] &&
googleConsentProperties.indexOf(googleMappedConsentName) !== -1
) {
payload[googleMappedConsentName] = consentState[mpMappedConsentName]
.Consented
? googleConsentValues.Granted
: googleConsentValues.Denied;
}
}

return payload;
};

module.exports = ConsentHandler;
23 changes: 23 additions & 0 deletions packages/GA4Client/src/event-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@ function EventHandler(common) {
this.common = common || {};
}

EventHandler.prototype.maybeSendConsentUpdateToGa4 = function (event) {
var eventConsentState = this.common.consentHandler.getEventConsentState(
event.ConsentState
);

if (eventConsentState) {
var updatedConsentPayload =
this.common.consentHandler.generateConsentStatePayloadFromMappings(
eventConsentState,
this.common.consentMappings
);

var eventConsentAsString = JSON.stringify(updatedConsentPayload);

if (eventConsentAsString !== this.common.consentPayloadAsString) {
gtag('consent', 'update', updatedConsentPayload);
this.common.consentPayloadAsString = eventConsentAsString;
}
}
};

// TODO: https://mparticle-eng.atlassian.net/browse/SQDSDKS-5715
EventHandler.prototype.sendEventToGA4 = function (eventName, eventAttributes) {
var standardizedEventName;
Expand All @@ -27,6 +48,7 @@ EventHandler.prototype.sendEventToGA4 = function (eventName, eventAttributes) {
};

EventHandler.prototype.logEvent = function (event) {
this.maybeSendConsentUpdateToGa4(event);
this.sendEventToGA4(event.EventName, event.EventAttributes);
};

Expand Down Expand Up @@ -67,6 +89,7 @@ EventHandler.prototype.logPageView = function (event) {
event.EventAttributes
);

this.maybeSendConsentUpdateToGa4(event);
this.sendEventToGA4('page_view', eventAttributes);

return true;
Expand Down
29 changes: 29 additions & 0 deletions packages/GA4Client/src/initialization.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ var initialization = {
var configSettings = {
send_page_view: forwarderSettings.enablePageView === 'True',
};

if (forwarderSettings.consentMappingWeb) {
common.consentMappings = parseSettingsString(
forwarderSettings.consentMappingWeb
);
}

window.dataLayer = window.dataLayer || [];

window.gtag = function () {
Expand Down Expand Up @@ -89,6 +96,24 @@ var initialization = {
} else {
isInitialized = true;
}

common.consentPayloadDefaults =
common.consentHandler.getConsentSettings();
var initialConsentState = common.consentHandler.getUserConsentState();

if (common.consentPayloadDefaults && initialConsentState) {
var defaultConsentPayload =
common.consentHandler.generateConsentStatePayloadFromMappings(
initialConsentState,
common.consentMappings
);
common.consentPayloadAsString = JSON.stringify(
defaultConsentPayload
);

gtag('consent', 'default', defaultConsentPayload);
}

return isInitialized;
},
};
Expand All @@ -104,4 +129,8 @@ function setClientId(clientId, moduleId) {
window.mParticle._setIntegrationDelay(moduleId, false);
}

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

module.exports = initialization;
Loading

0 comments on commit b90025b

Please sign in to comment.