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

chore(runway): cherry-pick fix: app opened event #13175

Merged
merged 1 commit into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 0 additions & 4 deletions app/components/UI/Ramp/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1 @@
import { AnalyticsEvents } from '../types';

export const AnonymousEvents: (keyof AnalyticsEvents)[] = [];

export const RAMPS_SEND = 'RAMPS_SEND';
23 changes: 0 additions & 23 deletions app/components/UI/Ramp/hooks/useAnalytics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,27 +38,4 @@ describe('useAnalytics', () => {
.build(),
);
});

it('calls trackEvent for anonymous params', () => {
const testEvent = 'RAMP_REGION_SELECTED';
const testEventParams = {
country_id: 'test-country-id',
is_unsupported_offramp: false,
is_unsupported_onramp: false,
} as const;

jest.mock('../constants', () => ({
AnonymousEvents: [testEvent],
}));

const { result } = renderHookWithProvider(() => useAnalytics());

result.current(testEvent, testEventParams);

expect(MetaMetrics.getInstance().trackEvent).toHaveBeenCalledWith(
MetricsEventBuilder.createEventBuilder(MetaMetricsEvents[testEvent])
.addSensitiveProperties(testEventParams)
.build(),
);
});
});
18 changes: 5 additions & 13 deletions app/components/UI/Ramp/hooks/useAnalytics.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,19 @@
import { useCallback } from 'react';
import { InteractionManager } from 'react-native';
import { AnalyticsEvents } from '../types';
import { AnonymousEvents } from '../constants';
import { MetaMetrics, MetaMetricsEvents } from '../../../../core/Analytics';
import { MetricsEventBuilder } from '../../../../core/Analytics/MetricsEventBuilder';

export function trackEvent<T extends keyof AnalyticsEvents>(
eventType: T,
params: AnalyticsEvents[T],
) {
const anonymous = AnonymousEvents.includes(eventType);
const metrics = MetaMetrics.getInstance();
const event = MetricsEventBuilder.createEventBuilder(
MetaMetricsEvents[eventType],
metrics.trackEvent(MetricsEventBuilder.createEventBuilder(
MetaMetricsEvents[eventType],
)
.addProperties({ ...params })
.build()
);

InteractionManager.runAfterInteractions(() => {
if (anonymous) {
metrics.trackEvent(event.addSensitiveProperties({ ...params }).build());
} else {
metrics.trackEvent(event.addProperties({ ...params }).build());
}
});
}

function useAnalytics() {
Expand Down
22 changes: 21 additions & 1 deletion app/core/Analytics/MetricsEventBuilder.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MetricsEventBuilder } from './MetricsEventBuilder';
import {MetricsEventBuilder} from './MetricsEventBuilder';
import { IMetaMetricsEvent, JsonMap } from './MetaMetrics.types';

describe('MetricsEventBuilder', () => {
Expand Down Expand Up @@ -80,6 +80,26 @@ describe('MetricsEventBuilder', () => {
expect(rebuiltEvent.sensitiveProperties).toEqual(newSensitiveProps);
});

it('compares events', () => {
const newProps: JsonMap = {
newProp: 'newValue',
};

const event = MetricsEventBuilder.createEventBuilder(mockLegacyEvent)
.addSensitiveProperties(newProps)
.build();

const similarEvent = MetricsEventBuilder.createEventBuilder(mockLegacyEvent)
.addSensitiveProperties(newProps)
.build();
expect(similarEvent).toEqual(event);

const differentEvent = MetricsEventBuilder.createEventBuilder(mockLegacyEvent)
.addProperties(newProps)
.build();
expect(differentEvent).not.toEqual(event);
});

it('removes properties', () => {
const event = MetricsEventBuilder.createEventBuilder(mockEvent)
.addProperties({ newProp: 'newValue' })
Expand Down
57 changes: 17 additions & 40 deletions app/core/Analytics/MetricsEventBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,57 +9,29 @@ import {
* the event tracking object to be produced by MetricsEventBuilder
*/
class TrackingEvent implements ITrackingEvent {
readonly #name: string;
#properties: JsonMap;
#sensitiveProperties: JsonMap;
#saveDataRecording: boolean;
readonly name: string;
properties: JsonMap;
sensitiveProperties: JsonMap;
saveDataRecording: boolean;

constructor(event: IMetaMetricsEvent) {
this.#name = event.category;
this.#properties = event.properties || {};
this.#sensitiveProperties = {};
this.#saveDataRecording = true;
}

get name(): string {
return this.#name;
}

get properties(): JsonMap {
return this.#properties;
}

set properties(properties: JsonMap) {
this.#properties = properties;
}

get sensitiveProperties(): JsonMap {
return this.#sensitiveProperties;
}

set sensitiveProperties(sensitiveProperties: JsonMap) {
this.#sensitiveProperties = sensitiveProperties;
}

get saveDataRecording(): boolean {
return this.#saveDataRecording;
}

set saveDataRecording(saveDataRecording: boolean) {
this.#saveDataRecording = saveDataRecording;
this.name = event.category;
this.properties = event.properties || {};
this.sensitiveProperties = {};
this.saveDataRecording = true;
}

get isAnonymous(): boolean {
return !!(
this.#sensitiveProperties && Object.keys(this.#sensitiveProperties).length
this.sensitiveProperties && Object.keys(this.sensitiveProperties).length
);
}

get hasProperties(): boolean {
return !!(
(this.#properties && Object.keys(this.#properties).length) ||
(this.#sensitiveProperties &&
Object.keys(this.#sensitiveProperties).length)
(this.properties && Object.keys(this.properties).length) ||
(this.sensitiveProperties &&
Object.keys(this.sensitiveProperties).length)
);
}
}
Expand All @@ -85,6 +57,11 @@ class MetricsEventBuilder {

protected constructor(event: IMetaMetricsEvent | ITrackingEvent) {
if (isTrackingEvent(event)) {
// Be careful that in case the event is already a ITrackingEvent
// we don't want to create a new one so this passes the reference.
// Changes applied to the source event will be reflected in the new event.
// If at any point you need to clone the ITrackingEvent, it will require to
// create a new ITrackingEvent object by copying the values.
this.#trackingEvent = event;
return;
}
Expand Down
29 changes: 17 additions & 12 deletions app/core/AppStateEventListener.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,25 +79,30 @@ describe('AppStateEventListener', () => {
mockAppStateListener('active');
jest.advanceTimersByTime(2000);

expect(mockMetrics.trackEvent).toHaveBeenCalledWith(
MetricsEventBuilder.createEventBuilder(MetaMetricsEvents.APP_OPENED)
.addSensitiveProperties({
attributionId: 'test123',
utm_source: 'source',
utm_medium: 'medium',
utm_campaign: 'campaign',
})
.build(),
);
const expectedEvent = MetricsEventBuilder.createEventBuilder(MetaMetricsEvents.APP_OPENED)
.addProperties({
attributionId: 'test123',
utm_source: 'source',
utm_medium: 'medium',
utm_campaign: 'campaign',
})
.build();

expect(mockMetrics.trackEvent).toHaveBeenCalledWith(expectedEvent);
});

it('does not track event when processAttribution returns undefined', () => {
it('tracks event when app becomes active without attribution data', () => {
jest
.spyOn(ReduxService, 'store', 'get')
.mockReturnValue({} as unknown as ReduxStore);
(processAttribution as jest.Mock).mockReturnValue(undefined);

mockAppStateListener('active');
jest.advanceTimersByTime(2000);

expect(mockMetrics.trackEvent).not.toHaveBeenCalled();
expect(mockMetrics.trackEvent).toHaveBeenCalledWith(
MetricsEventBuilder.createEventBuilder(MetaMetricsEvents.APP_OPENED).build()
);
});

it('handles errors gracefully', () => {
Expand Down
8 changes: 3 additions & 5 deletions app/core/AppStateEventListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,16 @@ export class AppStateEventListener {
currentDeeplink: this.currentDeeplink,
store: ReduxService.store,
});
const appOpenedEventBuilder = MetricsEventBuilder.createEventBuilder(MetaMetricsEvents.APP_OPENED);
if (attribution) {
const { attributionId, utm, ...utmParams } = attribution;
DevLogger.log(
`AppStateManager:: processAppStateChange:: sending event 'APP_OPENED' attributionId=${attribution.attributionId} utm=${attribution.utm}`,
utmParams,
);
MetaMetrics.getInstance().trackEvent(
MetricsEventBuilder.createEventBuilder(MetaMetricsEvents.APP_OPENED)
.addSensitiveProperties({ attributionId, ...utmParams })
.build(),
);
appOpenedEventBuilder.addProperties({ attributionId, ...utmParams });
}
MetaMetrics.getInstance().trackEvent(appOpenedEventBuilder.build());
} catch (error) {
Logger.error(
error as Error,
Expand Down
Loading