From 0ee2a5cd2cd9f5c6f17600cb9d9b2396aed38a92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Bergstr=C3=B6m?= Date: Thu, 23 Jan 2025 15:07:23 +0000 Subject: [PATCH] feat: support passing custom getUserId function This is useful when you want to handle your own logic for what you want to pass as `opaqueUserId`. --- README.md | 17 ++++++++++++ src/detector.custom-getuserid.test.ts | 39 +++++++++++++++++++++++++++ src/detector.ts | 16 ++++++----- 3 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 src/detector.custom-getuserid.test.ts diff --git a/README.md b/README.md index b84c4ae..7d8120e 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,23 @@ Finally, in case you are using banners and want to have further control on the a ``` +### Overriding default opaqueUserId behavior + +If you want to pass your own `opaqueUserId` to the library, you can do so by overiding the `getUserID()` function which is +responsible for retrieving a unique identifier for the user. It should additionally set a new value if none is found. This function should return a string with the `opaqueUserId` passed in the events. + +```javascript +window.TS = { + token: "", + getUserId() { + // globalUserId is the user id you would like to pass to the analytics + // generateAndStoreUserId is a function that generates a new user id and stores it in a cookie/local storage + return globalUserId ?? generateAndStoreUserId(); + }, +}; +``` + +```html # Troubleshooting ## I see `Uncaught Error: Mismatched anonymous define() module` in the browser console diff --git a/src/detector.custom-getuserid.test.ts b/src/detector.custom-getuserid.test.ts new file mode 100644 index 0000000..3213459 --- /dev/null +++ b/src/detector.custom-getuserid.test.ts @@ -0,0 +1,39 @@ +import { expect, test } from "vitest"; + +test("support custom getUserId function", async () => { + window.TS = { + token: "token", + getUserId: () => { + console.log("foo"); + return "custom-user-id"; + }, + }; + const events: any[] = []; + window.addEventListener("topsort", (e) => { + events.push((e as any).detail); + }); + document.body.innerHTML = ` +
+ `; + await import("./detector"); + + document.getElementById("product")?.click(); + expect(events).toMatchObject([ + { + type: "Impression", + page: "/", + product: "product-id-click-1", + bid: "1247eaae-63a1-4c20-9b52-9efdcdef3095", + id: expect.stringMatching(/[\d.a-zA-Z-]+/), + uid: "custom-user-id", + }, + { + type: "Click", + page: "/", + product: "product-id-click-1", + bid: "1247eaae-63a1-4c20-9b52-9efdcdef3095", + id: expect.stringMatching(/[\d.a-zA-Z-]+/), + uid: "custom-user-id", + }, + ]); +}); diff --git a/src/detector.ts b/src/detector.ts index d525d4b..f87a572 100644 --- a/src/detector.ts +++ b/src/detector.ts @@ -1,6 +1,6 @@ -import { type Config, Entity, TopsortClient, Event as TopsortEvent } from "@topsort/sdk"; +import { type Config, type Entity, TopsortClient, type Event as TopsortEvent } from "@topsort/sdk"; import { version } from "../package.json"; -import { ProcessorResult, Queue } from "./queue"; +import { type ProcessorResult, Queue } from "./queue"; import { truncateSet } from "./set"; import { BidStore } from "./store"; @@ -17,7 +17,7 @@ const bidStore = new BidStore("ts-b"); * just be a random number; */ function generateId(): string { - return window.URL.createObjectURL?.(new Blob()).split("/").pop() || Math.random() + ""; + return window.URL.createObjectURL?.(new Blob()).split("/").pop() || `${Math.random()}`; } let globalUserId: string | undefined; @@ -39,7 +39,7 @@ function getUserId(): string { function setUserIdCookie(id: string): void { const cookieName = window.TS.cookieName || "tsuid"; globalUserId = id; - document.cookie = cookieName + "=" + id + ";max-age=31536000"; + document.cookie = `${cookieName}=${id};max-age=31536000`; } function resetUserId(): string { @@ -49,13 +49,15 @@ function resetUserId(): string { } window.TS.setUserId = setUserIdCookie; -window.TS.getUserId = getUserId; +if (typeof window.TS.getUserId !== "function") { + window.TS.getUserId = getUserId; +} window.TS.resetUserId = resetUserId; // Based on https://stackoverflow.com/a/25490531/1413687 function getUserIdCookie(): string | undefined { const cookieName = window.TS.cookieName || "tsuid"; - const regex = new RegExp("(^|;)\\s*" + cookieName + "\\s*=\\s*([^;]+)"); + const regex = new RegExp(`(^|;)\\s*${cookieName}\\s*=\\s*([^;]+)`); return regex.exec(document.cookie)?.pop(); } @@ -225,7 +227,7 @@ function getEvent(type: EventType, node: HTMLElement): ProductEvent { t: Date.now(), page: getPage(), id: generateId(), - uid: getUserId(), + uid: window.TS.getUserId() || getUserId(), }; if (type === "Purchase") { event.items = JSON.parse(node.dataset.tsItems || "[]");