Skip to content
Closed
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
3 changes: 2 additions & 1 deletion lib/addons/prebid/analytics.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ Returns the `OptablePrebidAnalytics` instance, or `null` when no Prebid instance

Each sampled auction sends an `optable.prebid.auction` event with, among others:
`bidderRequests` (with per-bid `status`, `cpm`, `size`, `splitTestAssignment`),
`optableMatchers`, `optableSources`, `optableTargetingDone`, `bidWon`, `missed`,
`optableMatchers`, `optableSources`, `optableTargetingDone`, `liveintentEIDs`
(with per-bidder `hasLiEids` and `liSources`), `bidWon`, `missed`,
`url`, `tenant`, `prebidjsVersion`, `sessionDepth`, `pageAuctionsCount`,
`originSlug`, and the parsed `userAgent`/`device`.
69 changes: 69 additions & 0 deletions lib/addons/prebid/analytics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,75 @@ describe("OptablePrebidAnalytics", () => {

expect(result.optableTargetingDone).toBe("1");
});

const liveintentAuction = (bidderRequests: any[]) => ({
auctionId: "auction-liveintent",
bidderRequests: bidderRequests.map((user, i) => ({
bidderCode: `bidder${i + 1}`,
bidderRequestId: `req-${i + 1}`,
ortb2: { site: { domain: "example.com" }, user },
bids: [],
})),
bidsReceived: [],
noBids: [],
timeoutBids: [],
});

it("reports LiveIntent EIDs per bidder and at the top level", async () => {
const result = await analytics.toWitness(
liveintentAuction([
{
eids: [
{ source: "uidapi.com", uids: [{ id: "a", ext: { provider: "liveintent.com" } }] },
{ source: "openx.net", uids: [{ id: "b", ext: { provider: "liveintent.com" } }] },
],
},
{ eids: [{ source: "uidapi.com", uids: [{ id: "a", ext: { provider: "liveintent.com" } }] }] },
]),
[]
);

expect(result.bidderRequests[0]).toMatchObject({ hasLiEids: true, liSources: ["uidapi.com", "openx.net"] });
expect(result.bidderRequests[1]).toMatchObject({ hasLiEids: true, liSources: ["uidapi.com"] });
expect(result.liveintentEIDs).toEqual(["uidapi.com", "openx.net"]);
});

it("reports no LiveIntent EIDs when none carry the LiveIntent provider", async () => {
const result = await analytics.toWitness(
liveintentAuction([
{ eids: [{ source: "adsrvr.org", uids: [{ id: "a", ext: { provider: "example.com" } }] }] },
{},
]),
[]
);

expect(result.bidderRequests[0]).toMatchObject({ hasLiEids: false, liSources: [] });
expect(result.bidderRequests[1]).toMatchObject({ hasLiEids: false, liSources: [] });
expect(result.liveintentEIDs).toEqual([]);
});

it("reports LiveIntent and Optable EIDs side by side, before the source dedupe", async () => {
const result = await analytics.toWitness(
liveintentAuction([
{
ext: { eids: [{ source: "uidapi.com", uids: [{ id: "li", ext: { provider: "liveintent.com" } }] }] },
eids: [
{ inserter: "optable.co", matcher: "uid2", source: "uidapi.com", uids: [{ id: "o" }] },
{ inserter: "optable.co", matcher: "id5", source: "id5-sync.com", uids: [{ id: "o2" }] },
],
},
]),
[]
);

expect(result.bidderRequests[0]).toMatchObject({
optableSources: ["uidapi.com", "id5-sync.com"],
hasLiEids: true,
liSources: ["uidapi.com"],
});
expect(result.optableSources).toEqual(["uidapi.com", "id5-sync.com"]);
expect(result.liveintentEIDs).toEqual(["uidapi.com"]);
});
});

describe("trackAuctionEnd", () => {
Expand Down
9 changes: 9 additions & 0 deletions lib/addons/prebid/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ class OptablePrebidAnalytics {

const oMatchersSet = new Set();
const oSourcesSet = new Set();
const lSourcesSet = new Set();
let adUnitCode: string = "unknown";
let totalBids = 0;
let device = null;
Expand All @@ -400,6 +401,10 @@ class OptablePrebidAnalytics {
const optableMatchers = [...new Set(optableEIDS.map((e: any) => e.matcher).filter(Boolean))];
const optableSources = [...new Set(optableEIDS.map((e: any) => e.source).filter(Boolean))];

// LiveIntent EIDs, matched before the dedupe so a same-source EID can't hide them
const liveintentEIDS = allEids.filter((e: any) => e.uids?.some((u: any) => u.ext?.provider === "liveintent.com"));
const liSources = [...new Set(liveintentEIDS.map((e: any) => e.source).filter(Boolean))];

device = br.ortb2.device;

return {
Expand All @@ -409,6 +414,8 @@ class OptablePrebidAnalytics {
optableTargetingDone: optableEIDS.length > 0,
optableMatchers,
optableSources,
hasLiEids: liveintentEIDS.length > 0,
liSources,
status: STATUS.REQUESTED,
bids: bids.map(
(b: {
Expand Down Expand Up @@ -465,6 +472,7 @@ class OptablePrebidAnalytics {
bidderRequests: requests.map((br: any) => {
br.optableMatchers.forEach((m: unknown) => oMatchersSet.add(m));
br.optableSources.forEach((s: unknown) => oSourcesSet.add(s));
br.liSources.forEach((s: unknown) => lSourcesSet.add(s));

return br;
}),
Expand All @@ -478,6 +486,7 @@ class OptablePrebidAnalytics {
optableTargetingDone: oMatchersSet.size > 0 || oSourcesSet.size > 0 ? "1" : "0",
optableMatchers: Array.from(oMatchersSet),
optableSources: Array.from(oSourcesSet),
liveintentEIDs: Array.from(lSourcesSet),
bidWon: bidWonEvents.map((e) => ({
bidderCode: e.bidderCode,
adUnitCode: e.adUnitCode,
Expand Down