Skip to content
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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1227,6 +1227,12 @@ import { identifyAndTokenize } from "@optable/web-sdk/lib/dist/core/identify-tok
window.optable.identifyAndTokenize = (id) => identifyAndTokenize(sdk, id, { isControlGroup: () => isControlGroup });
```

`options` also controls the cache write: `cacheKey` (default `OPTABLE_RESOLVED`), and `maxUidsPerEid` and `nonOverridingMatchers`, both forwarded to [`mergeCache`](lib/core/eid-cache.md). Pass `nonOverridingMatchers` when tokenize runs alongside a targeting call that skipped the first-party matcher, so a first-party EID does not displace a third-party one already held on the same source:

```javascript
identifyAndTokenize(sdk, id, { isControlGroup: () => isControlGroup, nonOverridingMatchers: ["mysite.com"] });
```

The returned `{ merged, staleUid2s }` (null when skipped) lets the caller chain UID2 refreshes on the merged cache.

## Passport and Visitor ID
Expand Down
37 changes: 37 additions & 0 deletions lib/core/identify-tokenize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,43 @@ describe("identifyAndTokenize - failure paths and options", () => {
expect(eids[0].uids).toHaveLength(1);
});

it("keeps a held third-party EID when tokenize returns a nonOverridingMatchers EID on its source", async () => {
localStorage.setItem(
"OPTABLE_RESOLVED",
JSON.stringify({
ortb2: { user: { eids: [{ source: "liveramp.com", matcher: "3p.example", uids: [{ id: "third-party" }] }] } },
})
);
const { sdk, tokenize } = makeSdk();
tokenize.mockResolvedValue({
user: { eids: [{ source: "liveramp.com", matcher: "acme.com", uids: [{ id: "first-party" }] }] },
} as any);

await identifyAndTokenize(sdk, "abc123", { nonOverridingMatchers: ["acme.com"] });

expect(cachedEids().map((e: any) => e.uids[0].id)).toEqual(["third-party"]);
});

it("still caches a nonOverridingMatchers EID on a source nothing holds", async () => {
localStorage.setItem(
"OPTABLE_RESOLVED",
JSON.stringify({
ortb2: { user: { eids: [{ source: "id5-sync.com", matcher: "3p.example", uids: [{ id: "third-party" }] }] } },
})
);
const { sdk, tokenize } = makeSdk();
tokenize.mockResolvedValue({
user: { eids: [{ source: "liveramp.com", matcher: "acme.com", uids: [{ id: "first-party" }] }] },
} as any);

await identifyAndTokenize(sdk, "abc123", { nonOverridingMatchers: ["acme.com"] });

expect(cachedEids().map((e: any) => [e.source, e.uids[0].id])).toEqual([
["id5-sync.com", "third-party"],
["liveramp.com", "first-party"],
]);
});

it("returns the stale UID2s the merge found, for the refresh loop to chain on", async () => {
localStorage.setItem(
"OPTABLE_RESOLVED",
Expand Down
6 changes: 5 additions & 1 deletion lib/core/identify-tokenize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type IdentifyAndTokenizeOptions = {
isControlGroup?: () => boolean;
cacheKey?: string;
maxUidsPerEid?: number;
nonOverridingMatchers?: string[];
};

type IdentifyAndTokenizeResult = {
Expand Down Expand Up @@ -125,7 +126,10 @@ export async function identifyAndTokenize(
}

// mergeCache takes cache format, and replaceCache is idempotent.
const result = mergeCache(replaceCache(asCache), cached, { maxUidsPerEid: options.maxUidsPerEid });
const result = mergeCache(replaceCache(asCache), cached, {
maxUidsPerEid: options.maxUidsPerEid,
nonOverridingMatchers: options.nonOverridingMatchers,
});
localStorage.setItem(cacheKey, JSON.stringify(result.merged));
sendTargetingUpdateEvent(sdk.dcn, result.merged as TargetingResponse);

Expand Down