Commit c5bb1aa1 authored by Manoj Pandian Sakthivel's avatar Manoj Pandian Sakthivel
Browse files

Migrate GA to GA4

parent e9cd3cfa
Pipeline #294937 passed with stages
in 5 minutes and 8 seconds
......@@ -67,14 +67,15 @@ const unsupportedBrowserRouter = (_: Request, res: Response) => {
const getAnalyticsScript = () =>
process.env.REPORT_ANALYTICS?.toLowerCase() === 'true' &&
process.env.GOOGLE_ANALYTICS_KEY
? `<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', '${process.env.GOOGLE_ANALYTICS_KEY}', 'auto');
ga('send', 'pageview');
</script>`
? `<script async src="https://www.googletagmanager.com/gtag/js?id=${process.env.GOOGLE_ANALYTICS_KEY}"></>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${process.env.GOOGLE_ANALYTICS_KEY}');
</script>
`
: '';
export default unsupportedBrowserRouter;
......@@ -39,50 +39,61 @@ class AnalyticsTracking {
private setReporting() {
// don't send analytics other than in production deployment
// https://developers.google.com/analytics/devguides/collection/gtagjs/user-opt-out
if (!config.shouldReportAnalytics) {
this.googleAnalytics.ga('set', 'sendHitTask', null);
(window as any)[`ga-disable-${googleAnalyticsKey}`] = true;
}
}
// Track a pageview
public trackPageView(pagePath: string) {
this.googleAnalytics.pageview(pagePath);
this.googleAnalytics.pageview({
page_path: pagePath
});
}
// Track an event
public trackEvent(ga: AnalyticsOptions) {
ga.species && this.setSpeciesDimension(ga.species);
ga.app && this.setAppDimension(ga.app);
ga.feature && this.setFeatureDimension(ga.feature);
public trackEvent(gtag: AnalyticsOptions) {
gtag.species && this.setSpeciesDimension(gtag.species);
gtag.app && this.setAppDimension(gtag.app);
gtag.feature && this.setFeatureDimension(gtag.feature);
this.googleAnalytics.event({
eventAction: ga.action,
eventCategory: ga.category,
eventLabel: ga.label,
eventValue: ga.value
event_action: gtag.action,
event_category: gtag.category,
event_label: gtag.label,
event_value: gtag.value
});
ga.species && this.setSpeciesDimension(null);
ga.feature && this.setFeatureDimension(null);
gtag.species && this.setSpeciesDimension(null);
gtag.feature && this.setFeatureDimension(null);
}
// Set app custom dimension
public setAppDimension(app: string | null) {
this.googleAnalytics.ga('set', CustomDimensions.APP, app);
this.googleAnalytics.setDimension({
event_name: 'set_app_dimension',
dimension_key: CustomDimensions.APP,
dimension_value: app
});
}
// Set species custom dimension
public setSpeciesDimension(speciesAnalyticsName: string | null) {
this.googleAnalytics.ga(
'set',
CustomDimensions.SPECIES,
speciesAnalyticsName
);
this.googleAnalytics.setDimension({
event_name: 'set_species_dimension',
dimension_key: CustomDimensions.SPECIES,
dimension_value: speciesAnalyticsName
});
}
// Set feature custom dimension
public setFeatureDimension(featureType: string | null) {
this.googleAnalytics.ga('set', CustomDimensions.FEATURE, featureType);
this.googleAnalytics.setDimension({
event_name: 'set_feature_dimension',
dimension_key: CustomDimensions.FEATURE,
dimension_value: featureType
});
}
}
......
......@@ -16,46 +16,79 @@
import loadGoogleAnalytics from './loadGoogleAnalytics';
type TrackPageView = {
page_title?: string;
page_location?: string;
page_path: string;
};
type TrackEvent = {
event_action: string;
event_category: string;
event_label?: string;
event_value?: number;
};
type SetDimension = {
event_name: string;
dimension_key: string;
dimension_value: string | null;
};
/**
* The interface of the GoogleAnalytics class below
* replicates the interface of the ReactGA library that we were using before
*/
class GoogleAnalytics {
static googleAnalyticsKey: string;
static initialize(googleAnalyticsKey: string) {
loadGoogleAnalytics(googleAnalyticsKey);
this.googleAnalyticsKey = googleAnalyticsKey;
}
static ga(...args: any[]) {
window.ga(...args);
static gtag(...args: any[]) {
window.gtag(...args);
}
// referencee: example from Google's docs for analytics.js
// https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#hitType
static pageview(pagePath: string) {
window.ga('send', {
hitType: 'pageview',
page: pagePath
// referencee: example from Google's docs for gtag.js
// https://developers.google.com/analytics/devguides/collection/gtagjs/pages
static pageview(params: TrackPageView) {
window.gtag('event', 'page_view', {
send_to: this.googleAnalyticsKey,
...params
});
}
// referencee: Google's docs for analytics.js
// https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#eventCategory
static event(params: {
eventAction: string;
eventCategory: string;
eventLabel?: string;
eventValue?: number;
}) {
// https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#event_category
static event(params: TrackEvent) {
// clean up the params object before passing it to ga function
// by removing from it any empty fields
const options = Object.entries(params).reduce(
(obj, [key, value]): Partial<typeof params> => {
return value !== undefined ? { ...obj, [key]: value } : obj;
},
{}
);
window.ga('send', 'event', options);
const options: {
event_category: string;
event_label?: string;
value?: string;
} = {
event_category: params.event_category
};
if (params.event_label) {
options['event_label'] = params.event_label;
}
if (params.event_value) {
options['value'] = params.event_label;
}
window.gtag('event', params.event_action, options);
}
static setDimension(params: SetDimension) {
window.gtag('event', params.event_name, {
[params.dimension_key]: params.dimension_value
});
}
}
......
......@@ -27,45 +27,46 @@ import once from 'lodash/once';
*
*/
interface GA {
interface GTAG {
(...args: any[]): void; // executes analytics commands
q: any[]; // a container for queueing up analytics commands
l: number; // is used for timestamps
}
// extend the window interface with the google analytics object
declare global {
interface Window {
ga: GA;
gtag: GTAG;
dataLayer: any[];
}
}
const loadGoogleAnalytics = (trackerId: string) => {
createGAShim(trackerId);
loadScript();
loadScript(trackerId);
};
// create a simplified google analytics object and assign it to window
// to keep a queue of any pending analytics commands
// until the real google analytics object is ready to replace it
const createGAShim = (trackerId: string) => {
const ga = (...args: any[]) => {
const gtag = (...args: any[]) => {
// the sole purpose of the shim is to enqueue commands that it receives
// before the real Google Analytics script loads
window.ga.q.push(args);
window.dataLayer.push(args);
};
ga.q = [] as any[]; // initialise the command queue
ga.l = new Date().getTime(); // Google uses this for timing hits
window.ga = ga;
window.dataLayer = window.dataLayer || []; // initialise the command queue
window.gtag = gtag;
window.gtag('js', new Date()); // Google uses this for timing hits
window.ga('create', trackerId, 'auto'); // immediately enqueue a command for creating a tracker
// The statement below automatically sends Pageview
// TODO: Check if we need to disable it by passing: `{send_page_view: false}` as the third arg
window.gtag('config', trackerId);
};
const loadScript = () => {
const loadScript = (trackerId: string) => {
const scriptElement = document.createElement('script');
scriptElement.async = true;
scriptElement.src = 'https://www.google-analytics.com/analytics.js';
scriptElement.src = `https://www.googletagmanager.com/gtag/js?id=${trackerId}`;
document.body.appendChild(scriptElement);
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment