Skip to content

Commit

Permalink
feat: support passing custom getUserId function
Browse files Browse the repository at this point in the history
This is useful when you want to handle your own logic for what you
want to pass as `opaqueUserId`.
jbergstroem committed Jan 23, 2025
1 parent efa5b9e commit 0ee2a5c
Showing 3 changed files with 65 additions and 7 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -86,6 +86,23 @@ Finally, in case you are using banners and want to have further control on the a
</div>
```

### 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: "<YOUR-TOPSORT.JS-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
39 changes: 39 additions & 0 deletions src/detector.custom-getuserid.test.ts
Original file line number Diff line number Diff line change
@@ -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 = `
<div id="product" data-ts-product="product-id-click-1" data-ts-resolved-bid="1247eaae-63a1-4c20-9b52-9efdcdef3095"></div>
`;
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",
},
]);
});
16 changes: 9 additions & 7 deletions src/detector.ts
Original file line number Diff line number Diff line change
@@ -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(),

Check failure on line 230 in src/detector.ts

GitHub Actions / lint

Cannot invoke an object which is possibly 'undefined'.
};
if (type === "Purchase") {
event.items = JSON.parse(node.dataset.tsItems || "[]");

0 comments on commit 0ee2a5c

Please sign in to comment.