diff --git a/packages/plugin-ga-events-forwarder-browser/src/helpers.ts b/packages/plugin-ga-events-forwarder-browser/src/helpers.ts index 328237c9b..44e9ca2da 100644 --- a/packages/plugin-ga-events-forwarder-browser/src/helpers.ts +++ b/packages/plugin-ga-events-forwarder-browser/src/helpers.ts @@ -33,9 +33,11 @@ export const parseGA4Events = (url: URL, data?: BodyInit | null): GA4Event[] => return [sharedProperties]; } + const separator = data.toString().includes('\n') ? '\n' : '\\r\\'; + return data .toString() - .split('\\r\\') + .split(separator) .map((event) => { return { ...sharedProperties, @@ -53,25 +55,29 @@ export const parseGA4Events = (url: URL, data?: BodyInit | null): GA4Event[] => * @returns A list of Amplitude events, transformed from Google Analytics events. */ export const transformToAmplitudeEvents = (ga4Events: GA4Event[]): BaseEvent[] => - ga4Events.map((ga4Event) => ({ - event_type: String(ga4Event[GA_PAYLOAD_EVENT_NAME_KEY]), - user_id: String(ga4Event[GA_PAYLOAD_USER_ID_KEY]), - event_properties: { - ...getProperties(ga4Event, GA_PAYLOAD_EVENT_PROPERTY_STRING_PREFIX, GA_PAYLOAD_EVENT_PROPERTY_NUMBER_PREFIX), - [AMPLITUDE_EVENT_PROPERTY_MEASUREMENT_ID]: ga4Event[GA_PAYLOAD_TRACKING_ID_KEY], - }, - user_properties: getProperties( - ga4Event, - GA_PAYLOAD_USER_PROPERTY_STRING_PREFIX, - GA_PAYLOAD_USER_PROPERTY_NUMBER_PREFIX, - ), - // NOTE: Unable to pass an event to track() with custom library value because an internal plugin will overwrite `event.library` value. - // Instead, since an enrichment plugin's execute function is performed at a later time. pass an event to track() with library info in `event.extra`, - // then enrich `event.library` in this plugin's execute function. - extra: { - library: AMPLITUDE_EVENT_LIBRARY, - }, - })); + ga4Events + .filter((ga4Event) => Boolean(ga4Event[GA_PAYLOAD_EVENT_NAME_KEY])) + .map((ga4Event) => ({ + event_type: String(ga4Event[GA_PAYLOAD_EVENT_NAME_KEY]), + ...(ga4Event[GA_PAYLOAD_USER_ID_KEY] !== null && ga4Event[GA_PAYLOAD_USER_ID_KEY] !== undefined + ? { user_id: String(ga4Event[GA_PAYLOAD_USER_ID_KEY]) } + : undefined), + event_properties: { + ...getProperties(ga4Event, GA_PAYLOAD_EVENT_PROPERTY_STRING_PREFIX, GA_PAYLOAD_EVENT_PROPERTY_NUMBER_PREFIX), + [AMPLITUDE_EVENT_PROPERTY_MEASUREMENT_ID]: ga4Event[GA_PAYLOAD_TRACKING_ID_KEY], + }, + user_properties: getProperties( + ga4Event, + GA_PAYLOAD_USER_PROPERTY_STRING_PREFIX, + GA_PAYLOAD_USER_PROPERTY_NUMBER_PREFIX, + ), + // NOTE: Unable to pass an event to track() with custom library value because an internal plugin will overwrite `event.library` value. + // Instead, since an enrichment plugin's execute function is performed at a later time. pass an event to track() with library info in `event.extra`, + // then enrich `event.library` in this plugin's execute function. + extra: { + library: AMPLITUDE_EVENT_LIBRARY, + }, + })); /** * @param ga4Event A list of deserialized Google Analytics events. diff --git a/packages/plugin-ga-events-forwarder-browser/test/constants.ts b/packages/plugin-ga-events-forwarder-browser/test/constants.ts index da474a073..60f7819fb 100644 --- a/packages/plugin-ga-events-forwarder-browser/test/constants.ts +++ b/packages/plugin-ga-events-forwarder-browser/test/constants.ts @@ -7,7 +7,7 @@ export const MOCK_REGIONAL_URL: string = (() => { return regionalUrl.toString(); })(); -export const MOCK_GA_EVENT = { +export const MOCK_GA_EVENT: Record = { v: '2', tid: 'G-DELYSDZ9Q3', gtm: '45je3890', diff --git a/packages/plugin-ga-events-forwarder-browser/test/helpers.test.ts b/packages/plugin-ga-events-forwarder-browser/test/helpers.test.ts index 55071eaa7..262f2530c 100644 --- a/packages/plugin-ga-events-forwarder-browser/test/helpers.test.ts +++ b/packages/plugin-ga-events-forwarder-browser/test/helpers.test.ts @@ -31,7 +31,7 @@ describe('parseGA4Events', () => { ]); }); - test('should parse ga4 events w/ request body', () => { + test('should parse ga4 events w/ request body separated with `\\r\\`', () => { const url = new URL(MOCK_URL); const data = 'en=page_view&_ee=1\\r\\en=custom_event&_ee=1&epn.1=1&ep.a=a&upn.2=2&up.b=b'; const result = parseGA4Events(url, data); @@ -54,6 +54,30 @@ describe('parseGA4Events', () => { ]); }); + test('should parse ga4 events w/ request body separated with `\n`', () => { + const url = new URL(MOCK_URL); + const data = `en=page_view&_ee=1 +en=custom_event&_ee=1&epn.1=1&ep.a=a&upn.2=2&up.b=b`; + const result = parseGA4Events(url, data); + + expect(result).toEqual([ + { + ...MOCK_GA_EVENT, + en: 'page_view', + _ee: '1', + }, + { + ...MOCK_GA_EVENT, + en: 'custom_event', + _ee: '1', + 'ep.a': 'a', + 'epn.1': '1', + 'up.b': 'b', + 'upn.2': '2', + }, + ]); + }); + describe('transformToAmplitudeEvents', () => { test('should transform ga4 events to amplitude events', () => { const amplitudeEvent = transformToAmplitudeEvents([ @@ -86,6 +110,46 @@ describe('parseGA4Events', () => { }, ]); }); + + test('should ignore ga4 events w/o en', () => { + const amplitudeEvent = transformToAmplitudeEvents([ + MOCK_GA_EVENT, + // `en` is not defined + ]); + expect(amplitudeEvent).toEqual([]); + }); + + test('should handle missing uid', () => { + const mockGaEvent: Record = { + ...MOCK_GA_EVENT, + en: 'custom_event', + _ee: '1', + 'ep.a': 'a', + 'epn.1': '1', + 'up.b': 'b', + 'upn.2': '2', + }; + delete mockGaEvent.uid; + + const amplitudeEvent = transformToAmplitudeEvents([mockGaEvent]); + expect(amplitudeEvent).toEqual([ + { + event_type: 'custom_event', + event_properties: { + a: 'a', + 1: 1, + [AMPLITUDE_EVENT_PROPERTY_MEASUREMENT_ID]: 'G-DELYSDZ9Q3', + }, + user_properties: { + b: 'b', + 2: 2, + }, + extra: { + library: AMPLITUDE_EVENT_LIBRARY, + }, + }, + ]); + }); }); describe('getProperties', () => {