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
22 changes: 9 additions & 13 deletions lib/addons/prebid/analytics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ describe("OptablePrebidAnalytics", () => {
expect(result.liveintentEIDs).toEqual([]);
});

it("reports LiveIntent and Optable EIDs side by side, before the source dedupe", async () => {
it("reports LiveIntent and Optable EIDs side by side", async () => {
const result = await analytics.toWitness(
liveintentAuction([
{
Expand Down Expand Up @@ -2369,12 +2369,12 @@ describe("OptablePrebidAnalytics", () => {
});
});

describe("EID deduplication", () => {
describe("EID sources and matchers across ext.eids and eids", () => {
beforeEach(() => {
analytics = new OptablePrebidAnalytics(mockOptableInstance);
});

it("should deduplicate EIDs by source when both ext.eids and eids contain same source", async () => {
it("dedupes sources but keeps every Optable matcher when ext.eids and eids share a source", async () => {
const auctionEndEvent = {
auctionId: "auction-dedup",
bidderRequests: [
Expand All @@ -2400,10 +2400,9 @@ describe("OptablePrebidAnalytics", () => {

const result = await analytics.toWitness(auctionEndEvent, []);

// Should only have one source since they have the same source value
// One source, but both matchers that resolved it
Comment thread
mosherBT marked this conversation as resolved.
Resolved
expect(result.optableSources).toEqual(["optable.co"]);
// The last occurrence (from eids, not ext.eids) should win due to deduplication
expect(result.optableMatchers).toEqual(["matcher2"]);
expect(result.optableMatchers).toEqual(["matcher1", "matcher2"]);
});

it("should preserve EIDs with different sources from ext.eids and eids", async () => {
Expand Down Expand Up @@ -2528,7 +2527,7 @@ describe("OptablePrebidAnalytics", () => {
expect(result.optableMatchers).toEqual(["matcher1"]);
});

it("should deduplicate multiple EIDs with same source across different locations", async () => {
it("dedupes sources but keeps every Optable matcher across several shared sources", async () => {
const auctionEndEvent = {
auctionId: "auction-multi-dedup",
bidderRequests: [
Expand Down Expand Up @@ -2560,15 +2559,14 @@ describe("OptablePrebidAnalytics", () => {

const result = await analytics.toWitness(auctionEndEvent, []);

// Should only have one optable source and matcher due to deduplication
expect(result.optableSources).toEqual(["optable.co"]);
expect(result.optableMatchers).toEqual(["matcher2"]);
expect(result.optableMatchers).toEqual(["matcher1", "matcher2"]);
// Non-optable EIDs should be filtered out
expect(result.optableMatchers).not.toContain("other1");
expect(result.optableMatchers).not.toContain("other2");
});

it("should handle trackAuctionEnd with duplicate EIDs in ext.eids and eids", async () => {
it("keeps every Optable matcher through trackAuctionEnd when ext.eids and eids share a source", async () => {
const event = {
auctionId: "auction-track-dedup",
timeout: 3000,
Expand Down Expand Up @@ -2599,10 +2597,8 @@ describe("OptablePrebidAnalytics", () => {
expect(storedAuction).toBeDefined();

const payload = await analytics.toWitness(storedAuction.auctionEnd, []);
// Should only have one source after deduplication
expect(payload.optableSources).toEqual(["optable.co"]);
// The last matcher should win
expect(payload.optableMatchers).toEqual(["matcher2"]);
expect(payload.optableMatchers).toEqual(["matcher1", "matcher2"]);
});
});

Expand Down
14 changes: 6 additions & 8 deletions lib/addons/prebid/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,20 +429,18 @@ class OptablePrebidAnalytics {
const requests = bidderRequests.map((br: any) => {
const { bidderCode, bidderRequestId, bids = [] } = br;
const domain = br.ortb2.site?.domain ?? "unknown";
const allEids = [...(br.ortb2.user?.ext?.eids ?? []), ...(br.ortb2.user?.eids ?? [])];
// Deduplicate EIDs by source and inserter, so another inserter's EID can't hide Optable's
const eids = Array.from(new Map(allEids.map((eid: any) => [`${eid.source}|${eid.inserter}`, eid])).values());
const eids = [...(br.ortb2.user?.ext?.eids ?? []), ...(br.ortb2.user?.eids ?? [])];

// Optable EIDs
const optableEIDS = eids.filter((e: { inserter: string }) => e.inserter === "optable.co");
const optableMatchers = [...new Set(optableEIDS.map((e: any) => e.matcher).filter(Boolean))];
const optableSources = [...new Set(optableEIDS.map((e: any) => e.source).filter(Boolean))];

// Competitor EIDs, matched before the dedupe so a same-source EID can't hide them
const liSources = sourcesOf(allEids.filter(COMPETITOR_EIDS.liveintent));
const iiqSources = sourcesOf(allEids.filter(COMPETITOR_EIDS.intentiq));
const gcSources = sourcesOf(allEids.filter(COMPETITOR_EIDS.growthcode));
const tuSources = sourcesOf(allEids.filter(COMPETITOR_EIDS.transunion));
// Competitor EIDs
const liSources = sourcesOf(eids.filter(COMPETITOR_EIDS.liveintent));
const iiqSources = sourcesOf(eids.filter(COMPETITOR_EIDS.intentiq));
const gcSources = sourcesOf(eids.filter(COMPETITOR_EIDS.growthcode));
const tuSources = sourcesOf(eids.filter(COMPETITOR_EIDS.transunion));

device = br.ortb2.device;

Expand Down