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

fix: include measurement Id as event param #57

Merged
Show file tree
Hide file tree
Changes from 3 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
258 changes: 142 additions & 116 deletions packages/GA4Client/src/commerce-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,18 @@ CommerceHandler.prototype.logCommerceEvent = function (event) {
event.CustomFlags[GA4_COMMERCE_EVENT_TYPE] === VIEW_CART
) {
isViewCartEvent = true;
return logViewCart(event, affiliation);
return this.logViewCart(event, affiliation);
}
}
// Handle Impressions
if (event.EventCategory === ProductActionTypes.Impression) {
return logImpressionEvent(event, affiliation);
return this.logImpressionEvent(event, affiliation);
// Handle Promotions
} else if (
event.EventCategory === PromotionActionTypes.PromotionClick ||
event.EventCategory === PromotionActionTypes.PromotionView
) {
return logPromotionEvent(event);
return this.logPromotionEvent(event);
}

switch (event.EventCategory) {
Expand Down Expand Up @@ -99,7 +99,7 @@ CommerceHandler.prototype.logCommerceEvent = function (event) {
break;

case ProductActionTypes.CheckoutOption:
return logCheckoutOptionEvent(event, affiliation);
return this.logCheckoutOptionEvent(event, affiliation);

default:
// a view cart event is handled at the beginning of this function
Expand Down Expand Up @@ -136,10 +136,147 @@ CommerceHandler.prototype.logCommerceEvent = function (event) {
null;
}

gtag('event', mapGA4EcommerceEventName(event), ga4CommerceEventParameters);
this.sendCommerceEventToGA4(
mapGA4EcommerceEventName(event),
ga4CommerceEventParameters
);
return true;
Copy link
Contributor

Choose a reason for hiding this comment

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

I took a look at mapGA4EcommerceEventName and it looks like it's possible that null may be returned in place of an eventName. While it may be an edge case, it's something we should consider handling.

Maybe we can event validate that the response is actually a non-empty string as well.

Probably out of scope for your change but I think it's a good defensive practice so we don't accidentally send an undefined event to gtag.

Suggested change
this.sendCommerceEventToGA4(
mapGA4EcommerceEventName(event),
ga4CommerceEventParameters
);
return true;
var eventName = mapGA4EcommerceEventName(event);
if (typeof eventName === 'string' && eventName.length > 0) {
this.sendCommerceEventToGA4(
mapGA4EcommerceEventName(event),
ga4CommerceEventParameters
);
} else {
return false;
}
return true;

};

CommerceHandler.prototype.sendCommerceEventToGA4 = function (
eventName,
eventAttributes
) {
if (this.common.forwarderSettings.measurementId) {
eventAttributes.send_to = this.common.forwarderSettings.measurementId;
}

gtag('event', eventName, eventAttributes);
mmustafa-tse marked this conversation as resolved.
Show resolved Hide resolved
};

// Google previously had a CheckoutOption event, and now this has been split into 2 GA4 events - add_shipping_info and add_payment_info
// Since MP still uses CheckoutOption, we must map this to the 2 events using custom flags. To prevent any data loss from customers
// migrating from UA to GA4, we will set a default `set_checkout_option` which matches Firebase's data model.
CommerceHandler.prototype.logCheckoutOptionEvent = function (
event,
affiliation
) {
try {
var customFlags = event.CustomFlags,
GA4CommerceEventType = customFlags[GA4_COMMERCE_EVENT_TYPE],
ga4CommerceEventParameters;
Comment on lines +166 to +168
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should start splitting up our variable declarations when we migrate code.

Suggested change
var customFlags = event.CustomFlags,
GA4CommerceEventType = customFlags[GA4_COMMERCE_EVENT_TYPE],
ga4CommerceEventParameters;
var customFlags = event.CustomFlags;
var GA4CommerceEventType = customFlags[GA4_COMMERCE_EVENT_TYPE];
var ga4CommerceEventParameters;


// if no custom flags exist or there is no GA4CommerceEventType, the user has not updated their code from using legacy GA which still supports checkout_option
if (!customFlags || !GA4CommerceEventType) {
console.error(
'Your checkout option event for the Google Analytics 4 integration is missing a custom flag for "GA4.CommerceEventType". The event was sent using the deprecated set_checkout_option event. Review mParticle docs for GA4 for the custom flags to add.'
);
}

switch (GA4CommerceEventType) {
case ADD_SHIPPING_INFO:
ga4CommerceEventParameters = buildAddShippingInfo(
event,
affiliation
);
break;
case ADD_PAYMENT_INFO:
ga4CommerceEventParameters = buildAddPaymentInfo(
event,
affiliation
);
break;
default:
ga4CommerceEventParameters = buildCheckoutOptions(
event,
affiliation
);
break;
}
} catch (error) {
console.error(
'There is an issue with the custom flags in your CheckoutOption event. The event was not sent. Plrease review the docs and fix.',
error
);
return false;
}

this.sendCommerceEventToGA4(
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment here about checking if eventName is a non-empty string.

mapGA4EcommerceEventName(event),
ga4CommerceEventParameters
);

return true;
};

CommerceHandler.prototype.logPromotionEvent = function (event) {
var self = this;
try {
var ga4CommerceEventParameters;
event.PromotionAction.PromotionList.forEach(function (promotion) {
ga4CommerceEventParameters = buildPromotion(promotion);

self.sendCommerceEventToGA4(
mapGA4EcommerceEventName(event),
ga4CommerceEventParameters
);
return true;
mmustafa-tse marked this conversation as resolved.
Show resolved Hide resolved
});
} catch (error) {
console.error(
'Error logging Promotions to GA4. Promotions not logged.',
error
);
return false;
}
};

CommerceHandler.prototype.logImpressionEvent = function (event, affiliation) {
var self = this;
try {
var ga4CommerceEventParameters;
event.ProductImpressions.forEach(function (impression) {
ga4CommerceEventParameters = parseImpression(
impression,
affiliation
);


self.sendCommerceEventToGA4(
mapGA4EcommerceEventName(event),
ga4CommerceEventParameters
);

return true;
mmustafa-tse marked this conversation as resolved.
Show resolved Hide resolved
});
} catch (error) {
console.log(
'Error logging Impressions to GA4. Impressions not logged',
error
);
return false;
}
};

CommerceHandler.prototype.logViewCart = function (event, affiliation) {
var ga4CommerceEventParameters = buildViewCart(event, affiliation);
ga4CommerceEventParameters = this.common.mergeObjects(
ga4CommerceEventParameters,
this.common.limitEventAttributes(event.EventAttributes)
);
ga4CommerceEventParameters.currency = event.CurrencyCode;

ga4CommerceEventParameters.value =
(event.CustomFlags && event.CustomFlags['GA4.Value']) ||
event.ProductAction.TotalAmount ||
null;

this.sendCommerceEventToGA4(
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment about empty strings

mapGA4EcommerceEventName(event),
ga4CommerceEventParameters
);
};

function buildAddOrRemoveCartItem(event, affiliation) {
return {
items: buildProductsList(event.ProductAction.ProductList, affiliation),
Expand Down Expand Up @@ -394,117 +531,6 @@ function getCheckoutOptionEventName(customFlags) {
}
}

// Google previously had a CheckoutOption event, and now this has been split into 2 GA4 events - add_shipping_info and add_payment_info
// Since MP still uses CheckoutOption, we must map this to the 2 events using custom flags. To prevent any data loss from customers
// migrating from UA to GA4, we will set a default `set_checkout_option` which matches Firebase's data model.
function logCheckoutOptionEvent(event, affiliation) {
try {
var customFlags = event.CustomFlags,
GA4CommerceEventType = customFlags[GA4_COMMERCE_EVENT_TYPE],
ga4CommerceEventParameters;

// if no custom flags exist or there is no GA4CommerceEventType, the user has not updated their code from using legacy GA which still supports checkout_option
if (!customFlags || !GA4CommerceEventType) {
console.error(
'Your checkout option event for the Google Analytics 4 integration is missing a custom flag for "GA4.CommerceEventType". The event was sent using the deprecated set_checkout_option event. Review mParticle docs for GA4 for the custom flags to add.'
);
}

switch (GA4CommerceEventType) {
case ADD_SHIPPING_INFO:
ga4CommerceEventParameters = buildAddShippingInfo(
event,
affiliation
);
break;
case ADD_PAYMENT_INFO:
ga4CommerceEventParameters = buildAddPaymentInfo(
event,
affiliation
);
break;
default:
ga4CommerceEventParameters = buildCheckoutOptions(
event,
affiliation
);
break;
}
} catch (error) {
console.error(
'There is an issue with the custom flags in your CheckoutOption event. The event was not sent. Plrease review the docs and fix.',
error
);
return false;
}

gtag('event', mapGA4EcommerceEventName(event), ga4CommerceEventParameters);

return true;
}

function logPromotionEvent(event) {
try {
var ga4CommerceEventParameters;
event.PromotionAction.PromotionList.forEach(function (promotion) {
ga4CommerceEventParameters = buildPromotion(promotion);
gtag(
'event',
mapGA4EcommerceEventName(event),
ga4CommerceEventParameters
);
});
return true;
} catch (error) {
console.error(
'Error logging Promotions to GA4. Promotions not logged.',
error
);
}
return false;
}

function logImpressionEvent(event, affiliation) {
try {
var ga4CommerceEventParameters;
event.ProductImpressions.forEach(function (impression) {
ga4CommerceEventParameters = parseImpression(
impression,
affiliation
);

gtag(
'event',
mapGA4EcommerceEventName(event),
ga4CommerceEventParameters
);
});
} catch (error) {
console.log(
'Error logging Impressions to GA4. Impressions not logged',
error
);
return false;
}
return true;
}

function logViewCart(event, affiliation) {
var ga4CommerceEventParameters = buildViewCart(event, affiliation);
ga4CommerceEventParameters = self.common.mergeObjects(
ga4CommerceEventParameters,
self.common.limitEventAttributes(event.EventAttributes)
);
ga4CommerceEventParameters.currency = event.CurrencyCode;

ga4CommerceEventParameters.value =
(event.CustomFlags && event.CustomFlags['GA4.Value']) ||
event.ProductAction.TotalAmount ||
null;
gtag('event', mapGA4EcommerceEventName(event), ga4CommerceEventParameters);
return true;
}

function buildViewCart(event, affiliation) {
return {
items: buildProductsList(event.ProductAction.ProductList, affiliation),
Expand Down
3 changes: 2 additions & 1 deletion packages/GA4Client/src/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ var ConsentHandler = require('./consent');
var EVENT_NAME_MAX_LENGTH = 40;
var EVENT_ATTRIBUTE_KEY_MAX_LENGTH = 40;
var EVENT_ATTRIBUTE_VAL_MAX_LENGTH = 100;
var EVENT_ATTRIBUTE_MAX_NUMBER = 100;
// maximum event attributes reduced to 99 instead of 100 since now we include send_to as GA4 event parameter
var EVENT_ATTRIBUTE_MAX_NUMBER = 99;
mmustafa-tse marked this conversation as resolved.
Show resolved Hide resolved

var USER_ATTRIBUTE_KEY_MAX_LENGTH = 24;
var USER_ATTRIBUTE_VALUE_MAX_LENGTH = 36;
Expand Down
5 changes: 5 additions & 0 deletions packages/GA4Client/src/event-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ EventHandler.prototype.sendEventToGA4 = function (eventName, eventAttributes) {
standardizedAttributes
);

if (this.common.forwarderSettings.measurementId) {
standardizedAttributes.send_to =
this.common.forwarderSettings.measurementId;
}

gtag(
'event',
this.common.truncateEventName(standardizedEventName),
Expand Down
Loading
Loading