Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Report crypto sdk in posthog #11834

Merged
merged 7 commits into from
Nov 13, 2023
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
30 changes: 29 additions & 1 deletion src/PosthogAnalytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ export class PosthogAnalytics {
private authenticationType: Signup["authenticationType"] = "Other";
private watchSettingRef?: string;

// Will be set when the matrixClient is passed to the analytics object (e.g. on login).
private currentCryptoBackend?: "Rust" | "Legacy" = undefined;

public static get instance(): PosthogAnalytics {
if (!this._instance) {
this._instance = new PosthogAnalytics(posthog);
Expand Down Expand Up @@ -170,6 +173,7 @@ export class PosthogAnalytics {
SettingsStore.monitorSetting("layout", null);
SettingsStore.monitorSetting("useCompactLayout", null);
this.onLayoutUpdated();
this.updateCryptoSuperProperty();
}

private onLayoutUpdated = (): void => {
Expand Down Expand Up @@ -278,6 +282,8 @@ export class PosthogAnalytics {
this.registerSuperProperties(this.platformSuperProperties);
}
this.anonymity = anonymity;
// update anyhow, no-op if not enabled or Disabled.
this.updateCryptoSuperProperty();
}

private static getRandomAnalyticsId(): string {
Expand Down Expand Up @@ -367,7 +373,28 @@ export class PosthogAnalytics {
this.registerSuperProperties(this.platformSuperProperties);
}

private updateCryptoSuperProperty(): void {
if (!this.enabled || this.anonymity === Anonymity.Disabled) return;
// Update super property for cryptoSDK in posthog.
// This property will be subsequently passed in every event.
if (this.currentCryptoBackend) {
this.registerSuperProperties({ cryptoSDK: this.currentCryptoBackend });
}
}

public async updateAnonymityFromSettings(client: MatrixClient, pseudonymousOptIn: boolean): Promise<void> {
// Temporary until we have migration code to switch crypto sdk.
if (client.getCrypto()) {
const cryptoVersion = client.getCrypto()!.getVersion();
// version for rust is something like "Rust SDK 0.6.0 (9c6b550), Vodozemac 0.5.0"
// for legacy it will be 'Olm x.x.x"
if (cryptoVersion.includes("Rust SDK")) {
this.currentCryptoBackend = "Rust";
} else {
this.currentCryptoBackend = "Legacy";
}
}

// Update this.anonymity based on the user's analytics opt-in settings
const anonymity = pseudonymousOptIn ? Anonymity.Pseudonymous : Anonymity.Disabled;
this.setAnonymity(anonymity);
Expand All @@ -379,7 +406,8 @@ export class PosthogAnalytics {
}

if (anonymity !== Anonymity.Disabled) {
await PosthogAnalytics.instance.updatePlatformSuperProperties();
await this.updatePlatformSuperProperties();
this.updateCryptoSuperProperty();
}
}

Expand Down
76 changes: 76 additions & 0 deletions test/PosthogAnalytics-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ limitations under the License.

import { mocked } from "jest-mock";
import { PostHog } from "posthog-js";
import { CryptoApi, MatrixClient } from "matrix-js-sdk/src/matrix";

import { Anonymity, getRedactedCurrentLocation, IPosthogEvent, PosthogAnalytics } from "../src/PosthogAnalytics";
import SdkConfig from "../src/SdkConfig";
Expand All @@ -37,6 +38,7 @@ const getFakePosthog = (): PostHog =>
persistence: {
get_user_state: jest.fn(),
},
identifyUser: jest.fn(),
} as unknown as PostHog);

interface ITestEvent extends IPosthogEvent {
Expand Down Expand Up @@ -274,4 +276,78 @@ describe("PosthogAnalytics", () => {
});
});
});

describe("CryptoSdk", () => {
let analytics: PosthogAnalytics;
const getFakeClient = (): MatrixClient =>
({
getCrypto: jest.fn(),
setAccountData: jest.fn(),
// just fake return an `im.vector.analytics` content
getAccountDataFromServer: jest.fn().mockReturnValue({
id: "0000000",
pseudonymousAnalyticsOptIn: true,
}),
} as unknown as MatrixClient);

beforeEach(async () => {
SdkConfig.put({
brand: "Testing",
posthog: {
project_api_key: "foo",
api_host: "bar",
},
});

analytics = new PosthogAnalytics(fakePosthog);
});

// `updateAnonymityFromSettings` is called On page load / login / account data change.
// We manually call it so we can test the behaviour.
async function simulateLogin(rustBackend: boolean, pseudonymous = true) {
// To simulate a switch we call updateAnonymityFromSettings.
// As per documentation this function is called On login.
const mockClient = getFakeClient();
mocked(mockClient.getCrypto).mockReturnValue({
getVersion: () => {
return rustBackend ? "Rust SDK 0.6.0 (9c6b550), Vodozemac 0.5.0" : "Olm 3.2.0";
},
} as unknown as CryptoApi);
await analytics.updateAnonymityFromSettings(mockClient, pseudonymous);
}

it("should send rust cryptoSDK superProperty correctly", async () => {
analytics.setAnonymity(Anonymity.Pseudonymous);

await simulateLogin(false);

expect(mocked(fakePosthog).register.mock.lastCall![0]["cryptoSDK"]).toStrictEqual("Legacy");
});

it("should send Legacy cryptoSDK superProperty correctly", async () => {
analytics.setAnonymity(Anonymity.Pseudonymous);

await simulateLogin(false);

// Super Properties are properties associated with events that are set once and then sent with every capture call.
// They are set using posthog.register
expect(mocked(fakePosthog).register.mock.lastCall![0]["cryptoSDK"]).toStrictEqual("Legacy");
});

it("should send cryptoSDK superProperty when enabling analytics", async () => {
analytics.setAnonymity(Anonymity.Disabled);

await simulateLogin(true, false);

// This initial call is due to the call to register platformSuperProperties
// The important thing is that the cryptoSDK superProperty is not set.
expect(mocked(fakePosthog).register.mock.lastCall![0]).toStrictEqual({});

// switching to pseudonymous should ensure that the cryptoSDK superProperty is set correctly
analytics.setAnonymity(Anonymity.Pseudonymous);
// Super Properties are properties associated with events that are set once and then sent with every capture call.
// They are set using posthog.register
expect(mocked(fakePosthog).register.mock.lastCall![0]["cryptoSDK"]).toStrictEqual("Rust");
});
});
});
Loading