feat: Use amplitude for Cloud Analytics (#13217)

Migrates our analytics integration on Cloud from PostHog to Amplitude.
This change updates the core AnalyticsHelper class to use the Amplitude
SDK while maintaining the same tracking interface. Rest of all existing
analytics calls throughout the codebase continue to work without
modification.

**Changes:**
- Replace PostHog analytics with Amplitude SDK
- Rename ANALYTICS_TOKEN to CLOUD_ANALYTICS_TOKEN for clarity
- Fix bug in page() method signature that was causing malformed payloads
This commit is contained in:
Pranav
2026-01-09 09:32:09 -08:00
committed by GitHub
parent ded2f2751a
commit 837026c146
8 changed files with 178 additions and 105 deletions

View File

@@ -1,4 +1,4 @@
import posthog from 'posthog-js';
import * as amplitude from '@amplitude/analytics-browser';
/**
* AnalyticsHelper class to initialize and track user analytics
@@ -26,12 +26,10 @@ export class AnalyticsHelper {
return;
}
posthog.init(this.analyticsToken, {
api_host: 'https://app.posthog.com',
capture_pageview: false,
persistence: 'localStorage+cookie',
amplitude.init(this.analyticsToken, {
defaultTracking: false,
});
this.analytics = posthog;
this.analytics = amplitude;
}
/**
@@ -45,20 +43,26 @@ export class AnalyticsHelper {
}
this.user = user;
this.analytics.identify(this.user.id.toString(), {
email: this.user.email,
name: this.user.name,
avatar: this.user.avatar_url,
});
this.analytics.setUserId(`user-${this.user.id.toString()}`);
const identifyEvent = new amplitude.Identify();
identifyEvent.set('email', this.user.email);
identifyEvent.set('name', this.user.name);
identifyEvent.set('avatar', this.user.avatar_url);
this.analytics.identify(identifyEvent);
const { accounts, account_id: accountId } = this.user;
const [currentAccount] = accounts.filter(
account => account.id === accountId
);
if (currentAccount) {
this.analytics.group('company', currentAccount.id.toString(), {
name: currentAccount.name,
});
const groupId = `account-${currentAccount.id.toString()}`;
this.analytics.setGroup('company', groupId);
const groupIdentify = new amplitude.Identify();
groupIdentify.set('name', currentAccount.name);
this.analytics.groupIdentify('company', groupId, groupIdentify);
}
}
@@ -72,20 +76,21 @@ export class AnalyticsHelper {
if (!this.analytics) {
return;
}
this.analytics.capture(eventName, properties);
this.analytics.track(eventName, properties);
}
/**
* Track the page views
* @function
* @param {Object} params - Page view properties
* @param {string} pageName - Page name
* @param {Object} [properties={}] - Page view properties
*/
page(params) {
page(pageName, properties = {}) {
if (!this.analytics) {
return;
}
this.analytics.capture('$pageview', params);
this.analytics.track('$pageview', { pageName, ...properties });
}
}

View File

@@ -1,12 +1,15 @@
import helperObject, { AnalyticsHelper } from '../';
vi.mock('posthog-js', () => ({
default: {
init: vi.fn(),
identify: vi.fn(),
capture: vi.fn(),
group: vi.fn(),
},
vi.mock('@amplitude/analytics-browser', () => ({
init: vi.fn(),
setUserId: vi.fn(),
identify: vi.fn(),
setGroup: vi.fn(),
groupIdentify: vi.fn(),
track: vi.fn(),
Identify: vi.fn(() => ({
set: vi.fn(),
})),
}));
describe('helperObject', () => {
@@ -22,12 +25,12 @@ describe('AnalyticsHelper', () => {
});
describe('init', () => {
it('should initialize posthog with the correct token', async () => {
it('should initialize amplitude with the correct token', async () => {
await analyticsHelper.init();
expect(analyticsHelper.analytics).not.toBe(null);
});
it('should not initialize posthog if token is not provided', async () => {
it('should not initialize amplitude if token is not provided', async () => {
analyticsHelper = new AnalyticsHelper();
await analyticsHelper.init();
expect(analyticsHelper.analytics).toBe(null);
@@ -36,10 +39,15 @@ describe('AnalyticsHelper', () => {
describe('identify', () => {
beforeEach(() => {
analyticsHelper.analytics = { identify: vi.fn(), group: vi.fn() };
analyticsHelper.analytics = {
setUserId: vi.fn(),
identify: vi.fn(),
setGroup: vi.fn(),
groupIdentify: vi.fn(),
};
});
it('should call identify on posthog with correct arguments', () => {
it('should call setUserId and identify on amplitude with correct arguments', () => {
analyticsHelper.identify({
id: 123,
email: 'test@example.com',
@@ -49,19 +57,18 @@ describe('AnalyticsHelper', () => {
account_id: 1,
});
expect(analyticsHelper.analytics.identify).toHaveBeenCalledWith('123', {
email: 'test@example.com',
name: 'Test User',
avatar: 'avatar_url',
});
expect(analyticsHelper.analytics.group).toHaveBeenCalledWith(
'company',
'1',
{ name: 'Account 1' }
expect(analyticsHelper.analytics.setUserId).toHaveBeenCalledWith(
'user-123'
);
expect(analyticsHelper.analytics.identify).toHaveBeenCalled();
expect(analyticsHelper.analytics.setGroup).toHaveBeenCalledWith(
'company',
'account-1'
);
expect(analyticsHelper.analytics.groupIdentify).toHaveBeenCalled();
});
it('should call identify on posthog without group', () => {
it('should call identify on amplitude without group', () => {
analyticsHelper.identify({
id: 123,
email: 'test@example.com',
@@ -71,10 +78,10 @@ describe('AnalyticsHelper', () => {
account_id: 5,
});
expect(analyticsHelper.analytics.group).not.toHaveBeenCalled();
expect(analyticsHelper.analytics.setGroup).not.toHaveBeenCalled();
});
it('should not call analytics.page if analytics is null', () => {
it('should not call analytics methods if analytics is null', () => {
analyticsHelper.analytics = null;
analyticsHelper.identify({});
expect(analyticsHelper.analytics).toBe(null);
@@ -83,27 +90,27 @@ describe('AnalyticsHelper', () => {
describe('track', () => {
beforeEach(() => {
analyticsHelper.analytics = { capture: vi.fn() };
analyticsHelper.analytics = { track: vi.fn() };
analyticsHelper.user = { id: 123 };
});
it('should call capture on posthog with correct arguments', () => {
it('should call track on amplitude with correct arguments', () => {
analyticsHelper.track('Test Event', { prop1: 'value1', prop2: 'value2' });
expect(analyticsHelper.analytics.capture).toHaveBeenCalledWith(
expect(analyticsHelper.analytics.track).toHaveBeenCalledWith(
'Test Event',
{ prop1: 'value1', prop2: 'value2' }
);
});
it('should call capture on posthog with default properties', () => {
it('should call track on amplitude with default properties', () => {
analyticsHelper.track('Test Event');
expect(analyticsHelper.analytics.capture).toHaveBeenCalledWith(
expect(analyticsHelper.analytics.track).toHaveBeenCalledWith(
'Test Event',
{}
);
});
it('should not call capture on posthog if analytics is not initialized', () => {
it('should not call track on amplitude if analytics is not initialized', () => {
analyticsHelper.analytics = null;
analyticsHelper.track('Test Event', { prop1: 'value1', prop2: 'value2' });
expect(analyticsHelper.analytics).toBe(null);
@@ -112,24 +119,25 @@ describe('AnalyticsHelper', () => {
describe('page', () => {
beforeEach(() => {
analyticsHelper.analytics = { capture: vi.fn() };
analyticsHelper.analytics = { track: vi.fn() };
});
it('should call the capture method for pageview with the correct arguments', () => {
const params = {
name: 'Test page',
url: '/test',
it('should call the track method for pageview with the correct arguments', () => {
const pageName = 'home';
const properties = {
path: '/test',
name: 'home',
};
analyticsHelper.page(params);
expect(analyticsHelper.analytics.capture).toHaveBeenCalledWith(
analyticsHelper.page(pageName, properties);
expect(analyticsHelper.analytics.track).toHaveBeenCalledWith(
'$pageview',
params
{ pageName: 'home', path: '/test', name: 'home' }
);
});
it('should not call analytics.capture if analytics is null', () => {
it('should not call analytics.track if analytics is null', () => {
analyticsHelper.analytics = null;
analyticsHelper.page();
analyticsHelper.page('home');
expect(analyticsHelper.analytics).toBe(null);
});
});