Skip to content

Commit 1031c6a

Browse files
authored
New Posthog user id pattern: [inspectionId]-[UUID-distinctId] (#931)
1 parent a83db89 commit 1031c6a

File tree

12 files changed

+49
-20
lines changed

12 files changed

+49
-20
lines changed

configs/test-utils/src/__mocks__/@monkvision/analytics.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ class AnalyticsAdapterMock {
44
resetUser = jest.fn();
55
trackEvent = jest.fn();
66
setEventsProperties = jest.fn();
7+
getUserId = jest.fn(() => 'test-user-id');
78
}
89

910
export = {
@@ -18,6 +19,7 @@ export = {
1819
resetUser: jest.fn(),
1920
trackEvent: jest.fn(),
2021
setEventsProperties: jest.fn(),
22+
getUserId: jest.fn(() => 'test-user-id'),
2123
})),
2224
AnalyticsProvider: jest.fn(({ children }) => <>{children}</>),
2325
};

packages/analytics/README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ function MyCustomComponent() {
5656
resetUser,
5757
trackEvent,
5858
setEventsProperties,
59+
getUserId,
5960
} = useAnalytics();
6061
}
6162
```
@@ -89,6 +90,10 @@ class MyCustomAnalyticsAdapter implements AnalyticsAdapter {
8990
setEventsProperties(context: Record<string, Primitive>): void {
9091
// Set properties for every events
9192
}
93+
94+
getUserId(): string {
95+
// Get the current user ID
96+
}
9297
}
9398
```
9499

@@ -142,12 +147,12 @@ trackEvent: (name: string, context?: Record<string, Primitive>) => void
142147

143148
This method track a event and send it to the analytics tool. The name of the event is required and an optional context can be provided that can contain tags or properties associated to the event.
144149

145-
#### setEventsProperties
150+
#### getUserId
146151
```typescript
147-
trackEvent: (context: Record<string, Primitive>) => void
152+
getUserId: () => string
148153
```
149154

150-
This method set properties that will be sent with every `trackEvent`.
155+
This method retrieves the current user ID.
151156

152157
## Analytics Adapters
153158
### EmptyAnalyticsAdapter

packages/analytics/src/adapters/adapter.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,8 @@ export interface AnalyticsAdapter {
3838
* @param context context of every event.
3939
*/
4040
setEventsProperties: (context: Record<string, Primitive>) => void;
41+
/**
42+
* Get the ID of the user currently using the application.
43+
*/
44+
getUserId: () => string;
4145
}

packages/analytics/src/adapters/emptyAdapter.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,14 @@ export class EmptyAnalyticsAdapter implements AnalyticsAdapter {
8888
);
8989
}
9090
}
91+
92+
getUserId(): string {
93+
if (this.options.showUnsupportedMethodWarnings) {
94+
console.warn(
95+
'Application users are not supported by the current Monk Analytics Adapter and calling getUserId will have no effect.',
96+
);
97+
}
98+
99+
return '[UserId]';
100+
}
91101
}

packages/analytics/src/react/hooks.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export function useAnalytics(): AnalyticsAdapter {
1515
resetUser: adapter.resetUser.bind(adapter),
1616
trackEvent: adapter.trackEvent.bind(adapter),
1717
setEventsProperties: adapter.setEventsProperties.bind(adapter),
18+
getUserId: adapter.getUserId.bind(adapter),
1819
}),
1920
[],
2021
);

packages/analytics/test/react/provider.test.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { fireEvent, render, screen } from '@testing-library/react';
22
import { AnalyticsAdapter, AnalyticsProvider, useAnalytics } from '../../src';
33

44
function TestComponent() {
5-
const { setUserId, setUserProperties, trackEvent, resetUser, setEventsProperties } =
5+
const { setUserId, setUserProperties, trackEvent, resetUser, setEventsProperties, getUserId } =
66
useAnalytics();
77

88
return (
@@ -29,7 +29,10 @@ function TestComponent() {
2929
data-testid='set-events-properties-btn'
3030
onClick={() => setEventsProperties({ test: 'test' })}
3131
>
32-
trackEvent
32+
setEventsProperties
33+
</button>
34+
<button data-testid='get-user-id-btn' onClick={() => getUserId()}>
35+
setEventsProperties
3336
</button>
3437
</div>
3538
);
@@ -43,12 +46,14 @@ describe('AnalyticsProvider component', () => {
4346
resetUser: jest.fn(),
4447
trackEvent: jest.fn(),
4548
setEventsProperties: jest.fn(),
49+
getUserId: jest.fn(() => 'test-user-id'),
4650
};
4751
const setUserIdSpy = jest.spyOn(adapter, 'setUserId');
4852
const setUserPropertiesSpy = jest.spyOn(adapter, 'setUserProperties');
4953
const resetUserSpy = jest.spyOn(adapter, 'resetUser');
5054
const trackEventSpy = jest.spyOn(adapter, 'trackEvent');
5155
const setEventsPropertiesSpy = jest.spyOn(adapter, 'setEventsProperties');
56+
const getUserId = jest.spyOn(adapter, 'getUserId');
5257

5358
const { unmount } = render(
5459
<AnalyticsProvider adapter={adapter}>
@@ -66,6 +71,8 @@ describe('AnalyticsProvider component', () => {
6671
expect(trackEventSpy).toHaveBeenCalledTimes(1);
6772
fireEvent.click(screen.getByTestId('set-events-properties-btn'));
6873
expect(setEventsPropertiesSpy).toHaveBeenCalledTimes(1);
74+
fireEvent.click(screen.getByTestId('get-user-id-btn'));
75+
expect(getUserId).toHaveBeenCalledTimes(1);
6976
unmount();
7077
});
7178
});

packages/common-ui-web/src/components/CreateInspection/CreateInspection.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
useMonkAppState,
1010
} from '@monkvision/common';
1111
import { useMonitoring } from '@monkvision/monitoring';
12-
import { useAnalytics } from '@monkvision/analytics';
1312
import { styles } from './CreateInspection.styles';
1413
import { i18nCreateInspection } from './i18n';
1514
import { Spinner } from '../Spinner';
@@ -57,7 +56,6 @@ export const CreateInspection = i18nWrap(function CreateInspection({
5756
apiDomain: config.apiDomain,
5857
thumbnailDomain: config.thumbnailDomain,
5958
});
60-
const analytics = useAnalytics();
6159
const isMounted = useIsMounted();
6260

6361
useEffect(() => {
@@ -84,10 +82,9 @@ export const CreateInspection = i18nWrap(function CreateInspection({
8482
}
8583
} else {
8684
setTags({ inspectionId });
87-
analytics.setUserId(inspectionId);
8885
onInspectionCreated?.();
8986
}
90-
}, [inspectionId, setTags, analytics, retry, isMounted]);
87+
}, [inspectionId, setTags, retry, isMounted]);
9188

9289
return (
9390
<div style={styles['container']}>

packages/common-ui-web/src/components/Login/Login.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
useMonkTheme,
1010
} from '@monkvision/common';
1111
import { useMonitoring } from '@monkvision/monitoring';
12-
import { useAnalytics } from '@monkvision/analytics';
1312
import { Button } from '../Button';
1413
import { i18nLogin } from './i18n';
1514
import { styles } from './Login.styles';
@@ -55,7 +54,6 @@ export const Login = i18nWrap(function Login({ onLoginSuccessful, lang, style =
5554
const loading = useLoadingState();
5655
const { authToken, setAuthToken, config } = useMonkAppState();
5756
const { handleError, setUserId } = useMonitoring();
58-
const analytics = useAnalytics();
5957
const { login, logout } = useAuth();
6058
const { t } = useTranslation();
6159
const { rootStyles } = useMonkTheme();
@@ -95,7 +93,6 @@ export const Login = i18nWrap(function Login({ onLoginSuccessful, lang, style =
9593
const userId = token ? decodeMonkJwt(token) : undefined;
9694
if (userId?.sub) {
9795
setUserId(userId.sub);
98-
analytics.setUserProperties({ authToken: userId.sub });
9996
}
10097
}
10198
})

packages/common-ui-web/src/components/VehicleTypeSelection/VehicleTypeSelection.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { AllOrNone, VehicleType } from '@monkvision/types';
1010
import { RefObject, useEffect, useMemo, useRef, useState } from 'react';
1111
import { decodeMonkJwt, MonkApiConfig, useMonkApi } from '@monkvision/network';
1212
import { useMonitoring } from '@monkvision/monitoring';
13-
import { useAnalytics } from '@monkvision/analytics';
1413
import { styles } from './VehicleTypeSelection.styles';
1514
import { i18nVehicleTypeSelection } from './i18n';
1615
import { Button } from '../Button';
@@ -88,7 +87,6 @@ export const VehicleTypeSelection = i18nWrap(function VehicleTypeSelection(
8887
});
8988
const loading = useLoadingState();
9089
const { handleError, setTags, setUserId } = useMonitoring();
91-
const analytics = useAnalytics();
9290
const [initialScroll, setInitialScroll] = useState(true);
9391
const vehicleTypes = useMemo(
9492
() => getVehicleTypes(props.availableVehicleTypes),
@@ -105,14 +103,12 @@ export const VehicleTypeSelection = i18nWrap(function VehicleTypeSelection(
105103
useEffect(() => {
106104
if (props.inspectionId) {
107105
setTags({ inspectionId: props.inspectionId });
108-
analytics.setUserId(props.inspectionId);
109106
}
110107
const userId = props.authToken ? decodeMonkJwt(props.authToken) : undefined;
111108
if (userId?.sub) {
112109
setUserId(userId.sub);
113-
analytics.setUserProperties({ authToken: userId.sub });
114110
}
115-
}, [props.inspectionId, props.authToken, analytics, setTags, setUserId]);
111+
}, [props.inspectionId, props.authToken, setTags, setUserId]);
116112

117113
useEffect(() => {
118114
loading.start();

packages/common/src/apps/appStateProvider.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
VideoCaptureAppConfig,
88
} from '@monkvision/types';
99
import { sights } from '@monkvision/sights';
10-
import React, { PropsWithChildren, useContext, useEffect, useMemo, useState } from 'react';
10+
import { PropsWithChildren, useContext, useEffect, useMemo, useState } from 'react';
1111
import { useIsMounted, useLoadingState } from '../hooks';
1212
import { MonkSearchParam, useMonkSearchParams } from './searchParams';
1313
import {
@@ -17,7 +17,6 @@ import {
1717
VideoCaptureAppState,
1818
} from './appState';
1919
import { useAppStateMonitoring } from './monitoring';
20-
import { useAppStateAnalytics } from './analytics';
2120
import { getAvailableVehicleTypes } from '../utils';
2221

2322
/**
@@ -98,7 +97,6 @@ export function MonkAppStateProvider({
9897
const monkSearchParams = useMonkSearchParams({ availableVehicleTypes });
9998
const isMounted = useIsMounted();
10099
useAppStateMonitoring({ authToken, inspectionId, vehicleType, steeringWheel });
101-
useAppStateAnalytics({ inspectionId });
102100

103101
useEffect(() => {
104102
loading.onSuccess();

packages/inspection-capture-web/src/hooks/useTracking.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,17 @@ export function useTracking({ inspectionId, authToken }: TrackingParams) {
2525
const monitoring = useMonitoring();
2626

2727
useEffect(() => {
28+
const currentAnalyticsUserId = analytics.getUserId();
29+
let newAnalyticsUserId = `${inspectionId}:${currentAnalyticsUserId}`;
30+
if (currentAnalyticsUserId.includes(':')) {
31+
newAnalyticsUserId = `${inspectionId}:${currentAnalyticsUserId.split(':')[1]}`;
32+
}
33+
analytics.setUserId(newAnalyticsUserId);
34+
console.log('posthog id : ', newAnalyticsUserId, ', inspectionId: ', inspectionId);
2835
monitoring.setTags({
2936
inspectionId,
3037
});
38+
3139
const userId = decodeMonkJwt(authToken)?.sub;
3240
if (userId) {
3341
monitoring.setUserId(userId);

packages/posthog/src/adapter.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,8 @@ export class PosthogAnalyticsAdapter implements AnalyticsAdapter {
9898
setEventsProperties(context: Record<string, Primitive>): void {
9999
posthog.register(context);
100100
}
101+
102+
getUserId(): string {
103+
return posthog.get_distinct_id();
104+
}
101105
}

0 commit comments

Comments
 (0)