Remove redactedCurrentLocation and rely on posthog for DNT
* Redact and pass the redacted url as a property. redactedCurrentLocation might have issues with concurrent events * Remove DNT code and rely on posthog
This commit is contained in:
parent
4c6b0d35ad
commit
726b4497b2
2 changed files with 32 additions and 40 deletions
|
@ -69,7 +69,6 @@ export class PosthogAnalytics {
|
||||||
private anonymity = Anonymity.Anonymous;
|
private anonymity = Anonymity.Anonymous;
|
||||||
private initialised = false;
|
private initialised = false;
|
||||||
private posthog?: PostHog = null;
|
private posthog?: PostHog = null;
|
||||||
private redactedCurrentLocation = null;
|
|
||||||
private enabled = false;
|
private enabled = false;
|
||||||
|
|
||||||
private static _instance = null;
|
private static _instance = null;
|
||||||
|
@ -85,21 +84,20 @@ export class PosthogAnalytics {
|
||||||
this.posthog = posthog;
|
this.posthog = posthog;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init(anonymity: Anonymity) {
|
public init(anonymity: Anonymity) {
|
||||||
this.anonymity = Boolean(navigator.doNotTrack === "1") ? Anonymity.Anonymous : anonymity;
|
|
||||||
|
|
||||||
const posthogConfig = SdkConfig.get()["posthog"];
|
const posthogConfig = SdkConfig.get()["posthog"];
|
||||||
if (posthogConfig) {
|
if (posthogConfig) {
|
||||||
// Update the redacted current location before initialising posthog, as posthog.init triggers
|
|
||||||
// an immediate pageview event which calls the sanitize_properties callback
|
|
||||||
await this.updateRedactedCurrentLocation();
|
|
||||||
|
|
||||||
this.posthog.init(posthogConfig.projectApiKey, {
|
this.posthog.init(posthogConfig.projectApiKey, {
|
||||||
api_host: posthogConfig.apiHost,
|
api_host: posthogConfig.apiHost,
|
||||||
autocapture: false,
|
autocapture: false,
|
||||||
mask_all_text: true,
|
mask_all_text: true,
|
||||||
mask_all_element_attributes: true,
|
mask_all_element_attributes: true,
|
||||||
|
// this is disabled for now as its tricky to sanitize properties of the pageview
|
||||||
|
// event because sanitization requires async crypto calls and the sanitize_properties
|
||||||
|
// callback is synchronous.
|
||||||
|
capture_pageview: false,
|
||||||
sanitize_properties: this.sanitizeProperties.bind(this),
|
sanitize_properties: this.sanitizeProperties.bind(this),
|
||||||
|
respect_dnt: true,
|
||||||
});
|
});
|
||||||
this.initialised = true;
|
this.initialised = true;
|
||||||
this.enabled = true;
|
this.enabled = true;
|
||||||
|
@ -108,20 +106,20 @@ export class PosthogAnalytics {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateRedactedCurrentLocation() {
|
|
||||||
// TODO only calculate this when the location changes as its expensive
|
|
||||||
const { origin, hash, pathname } = window.location;
|
|
||||||
this.redactedCurrentLocation = await getRedactedCurrentLocation(
|
|
||||||
origin, hash, pathname, this.anonymity);
|
|
||||||
}
|
|
||||||
|
|
||||||
private sanitizeProperties(properties: posthog.Properties, _: string): posthog.Properties {
|
private sanitizeProperties(properties: posthog.Properties, _: string): posthog.Properties {
|
||||||
// Sanitize posthog's built in properties which leak PII e.g. url reporting
|
// Callback from posthog to sanitize properties before sending them to the server.
|
||||||
// see utils.js _.info.properties in posthog-js
|
//
|
||||||
|
// Here we sanitize posthog's built in properties which leak PII e.g. url reporting.
|
||||||
|
// See utils.js _.info.properties in posthog-js.
|
||||||
|
|
||||||
// this.redactedCurrentLocation needs to have been updated prior to reaching this point as
|
// Replace the $current_url with a redacted version.
|
||||||
// updating it involves async, which this callback is not
|
// $redacted_current_url is injected by this class earlier in capture(), as its generation
|
||||||
properties['$current_url'] = this.redactedCurrentLocation;
|
// is async and can't be done in this non-async callback.
|
||||||
|
if (!properties['$redacted_current_url']) {
|
||||||
|
console.log("$redacted_current_url not set in sanitizeProperties, will drop $current_url entirely");
|
||||||
|
}
|
||||||
|
properties['$current_url'] = properties['$redacted_current_url'];
|
||||||
|
delete properties['$redacted_current_url'];
|
||||||
|
|
||||||
if (this.anonymity == Anonymity.Anonymous) {
|
if (this.anonymity == Anonymity.Anonymous) {
|
||||||
// drop referrer information for anonymous users
|
// drop referrer information for anonymous users
|
||||||
|
@ -172,7 +170,9 @@ export class PosthogAnalytics {
|
||||||
if (!this.initialised) {
|
if (!this.initialised) {
|
||||||
throw Error("Tried to track event before PoshogAnalytics.init has completed");
|
throw Error("Tried to track event before PoshogAnalytics.init has completed");
|
||||||
}
|
}
|
||||||
await this.updateRedactedCurrentLocation();
|
const { origin, hash, pathname } = window.location;
|
||||||
|
properties['$redacted_current_url'] = await getRedactedCurrentLocation(
|
||||||
|
origin, hash, pathname, this.anonymity);
|
||||||
this.posthog.capture(eventName, properties);
|
this.posthog.capture(eventName, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ describe("PosthogAnalytics", () => {
|
||||||
describe("Initialisation", () => {
|
describe("Initialisation", () => {
|
||||||
it("Should not initialise if config is not set", async () => {
|
it("Should not initialise if config is not set", async () => {
|
||||||
jest.spyOn(SdkConfig, "get").mockReturnValue({});
|
jest.spyOn(SdkConfig, "get").mockReturnValue({});
|
||||||
await analytics.init(Anonymity.Pseudonymous);
|
analytics.init(Anonymity.Pseudonymous);
|
||||||
expect(analytics.isEnabled()).toBe(false);
|
expect(analytics.isEnabled()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -60,21 +60,14 @@ describe("PosthogAnalytics", () => {
|
||||||
apiHost: "bar",
|
apiHost: "bar",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await analytics.init(Anonymity.Pseudonymous);
|
analytics.init(Anonymity.Pseudonymous);
|
||||||
expect(analytics.isInitialised()).toBe(true);
|
expect(analytics.isInitialised()).toBe(true);
|
||||||
expect(analytics.isEnabled()).toBe(true);
|
expect(analytics.isEnabled()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should force anonymous if DNT is enabled", async () => {
|
|
||||||
navigator.doNotTrack = "1";
|
|
||||||
await analytics.init(Anonymity.Pseudonymous);
|
|
||||||
expect(analytics.getAnonymity()).toBe(Anonymity.Anonymous);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Tracking", () => {
|
describe("Tracking", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
navigator.doNotTrack = null;
|
|
||||||
jest.spyOn(SdkConfig, "get").mockReturnValue({
|
jest.spyOn(SdkConfig, "get").mockReturnValue({
|
||||||
posthog: {
|
posthog: {
|
||||||
projectApiKey: "foo",
|
projectApiKey: "foo",
|
||||||
|
@ -84,25 +77,24 @@ describe("PosthogAnalytics", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should pass track() to posthog", async () => {
|
it("Should pass track() to posthog", async () => {
|
||||||
await analytics.init(Anonymity.Pseudonymous);
|
analytics.init(Anonymity.Pseudonymous);
|
||||||
await analytics.trackAnonymousEvent<ITestEvent>("jest_test_event", {
|
await analytics.trackAnonymousEvent<ITestEvent>("jest_test_event", {
|
||||||
foo: "bar",
|
foo: "bar",
|
||||||
});
|
});
|
||||||
expect(fakePosthog.capture.mock.calls[0][0]).toBe("jest_test_event");
|
expect(fakePosthog.capture.mock.calls[0][0]).toBe("jest_test_event");
|
||||||
expect(fakePosthog.capture.mock.calls[0][1]).toEqual({ foo: "bar" });
|
expect(fakePosthog.capture.mock.calls[0][1]["foo"]).toEqual("bar");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should pass trackRoomEvent to posthog", async () => {
|
it("Should pass trackRoomEvent to posthog", async () => {
|
||||||
await analytics.init(Anonymity.Pseudonymous);
|
analytics.init(Anonymity.Pseudonymous);
|
||||||
const roomId = "42";
|
const roomId = "42";
|
||||||
await analytics.trackRoomEvent<IRoomEvent>("jest_test_event", roomId, {
|
await analytics.trackRoomEvent<IRoomEvent>("jest_test_event", roomId, {
|
||||||
foo: "bar",
|
foo: "bar",
|
||||||
});
|
});
|
||||||
expect(fakePosthog.capture.mock.calls[0][0]).toBe("jest_test_event");
|
expect(fakePosthog.capture.mock.calls[0][0]).toBe("jest_test_event");
|
||||||
expect(fakePosthog.capture.mock.calls[0][1]).toEqual({
|
expect(fakePosthog.capture.mock.calls[0][1]["foo"]).toEqual("bar");
|
||||||
foo: "bar",
|
expect(fakePosthog.capture.mock.calls[0][1]["hashedRoomId"])
|
||||||
hashedRoomId: "73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049",
|
.toEqual("73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049");
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should silently not track if not inititalised", async () => {
|
it("Should silently not track if not inititalised", async () => {
|
||||||
|
@ -113,7 +105,7 @@ describe("PosthogAnalytics", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should not track non-anonymous messages if anonymous", async () => {
|
it("Should not track non-anonymous messages if anonymous", async () => {
|
||||||
await analytics.init(Anonymity.Anonymous);
|
analytics.init(Anonymity.Anonymous);
|
||||||
await analytics.trackPseudonymousEvent<ITestEvent>("jest_test_event", {
|
await analytics.trackPseudonymousEvent<ITestEvent>("jest_test_event", {
|
||||||
foo: "bar",
|
foo: "bar",
|
||||||
});
|
});
|
||||||
|
@ -151,14 +143,14 @@ bd75b3e080945674c0351f75e0db33d1e90986fa07b318ea7edf776f5eef38d4`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should identify the user to posthog if pseudonymous", async () => {
|
it("Should identify the user to posthog if pseudonymous", async () => {
|
||||||
await analytics.init(Anonymity.Pseudonymous);
|
analytics.init(Anonymity.Pseudonymous);
|
||||||
await analytics.identifyUser("foo");
|
await analytics.identifyUser("foo");
|
||||||
expect(fakePosthog.identify.mock.calls[0][0])
|
expect(fakePosthog.identify.mock.calls[0][0])
|
||||||
.toBe("2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae");
|
.toBe("2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should not identify the user to posthog if anonymous", async () => {
|
it("Should not identify the user to posthog if anonymous", async () => {
|
||||||
await analytics.init(Anonymity.Anonymous);
|
analytics.init(Anonymity.Anonymous);
|
||||||
await analytics.identifyUser("foo");
|
await analytics.identifyUser("foo");
|
||||||
expect(fakePosthog.identify.mock.calls.length).toBe(0);
|
expect(fakePosthog.identify.mock.calls.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue