Skip to content

Core: use same transaction ID for twin ad units #10962

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 7, 2024
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: 1 addition & 5 deletions modules/craftBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {getBidRequest, logError} from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
import {auctionManager} from '../src/auctionManager.js';
import {find, includes} from '../src/polyfill.js';
import {getStorageManager} from '../src/storageManager.js';
import {ajax} from '../src/ajax.js';
Expand Down Expand Up @@ -186,12 +185,9 @@ function bidToTag(bid) {
if (keywords.length) {
tag.keywords = keywords;
}
// TODO: why does this need to iterate through every ad unit?
let adUnit = find(auctionManager.getAdUnits(), au => bid.transactionId === au.transactionId);
if (adUnit && adUnit.mediaTypes && adUnit.mediaTypes.banner) {
if (bid.mediaTypes?.banner) {
tag.ad_types.push(BANNER);
}

if (tag.ad_types.length === 0) {
delete tag.ad_types;
}
Expand Down
5 changes: 1 addition & 4 deletions modules/goldbachBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
import {config} from '../src/config.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {ADPOD, BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
import {auctionManager} from '../src/auctionManager.js';
import {find, includes} from '../src/polyfill.js';
import {INSTREAM, OUTSTREAM} from '../src/video.js';
import {hasPurpose1Consent} from '../src/utils/gpdr.js';
Expand Down Expand Up @@ -882,9 +881,7 @@ function bidToTag(bid) {
tag['banner_frameworks'] = bid.params.frameworks;
}

// TODO: why does this need to iterate through every ad unit?
let adUnit = find(auctionManager.getAdUnits(), au => bid.transactionId === au.transactionId);
if (adUnit && adUnit.mediaTypes && adUnit.mediaTypes.banner) {
if (bid.mediaTypes?.banner) {
tag.ad_types.push(BANNER);
}

Expand Down
14 changes: 5 additions & 9 deletions modules/hybridBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {_map, deepAccess, isArray, logWarn} from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {auctionManager} from '../src/auctionManager.js';
import {BANNER, VIDEO} from '../src/mediaTypes.js';
import {Renderer} from '../src/Renderer.js';
import {find} from '../src/polyfill.js';
Expand Down Expand Up @@ -95,16 +94,13 @@ function buildBid(bidData) {
bid.vastXml = bidData.content;
bid.mediaType = VIDEO;

// TODO: why does this need to iterate through every ad unit?
let adUnit = find(auctionManager.getAdUnits(), function (unit) {
return unit.transactionId === bidData.transactionId;
});
const video = bidData.mediaTypes?.video;

if (adUnit) {
bid.width = adUnit.mediaTypes.video.playerSize[0][0];
bid.height = adUnit.mediaTypes.video.playerSize[0][1];
if (video) {
bid.width = video.playerSize[0][0];
bid.height = video.playerSize[0][1];

if (adUnit.mediaTypes.video.context === 'outstream') {
if (video.context === 'outstream') {
bid.renderer = createRenderer(bid);
}
}
Expand Down
5 changes: 1 addition & 4 deletions modules/mediafuseBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {Renderer} from '../src/Renderer.js';
import {config} from '../src/config.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {ADPOD, BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
import {auctionManager} from '../src/auctionManager.js';
import {find, includes} from '../src/polyfill.js';
import {INSTREAM, OUTSTREAM} from '../src/video.js';
import {getStorageManager} from '../src/storageManager.js';
Expand Down Expand Up @@ -870,9 +869,7 @@ function bidToTag(bid) {
tag['banner_frameworks'] = bid.params.frameworks;
}

// TODO: why does this need to iterate through every ad unit?
let adUnit = find(auctionManager.getAdUnits(), au => bid.transactionId === au.transactionId);
if (adUnit && adUnit.mediaTypes && adUnit.mediaTypes.banner) {
if (bid.mediaTypes?.banner) {
tag.ad_types.push(BANNER);
}

Expand Down
1 change: 1 addition & 0 deletions modules/prebidServerBidAdapter/ortbConverter.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ const PBS_CONVERTER = ortbConverter({
src: CONSTANTS.S2S.SRC,
bidId: bidRequest ? (bidRequest.bidId || bidRequest.bid_Id) : null,
transactionId: context.adUnit.transactionId,
adUnitId: context.adUnit.adUnitId,
auctionId: context.bidderRequest.auctionId,
}), bidResponse),
adUnit: context.adUnit.code
Expand Down
6 changes: 3 additions & 3 deletions modules/priceFloors.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ const getHostname = (() => {
})();

// First look into bidRequest!
function getGptSlotFromAdUnit(transactionId, {index = auctionManager.index} = {}) {
const adUnit = index.getAdUnit({transactionId});
function getGptSlotFromAdUnit(adUnitId, {index = auctionManager.index} = {}) {
const adUnit = index.getAdUnit({adUnitId});
const isGam = deepAccess(adUnit, 'ortb2Imp.ext.data.adserver.name') === 'gam';
return isGam && adUnit.ortb2Imp.ext.data.adserver.adslot;
}
Expand All @@ -111,7 +111,7 @@ export let fieldMatchingFunctions = {
[SYN_FIELD]: () => '*',
'size': (bidRequest, bidResponse) => parseGPTSingleSizeArray(bidResponse.size) || '*',
'mediaType': (bidRequest, bidResponse) => bidResponse.mediaType || 'banner',
'gptSlot': (bidRequest, bidResponse) => getGptSlotFromAdUnit((bidRequest || bidResponse).transactionId) || getGptSlotInfoForAdUnitCode(getAdUnitCode(bidRequest, bidResponse)).gptSlot,
'gptSlot': (bidRequest, bidResponse) => getGptSlotFromAdUnit((bidRequest || bidResponse).adUnitId) || getGptSlotInfoForAdUnitCode(getAdUnitCode(bidRequest, bidResponse)).gptSlot,
'domain': getHostname,
'adUnitCode': (bidRequest, bidResponse) => getAdUnitCode(bidRequest, bidResponse)
}
Expand Down
17 changes: 6 additions & 11 deletions modules/voxBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import {_map, deepAccess, isArray, logWarn} from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, VIDEO} from '../src/mediaTypes.js';
import {find} from '../src/polyfill.js';
import {auctionManager} from '../src/auctionManager.js';
import {Renderer} from '../src/Renderer.js';
import {config} from '../src/config.js'
import {config} from '../src/config.js';

/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
Expand Down Expand Up @@ -99,17 +98,13 @@ function buildBid(bidData) {
if (bidData.placement === 'video') {
bid.vastXml = bidData.content;
bid.mediaType = VIDEO;
const video = bidData.mediaTypes?.video;

// TODO: why does this need to iterate through every ad unit?
let adUnit = find(auctionManager.getAdUnits(), function (unit) {
return unit.transactionId === bidData.transactionId;
});

if (adUnit) {
bid.width = adUnit.mediaTypes.video.playerSize[0][0];
bid.height = adUnit.mediaTypes.video.playerSize[0][1];
if (video) {
bid.width = video.playerSize[0][0];
bid.height = video.playerSize[0][1];

if (adUnit.mediaTypes.video.context === 'outstream') {
if (video.context === 'outstream') {
bid.renderer = createRenderer(bid);
}
}
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/adapterManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ function getBids({bidderCode, auctionId, bidderRequestId, adUnits, src, metrics}
bids.push(Object.assign({}, bid, {
adUnitCode: adUnit.code,
transactionId: adUnit.transactionId,
adUnitId: adUnit.adUnitId,
sizes: deepAccess(mediaTypes, 'banner.sizes') || deepAccess(mediaTypes, 'video.playerSize') || [],
bidId: bid.bid_id || getUniqueIdentifierStr(),
bidderRequestId,
Expand Down
4 changes: 2 additions & 2 deletions src/auction.js
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a
}

function addWinningBid(winningBid) {
const winningAd = adUnits.find(adUnit => adUnit.transactionId === winningBid.transactionId);
const winningAd = adUnits.find(adUnit => adUnit.adUnitId === winningBid.adUnitId);
_winningBids = _winningBids.concat(winningBid);
callBurl(winningBid);
adapterManager.callBidWonBidder(winningBid.adapterCode || winningBid.bidder, winningBid, adUnits);
Expand Down Expand Up @@ -557,7 +557,7 @@ function tryAddVideoBid(auctionInstance, bidResponse, afterBidAdded, {index = au
const videoMediaType = deepAccess(
index.getMediaTypes({
requestId: bidResponse.originalRequestId || bidResponse.requestId,
transactionId: bidResponse.transactionId
adUnitId: bidResponse.adUnitId
}), 'video');
const context = videoMediaType && deepAccess(videoMediaType, 'context');
const useCacheKey = videoMediaType && deepAccess(videoMediaType, 'useCacheKey');
Expand Down
18 changes: 9 additions & 9 deletions src/auctionIndex.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
* @typedef {Object} AuctionIndex
*
* @property {function({ auctionId: * }): *} getAuction Returns auction instance for `auctionId`
* @property {function({ transactionId: * }): *} getAdUnit Returns `adUnit` object for `transactionId`.
* @property {function({ adUnitId: * }): *} getAdUnit Returns `adUnit` object for `transactionId`.
* You should prefer `getMediaTypes` for looking up bid media types.
* @property {function({ transactionId: *, requestId: * }): *} getMediaTypes Returns mediaTypes object from bidRequest (through `requestId`) falling back to the adUnit (through `transactionId`).
* @property {function({ adUnitId: *, requestId: * }): *} getMediaTypes Returns mediaTypes object from bidRequest (through `requestId`) falling back to the adUnit (through `transactionId`).
* The bidRequest is given precedence because its mediaTypes can differ from the adUnit's (if bidder-specific labels are in use).
* Bids that have no associated request do not have labels either, and use the adUnit's mediaTypes.
* @property {function({ requestId: *, bidderRequestId: * }): *} getBidderRequest Returns bidderRequest that matches both requestId and bidderRequestId (if either or both are provided).
Expand All @@ -25,21 +25,21 @@ export function AuctionIndex(getAuctions) {
.find(auction => auction.getAuctionId() === auctionId);
}
},
getAdUnit({transactionId}) {
if (transactionId != null) {
getAdUnit({adUnitId}) {
if (adUnitId != null) {
return getAuctions()
.flatMap(a => a.getAdUnits())
.find(au => au.transactionId === transactionId);
.find(au => au.adUnitId === adUnitId);
}
},
getMediaTypes({transactionId, requestId}) {
getMediaTypes({adUnitId, requestId}) {
if (requestId != null) {
const req = this.getBidRequest({requestId});
if (req != null && (transactionId == null || req.transactionId === transactionId)) {
if (req != null && (adUnitId == null || req.adUnitId === adUnitId)) {
return req.mediaTypes;
}
} else if (transactionId != null) {
const au = this.getAdUnit({transactionId});
} else if (adUnitId != null) {
const au = this.getAdUnit({adUnitId});
if (au != null) {
return au.mediaTypes;
}
Expand Down
26 changes: 15 additions & 11 deletions src/bidfactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,23 @@ import { getUniqueIdentifierStr } from './utils.js';
dealId,
priceKeyString;
*/
function Bid(statusCode, {src = 'client', bidder = '', bidId, transactionId, auctionId} = {}) {
function Bid(statusCode, {src = 'client', bidder = '', bidId, transactionId, adUnitId, auctionId} = {}) {
var _bidSrc = src;
var _statusCode = statusCode || 0;

this.bidderCode = bidder;
this.width = 0;
this.height = 0;
this.statusMessage = _getStatus();
this.adId = getUniqueIdentifierStr();
this.requestId = bidId;
this.transactionId = transactionId;
this.auctionId = auctionId;
this.mediaType = 'banner';
this.source = _bidSrc;
Object.assign(this, {
bidderCode: bidder,
width: 0,
height: 0,
statusMessage: _getStatus(),
adId: getUniqueIdentifierStr(),
requestId: bidId,
transactionId,
adUnitId,
auctionId,
mediaType: 'banner',
source: _bidSrc
})

function _getStatus() {
switch (_statusCode) {
Expand Down Expand Up @@ -57,6 +60,7 @@ function Bid(statusCode, {src = 'client', bidder = '', bidId, transactionId, auc
bidder: this.bidderCode,
bidId: this.requestId,
transactionId: this.transactionId,
adUnitId: this.adUnitId,
auctionId: this.auctionId
}
};
Expand Down
26 changes: 19 additions & 7 deletions src/prebid.js
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,8 @@ export const startAuction = hook('async', function ({ bidsBackHandler, timeout:
defer.resolve({bids, timedOut, auctionId})
}

const tids = {};

/*
* for a given adunit which supports a set of mediaTypes
* and a given bidder which supports a set of mediaTypes
Expand All @@ -567,15 +569,18 @@ export const startAuction = hook('async', function ({ bidsBackHandler, timeout:
const bidderRegistry = adapterManager.bidderRegistry;

const bidders = allBidders.filter(bidder => !s2sBidders.has(bidder));

const tid = adUnit.ortb2Imp?.ext?.tid || generateUUID();
adUnit.transactionId = tid;
adUnit.adUnitId = generateUUID();
const tid = adUnit.ortb2Imp?.ext?.tid;
if (tid) {
if (tids.hasOwnProperty(adUnit.code)) {
logWarn(`Multiple distinct ortb2Imp.ext.tid were provided for twin ad units '${adUnit.code}'`)
} else {
tids[adUnit.code] = tid;
}
}
if (ttlBuffer != null && !adUnit.hasOwnProperty('ttlBuffer')) {
adUnit.ttlBuffer = ttlBuffer;
}
// Populate ortb2Imp.ext.tid with transactionId. Specifying a transaction ID per item in the ortb impression array, lets multiple transaction IDs be transmitted in a single bid request.
deepSetValue(adUnit, 'ortb2Imp.ext.tid', tid);

bidders.forEach(bidder => {
const adapter = bidderRegistry[bidder];
const spec = adapter && adapter.getSpec && adapter.getSpec();
Expand All @@ -594,11 +599,18 @@ export const startAuction = hook('async', function ({ bidsBackHandler, timeout:
});
adunitCounter.incrementRequestsCounter(adUnit.code);
});

if (!adUnits || adUnits.length === 0) {
logMessage('No adUnits configured. No bids requested.');
auctionDone();
} else {
adUnits.forEach(au => {
const tid = au.ortb2Imp?.ext?.tid || tids[au.code] || generateUUID();
if (!tids.hasOwnProperty(au.code)) {
tids[au.code] = tid;
}
au.transactionId = tid;
deepSetValue(au, 'ortb2Imp.ext.tid', tid);
});
const auction = auctionManager.createAuction({
adUnits,
adUnitCodes,
Expand Down
Loading