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
1 change: 1 addition & 0 deletions lib/addons/prebid/analytics.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,5 +130,6 @@ Each sampled auction sends an `optable.prebid.auction` event with, among others:
`bidderRequests` (with per-bid `status`, `cpm`, `size`, `splitTestAssignment`),
`optableMatchers`, `optableSources`, `optableTargetingDone`, `liveintentEIDs`
(with per-bidder `hasLiEids` and `liSources`), `bidWon`, `missed`,
`adUnitCode` (first bid's ad unit), `totalBids` (received bids with a `cpm`),
`url`, `tenant`, `prebidjsVersion`, `sessionDepth`, `pageAuctionsCount`,
`originSlug`, and the parsed `userAgent`/`device`.
63 changes: 62 additions & 1 deletion lib/addons/prebid/analytics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ describe("OptablePrebidAnalytics", () => {

expect(result).toMatchObject({
auctionId: "auction-123",
adUnitCode: "unknown",
adUnitCode: "ad-unit-1",
totalRequests: 1,
optableMatchers: ["matcher1"],
optableSources: ["source1"],
Expand Down Expand Up @@ -1051,6 +1051,67 @@ describe("OptablePrebidAnalytics", () => {
expect(payload.bidderRequests[0].status).toBe("RECEIVED");
});

const bidCountAuction = (bidsPerRequest: (string | undefined)[][], bidsReceived: any[]) => ({
auctionId: "auction-bid-count",
bidderRequests: bidsPerRequest.map((adUnits, i) => ({
bidderCode: `bidder${i + 1}`,
bidderRequestId: `req-${i + 1}`,
ortb2: { site: { domain: "example.com" }, user: { eids: [] } },
bids: adUnits.map((adUnitCode, j) => ({ bidId: `bid-${i + 1}-${j + 1}`, adUnitCode })),
})),
bidsReceived,
noBids: [],
timeoutBids: [],
});

it("reports totalBids: 0 when no bids are received", async () => {
const payload = await analytics.toWitness(bidCountAuction([["ad-unit-1"], ["ad-unit-1"]], []), []);
expect(payload.totalBids).toBe(0);
});

it("reports totalBids: 1 for a single received bid", async () => {
const payload = await analytics.toWitness(
bidCountAuction([["ad-unit-1"], ["ad-unit-1"]], [{ requestId: "bid-2-1", cpm: 1.2 }]),
[]
);
expect(payload.totalBids).toBe(1);
});

it("counts received bids with a cpm across bidder requests", async () => {
const payload = await analytics.toWitness(
bidCountAuction(
[
["ad-unit-1", "ad-unit-2"],
["ad-unit-1", "ad-unit-2"],
],
[
{ requestId: "bid-1-1", cpm: 1.2 },
{ requestId: "bid-1-2", cpm: 0 },
{ requestId: "bid-2-1", cpm: 0.8 },
{ requestId: "bid-2-2", cpm: null },
{ requestId: "bid-unrequested", cpm: 3 },
]
),
[]
);
expect(payload.totalBids).toBe(3);
});

it("sets the top-level adUnitCode to the first bid's ad unit", async () => {
const payload = await analytics.toWitness(bidCountAuction([["ad-unit-1", "ad-unit-2"], ["ad-unit-3"]], []), []);
expect(payload.adUnitCode).toBe("ad-unit-1");
});

it("skips bids without an ad unit when setting the top-level adUnitCode", async () => {
const payload = await analytics.toWitness(bidCountAuction([[undefined], [undefined, "ad-unit-2"]], []), []);
expect(payload.adUnitCode).toBe("ad-unit-2");
});

it("falls back to an 'unknown' adUnitCode when there are no bids", async () => {
const payload = await analytics.toWitness(bidCountAuction([[], []], []), []);
expect(payload.adUnitCode).toBe("unknown");
});

it("should extract splitTestAssignment from bidsReceived", async () => {
const event = {
auctionId: "auction-split-test",
Expand Down
3 changes: 3 additions & 0 deletions lib/addons/prebid/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -447,8 +447,10 @@ class OptablePrebidAnalytics {

requests.forEach((request: any) => {
request.bids.forEach((bid: any) => {
if (adUnitCode === "unknown" && bid.adUnitCode) adUnitCode = bid.adUnitCode;
Comment thread
mosherBT marked this conversation as resolved.
const bidReceived = bidsReceivedMap.get(bid.bidId);
if (bidReceived) {
if (bidReceived.cpm != null) totalBids += 1;
bid.status = STATUS.RECEIVED;
bid.cpm = bidReceived.cpm;
bid.size = `${bidReceived.width}x${bidReceived.height}`;
Expand Down Expand Up @@ -479,6 +481,7 @@ class OptablePrebidAnalytics {
auctionId,
adUnitCode,
totalRequests: bidderRequests.length,
totalBids,
optableSampling: this.config.samplingRate || 1,
// Processor schema declares optableTargetingDone as STRING and checks IN ('1','true').
// Send '1'/'0' so Spark reads a predictable string regardless of matcher count.
Expand Down