-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
EventsSDK: Support Google Tag Manager (#118)
In order to support Google Tag Manager, we will export a new function just specifically for sending Events API requests from it. This also adds a convertTypesGTM function which is used to convert booleans and numbers represented as strings to their correct types. This is unit tested in convertTypes.test.ts. J=https://yexttest.atlassian.net/browse/FUS-6055 TEST=auto, unit --------- Co-authored-by: Ethan Jaffee <[email protected]> Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
- Loading branch information
1 parent
f44c8e4
commit fd43132
Showing
8 changed files
with
321 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<!-- Do not edit this file. It is automatically generated by API Documenter. --> | ||
|
||
[Home](./index.md) > [@yext/analytics](./analytics.md) > [reportBrowserAnalytics](./analytics.reportbrowseranalytics.md) | ||
|
||
## reportBrowserAnalytics() function | ||
|
||
An alternative entry point for the Yext Analytics Events SDK, currently used by Google Tag Manager. This method reads the config and payload from the variable analyticsEventPayload stored in the global window object. The config and payload are then passed to the report function to be sent to the Yext Analytics Events API. | ||
|
||
<b>Signature:</b> | ||
|
||
```typescript | ||
export declare function reportBrowserAnalytics(): Promise<string>; | ||
``` | ||
<b>Returns:</b> | ||
|
||
Promise<string> | ||
|
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,70 @@ | ||
import { EventPayload } from './EventPayload'; | ||
|
||
// Define the list of possibly numerical properties to check and convert | ||
const propertiesToCheck = [ | ||
'sessionTrackingEnabled', | ||
'versionNumber', | ||
'siteUid', | ||
'count', | ||
'entity', | ||
'amount', | ||
'latitude', | ||
'longitude', | ||
'timestamp', | ||
'bot', | ||
'internalUser', | ||
'isDirectAnswer' | ||
]; | ||
|
||
export function convertStringToValue( | ||
data: Record<string, unknown> | ||
): EventPayload { | ||
// Recursive function to traverse and convert nested objects | ||
function recursiveConversion(obj: Record<string, unknown>) { | ||
for (const property in obj) { | ||
if (propertiesToCheck.includes(property)) { | ||
// Check if the property is in the list of properties to convert, | ||
// and if it's a string that represents a number, convert it to a number. | ||
// If it's a boolean string, convert it to a boolean. | ||
if (!isNaN(Number(obj[property]))) { | ||
obj[property] = Number(obj[property]); | ||
} else if (obj[property] === 'true' || obj[property] === 'false') { | ||
obj[property] = obj[property] === 'true'; | ||
} | ||
} | ||
// If the property is an object, recursively call the function on it | ||
else if (typeof obj[property] === 'object') { | ||
recursiveConversion(obj[property] as Record<string, unknown>); | ||
} | ||
} | ||
} | ||
|
||
// Copy the input data to avoid modifying the original | ||
const result: Record<string, unknown> = { ...data }; | ||
// Start the recursive conversion | ||
recursiveConversion(result); | ||
|
||
// Handle customValues separately as it's nested properties can have a user defined key name | ||
if (result.customValues && typeof result.customValues === 'object') { | ||
convertCustomValues(result.customValues as Record<string, unknown>); | ||
} | ||
|
||
// Return the modified result | ||
return { | ||
...result, | ||
action: result.action as EventPayload['action'] | ||
}; | ||
} | ||
|
||
// Function to convert custom values within customValues object | ||
function convertCustomValues(obj: Record<string, unknown>) { | ||
for (const key in obj) { | ||
if ( | ||
obj.hasOwnProperty(key) && | ||
typeof obj[key] === 'string' && | ||
!isNaN(Number(obj[key])) | ||
) { | ||
obj[key] = Number(obj[key]); | ||
} | ||
} | ||
} |
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,36 @@ | ||
<!doctype html> | ||
<html lang="en"> | ||
<head> | ||
<!-- Google Tag Manager --> | ||
<script> | ||
(function (w, d, s, l, i) { | ||
w[l] = w[l] || []; | ||
w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' }); | ||
var f = d.getElementsByTagName(s)[0], | ||
j = d.createElement(s), | ||
dl = l != 'dataLayer' ? '&l=' + l : ''; | ||
j.async = true; | ||
j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl; | ||
f.parentNode.insertBefore(j, f); | ||
})(window, document, 'script', 'dataLayer', 'GTM-5XJN5FXJ'); | ||
</script> | ||
<!-- End Google Tag Manager --> | ||
<meta charset="UTF-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Analytics Test</title> | ||
</head> | ||
<body> | ||
<!-- Google Tag Manager (noscript) --> | ||
<noscript | ||
><iframe | ||
src="https://www.googletagmanager.com/ns.html?id=GTM-5XJN5FXJ" | ||
height="0" | ||
width="0" | ||
style="display: none; visibility: hidden" | ||
></iframe | ||
></noscript> | ||
<!-- End Google Tag Manager (noscript) --> | ||
<h1>Analytics Test</h1> | ||
<button type="button">Test</button> | ||
</body> | ||
</html> |
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,115 @@ | ||
import { convertStringToValue } from '../src/convertStringToValue'; | ||
|
||
describe('convertTypesGTM Test', () => { | ||
it('should convert string represented numbers to numerical type - standard case', () => { | ||
const gtmPayload = { | ||
action: 'ADD_TO_CART', | ||
count: '5', | ||
search: { | ||
versionNumber: '5' | ||
}, | ||
sites: { | ||
siteUid: '5' | ||
} | ||
}; | ||
|
||
const result = convertStringToValue(gtmPayload); | ||
|
||
expect(result).toEqual({ | ||
action: 'ADD_TO_CART', | ||
count: 5, | ||
search: { | ||
versionNumber: 5 | ||
}, | ||
sites: { | ||
siteUid: 5 | ||
} | ||
}); | ||
}); | ||
|
||
it('should convert string represented numbers to numerical type - entity is Number', () => { | ||
const gtmPayload = { | ||
action: 'ADD_TO_CART', | ||
entity: '1234' | ||
}; | ||
|
||
const result = convertStringToValue(gtmPayload); | ||
|
||
expect(result).toEqual({ | ||
action: 'ADD_TO_CART', | ||
entity: 1234 | ||
}); | ||
}); | ||
|
||
it('should convert string represented numbers to numerical type - entity is string', () => { | ||
const gtmPayload = { | ||
action: 'ADD_TO_CART', | ||
entity: 'myEntity' | ||
}; | ||
|
||
const result = convertStringToValue(gtmPayload); | ||
|
||
expect(result).toEqual({ | ||
action: 'ADD_TO_CART', | ||
entity: 'myEntity' | ||
}); | ||
}); | ||
|
||
it('should convert customValues properly', () => { | ||
const gtmPayload = { | ||
action: 'ADD_TO_CART', | ||
customValues: { | ||
myCustomVal: '5', | ||
myCustomVal2: '1234' | ||
} | ||
}; | ||
|
||
const result = convertStringToValue(gtmPayload); | ||
|
||
expect(result).toEqual({ | ||
action: 'ADD_TO_CART', | ||
customValues: { | ||
myCustomVal: 5, | ||
myCustomVal2: 1234 | ||
} | ||
}); | ||
}); | ||
it('should convert string represented booleans to boolean type', () => { | ||
const gtmPayload = { | ||
action: 'ADD_TO_CART', | ||
bot: 'true', | ||
internalUser: 'false', | ||
search: { | ||
versionNumber: '5', | ||
isDirectAnswer: 'false' | ||
} | ||
}; | ||
|
||
const result = convertStringToValue(gtmPayload); | ||
|
||
expect(result).toEqual({ | ||
action: 'ADD_TO_CART', | ||
bot: true, | ||
internalUser: false, | ||
search: { | ||
versionNumber: 5, | ||
isDirectAnswer: false | ||
} | ||
}); | ||
}); | ||
it('test convert AnayticsConfig - should convert sessionTrackingEnabled', () => { | ||
const analyticsConfig = { | ||
key: 'apiKey', | ||
sessionTrackingEnabled: 'false', | ||
region: 'US' | ||
}; | ||
|
||
const result = convertStringToValue(analyticsConfig); | ||
|
||
expect(result).toEqual({ | ||
key: 'apiKey', | ||
sessionTrackingEnabled: false, | ||
region: 'US' | ||
}); | ||
}); | ||
}); |
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,38 @@ | ||
import { reportBrowserAnalytics } from '../src/index'; | ||
import { AnalyticsEventReporter } from '../src/AnalyticsEventReporter'; // replace with actual import path | ||
|
||
describe('reportBrowserAnalytics', () => { | ||
let reportSpy: jest.SpyInstance; | ||
|
||
beforeEach(() => { | ||
// Mock window['analyticsEventPayload'] | ||
(global as any).window = {}; | ||
Check warning on line 9 in tests/reportBrowserAnalytics.test.ts GitHub Actions / test-e2e
Check warning on line 9 in tests/reportBrowserAnalytics.test.ts GitHub Actions / call_run_tests / tests (14.x)
|
||
|
||
// Mock report method | ||
reportSpy = jest.spyOn(AnalyticsEventReporter.prototype, 'report'); | ||
reportSpy.mockResolvedValue('report'); | ||
}); | ||
|
||
afterEach(() => { | ||
jest.resetAllMocks(); | ||
}); | ||
|
||
it('should return a promise that resolves when payload is present', async () => { | ||
const mockPayload = [ | ||
['config', { key: 'apiKey' }], | ||
['payload', { bot: false }] | ||
]; | ||
(global as any).window['analyticsEventPayload'] = mockPayload; | ||
Check warning on line 25 in tests/reportBrowserAnalytics.test.ts GitHub Actions / test-e2e
Check warning on line 25 in tests/reportBrowserAnalytics.test.ts GitHub Actions / call_run_tests / tests (14.x)
|
||
|
||
const result = await reportBrowserAnalytics(); | ||
|
||
expect(result).toBe('report'); | ||
expect(reportSpy).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should return a promise that rejects when analyticeEvenPayload is not present in window', async () => { | ||
(global as any).window['analyticsEventPayload'] = undefined; | ||
Check warning on line 34 in tests/reportBrowserAnalytics.test.ts GitHub Actions / test-e2e
Check warning on line 34 in tests/reportBrowserAnalytics.test.ts GitHub Actions / call_run_tests / tests (14.x)
|
||
|
||
await expect(reportBrowserAnalytics()).rejects.toEqual('No payload found.'); | ||
}); | ||
}); |