Skip to content

Commit 8931850

Browse files
authored
Core: use same transaction ID for twin ad units (#10962)
* swith transactionId to adUnitId * use same TID for ad units with the same code * fix appnexus clones
1 parent f380912 commit 8931850

21 files changed

+242
-132
lines changed

modules/craftBidAdapter.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {getBidRequest, logError} from '../src/utils.js';
22
import {registerBidder} from '../src/adapters/bidderFactory.js';
33
import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
4-
import {auctionManager} from '../src/auctionManager.js';
54
import {find, includes} from '../src/polyfill.js';
65
import {getStorageManager} from '../src/storageManager.js';
76
import {ajax} from '../src/ajax.js';
@@ -186,12 +185,9 @@ function bidToTag(bid) {
186185
if (keywords.length) {
187186
tag.keywords = keywords;
188187
}
189-
// TODO: why does this need to iterate through every ad unit?
190-
let adUnit = find(auctionManager.getAdUnits(), au => bid.transactionId === au.transactionId);
191-
if (adUnit && adUnit.mediaTypes && adUnit.mediaTypes.banner) {
188+
if (bid.mediaTypes?.banner) {
192189
tag.ad_types.push(BANNER);
193190
}
194-
195191
if (tag.ad_types.length === 0) {
196192
delete tag.ad_types;
197193
}

modules/goldbachBidAdapter.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import {
1919
import {config} from '../src/config.js';
2020
import {registerBidder} from '../src/adapters/bidderFactory.js';
2121
import {ADPOD, BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
22-
import {auctionManager} from '../src/auctionManager.js';
2322
import {find, includes} from '../src/polyfill.js';
2423
import {INSTREAM, OUTSTREAM} from '../src/video.js';
2524
import {hasPurpose1Consent} from '../src/utils/gpdr.js';
@@ -882,9 +881,7 @@ function bidToTag(bid) {
882881
tag['banner_frameworks'] = bid.params.frameworks;
883882
}
884883

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

modules/hybridBidAdapter.js

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {_map, deepAccess, isArray, logWarn} from '../src/utils.js';
22
import {registerBidder} from '../src/adapters/bidderFactory.js';
3-
import {auctionManager} from '../src/auctionManager.js';
43
import {BANNER, VIDEO} from '../src/mediaTypes.js';
54
import {Renderer} from '../src/Renderer.js';
65
import {find} from '../src/polyfill.js';
@@ -95,16 +94,13 @@ function buildBid(bidData) {
9594
bid.vastXml = bidData.content;
9695
bid.mediaType = VIDEO;
9796

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

103-
if (adUnit) {
104-
bid.width = adUnit.mediaTypes.video.playerSize[0][0];
105-
bid.height = adUnit.mediaTypes.video.playerSize[0][1];
99+
if (video) {
100+
bid.width = video.playerSize[0][0];
101+
bid.height = video.playerSize[0][1];
106102

107-
if (adUnit.mediaTypes.video.context === 'outstream') {
103+
if (video.context === 'outstream') {
108104
bid.renderer = createRenderer(bid);
109105
}
110106
}

modules/mediafuseBidAdapter.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import {Renderer} from '../src/Renderer.js';
2020
import {config} from '../src/config.js';
2121
import {registerBidder} from '../src/adapters/bidderFactory.js';
2222
import {ADPOD, BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
23-
import {auctionManager} from '../src/auctionManager.js';
2423
import {find, includes} from '../src/polyfill.js';
2524
import {INSTREAM, OUTSTREAM} from '../src/video.js';
2625
import {getStorageManager} from '../src/storageManager.js';
@@ -870,9 +869,7 @@ function bidToTag(bid) {
870869
tag['banner_frameworks'] = bid.params.frameworks;
871870
}
872871

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

modules/prebidServerBidAdapter/ortbConverter.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ const PBS_CONVERTER = ortbConverter({
118118
src: CONSTANTS.S2S.SRC,
119119
bidId: bidRequest ? (bidRequest.bidId || bidRequest.bid_Id) : null,
120120
transactionId: context.adUnit.transactionId,
121+
adUnitId: context.adUnit.adUnitId,
121122
auctionId: context.bidderRequest.auctionId,
122123
}), bidResponse),
123124
adUnit: context.adUnit.code

modules/priceFloors.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ const getHostname = (() => {
9494
})();
9595

9696
// First look into bidRequest!
97-
function getGptSlotFromAdUnit(transactionId, {index = auctionManager.index} = {}) {
98-
const adUnit = index.getAdUnit({transactionId});
97+
function getGptSlotFromAdUnit(adUnitId, {index = auctionManager.index} = {}) {
98+
const adUnit = index.getAdUnit({adUnitId});
9999
const isGam = deepAccess(adUnit, 'ortb2Imp.ext.data.adserver.name') === 'gam';
100100
return isGam && adUnit.ortb2Imp.ext.data.adserver.adslot;
101101
}
@@ -111,7 +111,7 @@ export let fieldMatchingFunctions = {
111111
[SYN_FIELD]: () => '*',
112112
'size': (bidRequest, bidResponse) => parseGPTSingleSizeArray(bidResponse.size) || '*',
113113
'mediaType': (bidRequest, bidResponse) => bidResponse.mediaType || 'banner',
114-
'gptSlot': (bidRequest, bidResponse) => getGptSlotFromAdUnit((bidRequest || bidResponse).transactionId) || getGptSlotInfoForAdUnitCode(getAdUnitCode(bidRequest, bidResponse)).gptSlot,
114+
'gptSlot': (bidRequest, bidResponse) => getGptSlotFromAdUnit((bidRequest || bidResponse).adUnitId) || getGptSlotInfoForAdUnitCode(getAdUnitCode(bidRequest, bidResponse)).gptSlot,
115115
'domain': getHostname,
116116
'adUnitCode': (bidRequest, bidResponse) => getAdUnitCode(bidRequest, bidResponse)
117117
}

modules/voxBidAdapter.js

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@ import {_map, deepAccess, isArray, logWarn} from '../src/utils.js';
22
import {registerBidder} from '../src/adapters/bidderFactory.js';
33
import {BANNER, VIDEO} from '../src/mediaTypes.js';
44
import {find} from '../src/polyfill.js';
5-
import {auctionManager} from '../src/auctionManager.js';
65
import {Renderer} from '../src/Renderer.js';
7-
import {config} from '../src/config.js'
6+
import {config} from '../src/config.js';
87

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

103-
// TODO: why does this need to iterate through every ad unit?
104-
let adUnit = find(auctionManager.getAdUnits(), function (unit) {
105-
return unit.transactionId === bidData.transactionId;
106-
});
107-
108-
if (adUnit) {
109-
bid.width = adUnit.mediaTypes.video.playerSize[0][0];
110-
bid.height = adUnit.mediaTypes.video.playerSize[0][1];
103+
if (video) {
104+
bid.width = video.playerSize[0][0];
105+
bid.height = video.playerSize[0][1];
111106

112-
if (adUnit.mediaTypes.video.context === 'outstream') {
107+
if (video.context === 'outstream') {
113108
bid.renderer = createRenderer(bid);
114109
}
115110
}

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/adapterManager.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ function getBids({bidderCode, auctionId, bidderRequestId, adUnits, src, metrics}
115115
bids.push(Object.assign({}, bid, {
116116
adUnitCode: adUnit.code,
117117
transactionId: adUnit.transactionId,
118+
adUnitId: adUnit.adUnitId,
118119
sizes: deepAccess(mediaTypes, 'banner.sizes') || deepAccess(mediaTypes, 'video.playerSize') || [],
119120
bidId: bid.bid_id || getUniqueIdentifierStr(),
120121
bidderRequestId,

src/auction.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a
369369
}
370370

371371
function addWinningBid(winningBid) {
372-
const winningAd = adUnits.find(adUnit => adUnit.transactionId === winningBid.transactionId);
372+
const winningAd = adUnits.find(adUnit => adUnit.adUnitId === winningBid.adUnitId);
373373
_winningBids = _winningBids.concat(winningBid);
374374
callBurl(winningBid);
375375
adapterManager.callBidWonBidder(winningBid.adapterCode || winningBid.bidder, winningBid, adUnits);
@@ -557,7 +557,7 @@ function tryAddVideoBid(auctionInstance, bidResponse, afterBidAdded, {index = au
557557
const videoMediaType = deepAccess(
558558
index.getMediaTypes({
559559
requestId: bidResponse.originalRequestId || bidResponse.requestId,
560-
transactionId: bidResponse.transactionId
560+
adUnitId: bidResponse.adUnitId
561561
}), 'video');
562562
const context = videoMediaType && deepAccess(videoMediaType, 'context');
563563
const useCacheKey = videoMediaType && deepAccess(videoMediaType, 'useCacheKey');

src/auctionIndex.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
* @typedef {Object} AuctionIndex
33
*
44
* @property {function({ auctionId: * }): *} getAuction Returns auction instance for `auctionId`
5-
* @property {function({ transactionId: * }): *} getAdUnit Returns `adUnit` object for `transactionId`.
5+
* @property {function({ adUnitId: * }): *} getAdUnit Returns `adUnit` object for `transactionId`.
66
* You should prefer `getMediaTypes` for looking up bid media types.
7-
* @property {function({ transactionId: *, requestId: * }): *} getMediaTypes Returns mediaTypes object from bidRequest (through `requestId`) falling back to the adUnit (through `transactionId`).
7+
* @property {function({ adUnitId: *, requestId: * }): *} getMediaTypes Returns mediaTypes object from bidRequest (through `requestId`) falling back to the adUnit (through `transactionId`).
88
* The bidRequest is given precedence because its mediaTypes can differ from the adUnit's (if bidder-specific labels are in use).
99
* Bids that have no associated request do not have labels either, and use the adUnit's mediaTypes.
1010
* @property {function({ requestId: *, bidderRequestId: * }): *} getBidderRequest Returns bidderRequest that matches both requestId and bidderRequestId (if either or both are provided).
@@ -25,21 +25,21 @@ export function AuctionIndex(getAuctions) {
2525
.find(auction => auction.getAuctionId() === auctionId);
2626
}
2727
},
28-
getAdUnit({transactionId}) {
29-
if (transactionId != null) {
28+
getAdUnit({adUnitId}) {
29+
if (adUnitId != null) {
3030
return getAuctions()
3131
.flatMap(a => a.getAdUnits())
32-
.find(au => au.transactionId === transactionId);
32+
.find(au => au.adUnitId === adUnitId);
3333
}
3434
},
35-
getMediaTypes({transactionId, requestId}) {
35+
getMediaTypes({adUnitId, requestId}) {
3636
if (requestId != null) {
3737
const req = this.getBidRequest({requestId});
38-
if (req != null && (transactionId == null || req.transactionId === transactionId)) {
38+
if (req != null && (adUnitId == null || req.adUnitId === adUnitId)) {
3939
return req.mediaTypes;
4040
}
41-
} else if (transactionId != null) {
42-
const au = this.getAdUnit({transactionId});
41+
} else if (adUnitId != null) {
42+
const au = this.getAdUnit({adUnitId});
4343
if (au != null) {
4444
return au.mediaTypes;
4545
}

src/bidfactory.js

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,23 @@ import { getUniqueIdentifierStr } from './utils.js';
1414
dealId,
1515
priceKeyString;
1616
*/
17-
function Bid(statusCode, {src = 'client', bidder = '', bidId, transactionId, auctionId} = {}) {
17+
function Bid(statusCode, {src = 'client', bidder = '', bidId, transactionId, adUnitId, auctionId} = {}) {
1818
var _bidSrc = src;
1919
var _statusCode = statusCode || 0;
2020

21-
this.bidderCode = bidder;
22-
this.width = 0;
23-
this.height = 0;
24-
this.statusMessage = _getStatus();
25-
this.adId = getUniqueIdentifierStr();
26-
this.requestId = bidId;
27-
this.transactionId = transactionId;
28-
this.auctionId = auctionId;
29-
this.mediaType = 'banner';
30-
this.source = _bidSrc;
21+
Object.assign(this, {
22+
bidderCode: bidder,
23+
width: 0,
24+
height: 0,
25+
statusMessage: _getStatus(),
26+
adId: getUniqueIdentifierStr(),
27+
requestId: bidId,
28+
transactionId,
29+
adUnitId,
30+
auctionId,
31+
mediaType: 'banner',
32+
source: _bidSrc
33+
})
3134

3235
function _getStatus() {
3336
switch (_statusCode) {
@@ -57,6 +60,7 @@ function Bid(statusCode, {src = 'client', bidder = '', bidId, transactionId, auc
5760
bidder: this.bidderCode,
5861
bidId: this.requestId,
5962
transactionId: this.transactionId,
63+
adUnitId: this.adUnitId,
6064
auctionId: this.auctionId
6165
}
6266
};

src/prebid.js

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,8 @@ export const startAuction = hook('async', function ({ bidsBackHandler, timeout:
552552
defer.resolve({bids, timedOut, auctionId})
553553
}
554554

555+
const tids = {};
556+
555557
/*
556558
* for a given adunit which supports a set of mediaTypes
557559
* and a given bidder which supports a set of mediaTypes
@@ -567,15 +569,18 @@ export const startAuction = hook('async', function ({ bidsBackHandler, timeout:
567569
const bidderRegistry = adapterManager.bidderRegistry;
568570

569571
const bidders = allBidders.filter(bidder => !s2sBidders.has(bidder));
570-
571-
const tid = adUnit.ortb2Imp?.ext?.tid || generateUUID();
572-
adUnit.transactionId = tid;
572+
adUnit.adUnitId = generateUUID();
573+
const tid = adUnit.ortb2Imp?.ext?.tid;
574+
if (tid) {
575+
if (tids.hasOwnProperty(adUnit.code)) {
576+
logWarn(`Multiple distinct ortb2Imp.ext.tid were provided for twin ad units '${adUnit.code}'`)
577+
} else {
578+
tids[adUnit.code] = tid;
579+
}
580+
}
573581
if (ttlBuffer != null && !adUnit.hasOwnProperty('ttlBuffer')) {
574582
adUnit.ttlBuffer = ttlBuffer;
575583
}
576-
// 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.
577-
deepSetValue(adUnit, 'ortb2Imp.ext.tid', tid);
578-
579584
bidders.forEach(bidder => {
580585
const adapter = bidderRegistry[bidder];
581586
const spec = adapter && adapter.getSpec && adapter.getSpec();
@@ -594,11 +599,18 @@ export const startAuction = hook('async', function ({ bidsBackHandler, timeout:
594599
});
595600
adunitCounter.incrementRequestsCounter(adUnit.code);
596601
});
597-
598602
if (!adUnits || adUnits.length === 0) {
599603
logMessage('No adUnits configured. No bids requested.');
600604
auctionDone();
601605
} else {
606+
adUnits.forEach(au => {
607+
const tid = au.ortb2Imp?.ext?.tid || tids[au.code] || generateUUID();
608+
if (!tids.hasOwnProperty(au.code)) {
609+
tids[au.code] = tid;
610+
}
611+
au.transactionId = tid;
612+
deepSetValue(au, 'ortb2Imp.ext.tid', tid);
613+
});
602614
const auction = auctionManager.createAuction({
603615
adUnits,
604616
adUnitCodes,

0 commit comments

Comments
 (0)