Skip to content

Commit

Permalink
Prebid 8: improve transmitTid logic for PBS adapter (#10092)
Browse files Browse the repository at this point in the history
  • Loading branch information
dgirardi authored Jun 13, 2023
1 parent b053d55 commit e0c6e74
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 41 deletions.
35 changes: 33 additions & 2 deletions modules/prebidServerBidAdapter/ortbConverter.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ import {setImpBidParams} from '../../libraries/pbsExtensions/processors/params.j
import {SUPPORTED_MEDIA_TYPES} from '../../libraries/pbsExtensions/processors/mediaType.js';
import {IMP, REQUEST, RESPONSE} from '../../src/pbjsORTB.js';
import {beConvertCurrency} from '../../src/utils/currency.js';
import {redactor} from '../../src/activities/redactor.js';
import {s2sActivityParams} from '../../src/adapterManager.js';
import {activityParams} from '../../src/activities/activityParams.js';
import {MODULE_TYPE_BIDDER} from '../../src/activities/modules.js';
import {isActivityAllowed} from '../../src/activities/rules.js';
import {ACTIVITY_TRANSMIT_TID} from '../../src/activities/activities.js';

const DEFAULT_S2S_TTL = 60;
const DEFAULT_S2S_CURRENCY = 'USD';
Expand Down Expand Up @@ -68,6 +74,10 @@ const PBS_CONVERTER = ortbConverter({
deepSetValue(request, 'ext.prebid.data.eidpermissions', eidPermissions);
}

if (!context.transmitTids) {
deepSetValue(request, 'ext.prebid.createtids', false);
}

return request;
}
},
Expand Down Expand Up @@ -165,13 +175,17 @@ const PBS_CONVERTER = ortbConverter({
deepSetValue(ortbRequest, 'ext.prebid', mergeDeep(ortbRequest.ext?.prebid || {}, context.s2sBidRequest.s2sConfig.extPrebid));
}

// for global FPD, check allowed activities against "prebid.pbsBidAdapter"...
context.getRedactor().ortb2(ortbRequest);

const fpdConfigs = Object.entries(context.s2sBidRequest.ortb2Fragments?.bidder || {}).filter(([bidder]) => {
const bidders = context.s2sBidRequest.s2sConfig.bidders;
const allowUnknownBidderCodes = context.s2sBidRequest.s2sConfig.allowUnknownBidderCodes;
return allowUnknownBidderCodes || (bidders && bidders.includes(bidder));
}).map(([bidder, ortb2]) => ({
// ... but for bidder specific FPD we can use the actual bidder
bidders: [bidder],
config: {ortb2}
config: {ortb2: context.getRedactor(bidder).ortb2(ortb2)}
}));
if (fpdConfigs.length) {
deepSetValue(ortbRequest, 'ext.prebid.bidderconfig', fpdConfigs);
Expand Down Expand Up @@ -239,10 +253,25 @@ export function buildPBSRequest(s2sBidRequest, bidderRequests, adUnits, requeste
const requestTimestamp = timestamp();
const impIds = new Set();
const proxyBidRequests = [];
const s2sParams = s2sActivityParams(s2sBidRequest.s2sConfig);

const getRedactor = (() => {
const global = redactor(s2sParams);
const bidders = {};
return (bidder) => {
if (bidder == null) return global;
if (!bidders.hasOwnProperty(bidder)) {
bidders[bidder] = redactor(activityParams(MODULE_TYPE_BIDDER, bidder));
}
return bidders[bidder]
}
})();

adUnits = adUnits.map((au) => getRedactor().bidRequest(au))

adUnits.forEach(adUnit => {
const actualBidRequests = new Map();

adUnits.bids = adUnit.bids.map(br => getRedactor(br.bidder).bidRequest(br));
adUnit.bids.forEach((bid) => {
if (bid.mediaTypes != null) {
// TODO: support labels / conditional bids
Expand Down Expand Up @@ -284,6 +313,8 @@ export function buildPBSRequest(s2sBidRequest, bidderRequests, adUnits, requeste
actualBidderRequests: bidderRequests,
eidPermissions,
nativeRequest: s2sBidRequest.s2sConfig.ortbNative,
getRedactor,
transmitTids: isActivityAllowed(ACTIVITY_TRANSMIT_TID, s2sParams),
}
});
}
Expand Down
27 changes: 16 additions & 11 deletions src/adapterManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import {ACTIVITY_FETCH_BIDS, ACTIVITY_REPORT_ANALYTICS} from './activities/activ
import {ACTIVITY_PARAM_ANL_CONFIG, ACTIVITY_PARAM_S2S_NAME, activityParamsBuilder} from './activities/params.js';
import {redactor} from './activities/redactor.js';

const PBS_ADAPTER_NAME = 'pbsBidAdapter';
export const PBS_ADAPTER_NAME = 'pbsBidAdapter';
export const PARTITIONS = {
CLIENT: 'client',
SERVER: 'server'
Expand All @@ -68,6 +68,12 @@ var _analyticsRegistry = {};

const activityParams = activityParamsBuilder((alias) => adapterManager.resolveAlias(alias));

export function s2sActivityParams(s2sConfig) {
return activityParams(MODULE_TYPE_PREBID, PBS_ADAPTER_NAME, {
[ACTIVITY_PARAM_S2S_NAME]: s2sConfig.configName
});
}

/**
* @typedef {object} LabelDescriptor
* @property {boolean} labelAll describes whether or not this object expects all labels to match, or any label to match
Expand Down Expand Up @@ -271,8 +277,12 @@ adapterManager.makeBidRequests = hook('sync', function (adUnits, auctionStart, a
const ortb2 = ortb2Fragments.global || {};
const bidderOrtb2 = ortb2Fragments.bidder || {};

function addOrtb2(bidderRequest) {
const redact = dep.redact(activityParams(MODULE_TYPE_BIDDER, bidderRequest.bidderCode));
function addOrtb2(bidderRequest, s2sActivityParams) {
const redact = dep.redact(
s2sActivityParams != null
? s2sActivityParams
: activityParams(MODULE_TYPE_BIDDER, bidderRequest.bidderCode)
);
const fpd = Object.freeze(redact.ortb2(mergeDeep({source: {tid: auctionId}}, ortb2, bidderOrtb2[bidderRequest.bidderCode])));
bidderRequest.ortb2 = fpd;
bidderRequest.bids = bidderRequest.bids.map((bid) => {
Expand All @@ -282,14 +292,9 @@ adapterManager.makeBidRequests = hook('sync', function (adUnits, auctionStart, a
return bidderRequest;
}

function isS2SAllowed(s2sConfig) {
return dep.isAllowed(ACTIVITY_FETCH_BIDS, activityParams(MODULE_TYPE_PREBID, PBS_ADAPTER_NAME, {
[ACTIVITY_PARAM_S2S_NAME]: s2sConfig.configName
}));
}

_s2sConfigs.forEach(s2sConfig => {
if (s2sConfig && s2sConfig.enabled && isS2SAllowed(s2sConfig)) {
const s2sParams = s2sActivityParams(s2sConfig);
if (s2sConfig && s2sConfig.enabled && dep.isAllowed(ACTIVITY_FETCH_BIDS, s2sParams)) {
let {adUnits: adUnitsS2SCopy, hasModuleBids} = getAdUnitCopyForPrebidServer(adUnits, s2sConfig);

// uniquePbsTid is so we know which server to send which bids to during the callBids function
Expand All @@ -309,7 +314,7 @@ adapterManager.makeBidRequests = hook('sync', function (adUnits, auctionStart, a
src: CONSTANTS.S2S.SRC,
refererInfo,
metrics,
});
}, s2sParams);
if (bidderRequest.bids.length !== 0) {
bidRequests.push(bidderRequest);
}
Expand Down
143 changes: 115 additions & 28 deletions test/spec/modules/prebidServerBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import 'modules/consentManagement.js';
import 'modules/consentManagementUsp.js';
import 'modules/schain.js';
import 'modules/fledgeForGpt.js';
import * as redactor from 'src/activities/redactor.js';
import {hook} from '../../../src/hook.js';
import {decorateAdUnitsWithNativeParams} from '../../../src/native.js';
import {auctionManager} from '../../../src/auctionManager.js';
Expand Down Expand Up @@ -636,11 +637,101 @@ describe('S2S Adapter', function () {
resetSyncedStatus();
});

it('should pick source.tid from FPD', () => {
config.setConfig({ s2sConfig: CONFIG });
adapter.callBids({...REQUEST, ortb2Fragments: {global: {source: {tid: 'mock-tid'}}}}, BID_REQUESTS, addBidResponse, done, ajax);
const req = JSON.parse(server.requests[0].requestBody)
expect(req.source.tid).to.eql('mock-tid');
describe('FPD redaction', () => {
let sandbox, ortb2Fragments, redactorMocks, s2sReq;

beforeEach(() => {
sandbox = sinon.sandbox.create();
redactorMocks = {};
sandbox.stub(redactor, 'redactor').callsFake((params) => {
if (!redactorMocks.hasOwnProperty(params.component)) {
redactorMocks[params.component] = {
ortb2: sinon.stub().callsFake(o => o),
bidRequest: sinon.stub().callsFake(o => o)
}
}
return redactorMocks[params.component];
})
ortb2Fragments = {
global: {
mock: 'value'
},
bidder: {
appnexus: {
mock: 'A'
}
}
}
const s2sConfig = {
...CONFIG,
};
config.setConfig({s2sConfig});
s2sReq = {
...REQUEST,
s2sConfig
};
});

afterEach(() => {
sandbox.restore();
})

function callBids() {
adapter.callBids({
...s2sReq,
ortb2Fragments
}, BID_REQUESTS, addBidResponse, done, ajax);
}

it('should be applied to ortb2Fragments', () => {
callBids();
sinon.assert.calledWithMatch(redactorMocks['prebid.pbsBidAdapter'].ortb2, ortb2Fragments.global);
Object.entries(ortb2Fragments.bidder).forEach(([bidder, ortb2]) => {
sinon.assert.calledWith(redactorMocks[`bidder.${bidder}`].ortb2, ortb2);
});
});

it('should be applied to ad units', () => {
callBids();
s2sReq.ad_units.forEach(au => {
sinon.assert.calledWith(redactorMocks['prebid.pbsBidAdapter'].bidRequest, au);
au.bids.forEach((bid) => {
sinon.assert.calledWith(redactorMocks[`bidder.${bid.bidder}`].bidRequest, bid);
})
})
})
});

describe('transaction IDs', () => {
let s2sReq;
beforeEach(() => {
s2sReq = {
...REQUEST,
ortb2Fragments: {global: {source: {tid: 'mock-tid'}}},
ad_units: REQUEST.ad_units.map(au => ({...au, ortb2Imp: {ext: {tid: 'mock-tid'}}}))
};
BID_REQUESTS[0].bids[0].ortb2Imp = {ext: {tid: 'mock-tid'}};
});

function makeRequest() {
adapter.callBids(s2sReq, BID_REQUESTS, addBidResponse, done, ajax);
return JSON.parse(server.requests[0].requestBody);
}

it('should not be set when transmitTid is not allowed, with ext.prebid.createtids: false', () => {
config.setConfig({ s2sConfig: CONFIG, enableTIDs: false });
const req = makeRequest();
expect(req.source.tid).to.not.exist;
expect(req.imp[0].ext.tid).to.not.exist;
expect(req.ext.prebid.createtids).to.equal(false);
});

it('should be picked from FPD otherwise', () => {
config.setConfig({s2sConfig: CONFIG, enableTIDs: true});
const req = makeRequest();
expect(req.source.tid).to.eql('mock-tid');
expect(req.imp[0].ext.tid).to.eql('mock-tid');
})
})

it('should set tmax to s2sConfig.timeout', () => {
Expand Down Expand Up @@ -1638,19 +1729,17 @@ describe('S2S Adapter', function () {

const requestBid = JSON.parse(server.requests[0].requestBody);

expect(requestBid.ext).to.deep.equal({
prebid: {
auctiontimestamp: 1510852447530,
targeting: {
includebidderkeys: false,
includewinners: true
},
channel: {
name: 'pbjs',
version: 'v$prebid.version$'
}
sinon.assert.match(requestBid.ext.prebid, {
auctiontimestamp: 1510852447530,
targeting: {
includebidderkeys: false,
includewinners: true
},
channel: {
name: 'pbjs',
version: 'v$prebid.version$'
}
});
})
});

it('skips dynamic aliases to request when skipPbsAliasing enabled', function () {
Expand All @@ -1677,17 +1766,15 @@ describe('S2S Adapter', function () {

const requestBid = JSON.parse(server.requests[0].requestBody);

expect(requestBid.ext).to.deep.equal({
prebid: {
auctiontimestamp: 1510852447530,
targeting: {
includebidderkeys: false,
includewinners: true
},
channel: {
name: 'pbjs',
version: 'v$prebid.version$'
}
sinon.assert.match(requestBid.ext.prebid, {
auctiontimestamp: 1510852447530,
targeting: {
includebidderkeys: false,
includewinners: true
},
channel: {
name: 'pbjs',
version: 'v$prebid.version$'
}
});
});
Expand Down

0 comments on commit e0c6e74

Please sign in to comment.