From 74b0e52f9a2ac243af50ab419da9c296b74a1540 Mon Sep 17 00:00:00 2001 From: James Salter Date: Wed, 21 Jul 2021 08:23:42 +0100 Subject: [PATCH] Enforce anon/pseudo-anon via types --- src/PosthogAnalytics.ts | 48 +++++++++++++++++++++++++---------- test/PosthogAnalytics-test.ts | 25 +++++++++++++----- 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/src/PosthogAnalytics.ts b/src/PosthogAnalytics.ts index 5b2a601adc..133c9275d4 100644 --- a/src/PosthogAnalytics.ts +++ b/src/PosthogAnalytics.ts @@ -1,11 +1,28 @@ import posthog from 'posthog-js'; import SdkConfig from './SdkConfig'; -export interface IEvent { - key: string; +interface IEvent { + // The event name that will be used by PostHog. + // TODO: standard format (camel case? snake? UpperCase?) + eventName: string; + + // The properties of the event that will be stored in PostHog. properties: {} } +// If an event extends IPseudonymousEvent, the event contains pseudonymous data +// that won't be sent unless the user has explicitly consented to pseudonymous tracking. +// For example, hashed user IDs or room IDs. +export interface IPseudonymousEvent extends IEvent {} + +// If an event extends IAnonymousEvent, the event strictly contains *only* anonymous data which +// may be sent without explicit user consent. +export interface IAnonymousEvent extends IEvent {} + +export interface IRoomEvent extends IPseudonymousEvent { + hashedRoomId: string +} + export interface IOnboardingLoginBegin extends IEvent { key: "onboarding_login_begin", } @@ -55,27 +72,32 @@ export class PosthogAnalytics { this.onlyTrackAnonymousEvents = enabled; } - public track( - key: E["key"], + public trackPseudonymousEvent( + eventName: E["eventName"], properties: E["properties"], - anonymous = false, ) { if (!this.initialised) return; - if (this.onlyTrackAnonymousEvents && !anonymous) return; - - this.posthog.capture(key, properties); + if (this.onlyTrackAnonymousEvents) return; + this.posthog.capture(eventName, properties); } - public async trackRoomEvent( - key: E["key"], - roomId: string, + public trackAnonymousEvent( + eventName: E["eventName"], properties: E["properties"], - ...args + ) { + if (!this.initialised) return; + this.posthog.capture(eventName, properties); + } + + public async trackRoomEvent( + eventName: E["eventName"], + roomId: string, + properties: Omit, ) { const updatedProperties = { ...properties, hashedRoomId: roomId ? await hashHex(roomId) : null, }; - this.track(key, updatedProperties, ...args); + this.trackPseudonymousEvent(eventName, updatedProperties); } } diff --git a/test/PosthogAnalytics-test.ts b/test/PosthogAnalytics-test.ts index 56e6af8666..dfadac921d 100644 --- a/test/PosthogAnalytics-test.ts +++ b/test/PosthogAnalytics-test.ts @@ -1,4 +1,4 @@ -import { IEvent, PosthogAnalytics } from '../src/PosthogAnalytics'; +import { IAnonymousEvent, IRoomEvent, PosthogAnalytics } from '../src/PosthogAnalytics'; import SdkConfig from '../src/SdkConfig'; const crypto = require('crypto'); @@ -12,13 +12,20 @@ class FakePosthog { } } -export interface ITestEvent extends IEvent { +export interface ITestEvent extends IAnonymousEvent { key: "jest_test_event", properties: { foo: string } } +export interface ITestRoomEvent extends IRoomEvent { + key: "jest_test_room_event", + properties: { + foo: string + } +} + describe("PosthogAnalytics", () => { let analytics: PosthogAnalytics; let fakePosthog: FakePosthog; @@ -61,7 +68,7 @@ describe("PosthogAnalytics", () => { it("Should pass track() to posthog", () => { analytics.init(false); - analytics.track("jest_test_event", { + analytics.trackAnonymousEvent("jest_test_event", { foo: "bar", }); expect(fakePosthog.capture.mock.calls[0][0]).toBe("jest_test_event"); @@ -71,7 +78,7 @@ describe("PosthogAnalytics", () => { it("Should pass trackRoomEvent to posthog", () => { analytics.init(false); const roomId = "42"; - return analytics.trackRoomEvent("jest_test_event", roomId, { + return analytics.trackRoomEvent("jest_test_event", roomId, { foo: "bar", }).then(() => { expect(fakePosthog.capture.mock.calls[0][0]).toBe("jest_test_event"); @@ -82,11 +89,17 @@ describe("PosthogAnalytics", () => { }); }); - it("Should silently not send messages if not inititalised", () => { - analytics.track("jest_test_event", { + it("Should silently not track if not inititalised", () => { + analytics.trackAnonymousEvent("jest_test_event", { foo: "bar", }); expect(fakePosthog.capture.mock.calls.length).toBe(0); }); + + it("Should not track non-anonymous messages if onlyTrackAnonymousEvents is true", () => { + analytics.trackAnonymousEvent("jest_test_event", { + foo: "bar", + }); + }); });