-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨(web_analytics) improve web analytics
Differentiate between Google Analytics and Google Tag Manager js code as different Web Analytics providers. Send web analytics event when an user enrolls to a course using the LMS enrollment api. Closes #1565
- Loading branch information
Showing
20 changed files
with
387 additions
and
15 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/** | ||
* Interface for every the Web Analytics API. | ||
* Each web analytics provider should implement this interface | ||
* to adapt the calls from React to the external JS. | ||
*/ | ||
export interface WebAnalyticsAPI { | ||
/** | ||
* Sends the enrolled user to a course event. | ||
* @param resourceLink the course link that the user have been enrolled | ||
*/ | ||
sendEnrolledEvent(resourceLink: string): void; | ||
} | ||
|
||
export enum WebAnalyticsAPIBackend { | ||
GOOGLE_ANALYTICS = 'google_analytics', | ||
GOOGLE_TAG_MANAGER = 'google_tag_manager', | ||
} |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/** | ||
* Declare the Google Analytics ga function. | ||
*/ | ||
import type { Maybe } from 'types/utils'; | ||
|
||
declare global { | ||
const ga: Maybe< | ||
( | ||
tracker: string, | ||
hitType: string, | ||
eventCategory: string, | ||
eventAction: string, | ||
eventLabel: string, | ||
eventValue?: any, | ||
) => void | ||
>; | ||
} |
19 changes: 19 additions & 0 deletions
19
src/frontend/js/types/web-analytics/google_tag_manager.d.ts
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/** | ||
* Declare the Google Tag Manager `window.dataLayer` array. | ||
* Used when the web analytics is configured with `google_tag_manager`. | ||
* | ||
* So we can use `window.dataLayer` has normal JS code. | ||
* | ||
* window.dataLayer.push({ | ||
* event: 'event', | ||
* eventProps: { | ||
* category: category, | ||
* action: action, | ||
* label: label, | ||
* value: value | ||
* } | ||
* }); | ||
*/ | ||
interface Window { | ||
dataLayer?: Record<string, any>[]; | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { WebAnalyticsAPI } from 'types/WebAnalytics'; | ||
|
||
/** | ||
* Base implementation for each web analytics provider. | ||
*/ | ||
export abstract class BaseWebAnalyticsApi implements WebAnalyticsAPI { | ||
/** | ||
* Abstract method to send events by the overridden web analytics API providers. | ||
* @param category the category of the event | ||
* @param action the action that the user has performed or the action of the event. | ||
* @param label additional info about specific elements to identify a product like the course run. | ||
* @param value | ||
*/ | ||
abstract sendEvent(category: string, action: string, label: string, value?: number): void; | ||
|
||
/** | ||
* Sends the enrolled user to a course event. | ||
* @param resourceLink the course link that the user have been enrolled | ||
*/ | ||
sendEnrolledEvent(resourceLink: string): void { | ||
return this.sendEvent('courseEnroll', 'courseEnrollApi', resourceLink); | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
src/frontend/js/utils/api/web-analytics/google_analytics.spec.ts
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* eslint-disable @typescript-eslint/no-unused-vars */ | ||
import { ContextFactory as mockContextFactory } from 'utils/test/factories'; | ||
import WebAnalyticsAPIHandler from '.'; | ||
import GoogleAnalyticsApi from './google_analytics'; | ||
|
||
jest.mock('utils/context', () => ({ | ||
__esModule: true, | ||
default: mockContextFactory({ | ||
web_analytics_provider: 'google_analytics', | ||
}).generate(), | ||
})); | ||
|
||
describe('Web Analytics', () => { | ||
beforeAll(() => { | ||
// Mock the `ga` function so the verification of the presence of the Google Analytics passes. | ||
(global as any).ga = jest.fn(); | ||
}); | ||
|
||
it('returns the Google Analytics API if provider is `google_analytics`', () => { | ||
const api = WebAnalyticsAPIHandler(); | ||
expect(api).toBeDefined(); | ||
expect(api).toBeInstanceOf(GoogleAnalyticsApi); | ||
}); | ||
}); |
26 changes: 26 additions & 0 deletions
26
src/frontend/js/utils/api/web-analytics/google_analytics.ts
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* eslint-disable class-methods-use-this */ | ||
import { BaseWebAnalyticsApi } from './base'; | ||
|
||
/** | ||
* | ||
* Google Analytics Richie Web Analytics API Implementation | ||
* | ||
* This implementation is used when web analytics is configured has `google_analytics`. | ||
* It will send events to the google analytics. | ||
* | ||
*/ | ||
export default class GoogleAnalyticsApi extends BaseWebAnalyticsApi { | ||
ga: Exclude<typeof ga, undefined>; | ||
|
||
constructor() { | ||
super(); | ||
if (ga === undefined) { | ||
throw new Error('Incorrect configuration on Google Analytics on Web Analytics.'); | ||
} | ||
this.ga = ga; | ||
} | ||
|
||
sendEvent(category: string, action: string, label: string, value?: number): void { | ||
this.ga('send', 'event', category, action, label, value); | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
src/frontend/js/utils/api/web-analytics/google_tag_manager.spec.ts
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { ContextFactory as mockContextFactory } from 'utils/test/factories'; | ||
import WebAnalyticsAPIHandler from '.'; | ||
import GoogleTagManagerApi from './google_tag_manager'; | ||
|
||
jest.mock('utils/context', () => ({ | ||
__esModule: true, | ||
default: mockContextFactory({ | ||
web_analytics_provider: 'google_tag_manager', | ||
}).generate(), | ||
})); | ||
|
||
describe('Web Analytics', () => { | ||
beforeAll(() => { | ||
// Mock the `windows.dataLayer` so the verification of the presence of the Google Tag Manager passes. | ||
window.dataLayer = []; | ||
}); | ||
|
||
it('returns the Google Tag Manager API if provider is `google_tag_manager`', () => { | ||
const api = WebAnalyticsAPIHandler(); | ||
expect(api).toBeDefined(); | ||
expect(api).toBeInstanceOf(GoogleTagManagerApi); | ||
}); | ||
}); |
33 changes: 33 additions & 0 deletions
33
src/frontend/js/utils/api/web-analytics/google_tag_manager.ts
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* eslint-disable class-methods-use-this */ | ||
import { BaseWebAnalyticsApi } from './base'; | ||
|
||
/** | ||
* | ||
* Google Tag Manager Richie Web Analytics API Implementation | ||
* | ||
* This implementation is used when web analytics is configured has `google_tag_manager`. | ||
* It will send events to the google tag manager. | ||
* | ||
*/ | ||
export default class GoogleTagManagerApi extends BaseWebAnalyticsApi { | ||
dataLayer: Exclude<typeof window.dataLayer, undefined>; | ||
|
||
constructor() { | ||
super(); | ||
if (window.dataLayer === undefined) { | ||
throw new Error('Incorrect configuration on Google Tag Manager on Web Analytics.'); | ||
} | ||
this.dataLayer = window.dataLayer; | ||
} | ||
sendEvent(category: string, action: string, label: string, value?: number): void { | ||
this.dataLayer.push({ | ||
event: 'courseEnroll', | ||
eventProps: { | ||
category, | ||
action, | ||
label, | ||
value, | ||
}, | ||
}); | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { Maybe } from 'types/utils'; | ||
import { WebAnalyticsAPI, WebAnalyticsAPIBackend } from 'types/WebAnalytics'; | ||
import context from 'utils/context'; | ||
import { handle } from 'utils/errors/handle'; | ||
import GoogleAnalyticsApi from './google_analytics'; | ||
import GoogleTagManagerApi from './google_tag_manager'; | ||
|
||
const WEB_ANALYTICS_PROVIDER = context?.web_analytics_provider; | ||
|
||
const WebAnalyticsAPIHandler = (): Maybe<WebAnalyticsAPI> => { | ||
try { | ||
switch (WEB_ANALYTICS_PROVIDER) { | ||
case WebAnalyticsAPIBackend.GOOGLE_ANALYTICS: | ||
return new GoogleAnalyticsApi(); | ||
case WebAnalyticsAPIBackend.GOOGLE_TAG_MANAGER: | ||
return new GoogleTagManagerApi(); | ||
} | ||
} catch (error) { | ||
handle(error); | ||
} | ||
return undefined; | ||
}; | ||
|
||
export default WebAnalyticsAPIHandler; |
13 changes: 13 additions & 0 deletions
13
src/frontend/js/utils/api/web-analytics/no_provider.spec.ts
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { ContextFactory as mockContextFactory } from 'utils/test/factories'; | ||
import WebAnalyticsAPIHandler from '.'; | ||
|
||
jest.mock('utils/context', () => ({ | ||
__esModule: true, | ||
default: mockContextFactory().generate(), | ||
})); | ||
describe('Web Analytics', () => { | ||
it('returns a concrete implementation when the web analytics module is activated', () => { | ||
const api = WebAnalyticsAPIHandler(); | ||
expect(api).toBeUndefined(); | ||
}); | ||
}); |
15 changes: 15 additions & 0 deletions
15
src/frontend/js/utils/api/web-analytics/unknown_provider.spec.ts
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { ContextFactory as mockContextFactory } from 'utils/test/factories'; | ||
import WebAnalyticsAPIHandler from '.'; | ||
|
||
jest.mock('utils/context', () => ({ | ||
__esModule: true, | ||
default: mockContextFactory({ | ||
web_analytics_provider: 'unknown_provider', | ||
}).generate(), | ||
})); | ||
describe('Web Analytics', () => { | ||
it('returns undefined when an unknown provider for the frontend code is activated', () => { | ||
const api = WebAnalyticsAPIHandler(); | ||
expect(api).toBeUndefined(); | ||
}); | ||
}); |
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Oops, something went wrong.