Skip to content

Commit 9d5b9b4

Browse files
author
Isaac Dettman
authored
PBS Bid Adapter oRTB caching and video updates (prebid#3528)
* updates to both the rubicon adapter and the prebid server for ORTB requirements * updates to bid and auction data to reflect oRTB structure * merged pbjs commits * reverted karma conf * always add ext.prebid.targeting.includewinners: true for openrtb * add unit test for ext.prebid.targeting.includewinners for openrtb * added s2sConfig 'video.ext.prebid' support and unit test * added code comments for changes to ORTB * handle response.ext.prebid.cache values and added unit test * update to merged video.ext.prebid with request.ext.prebid * update to merge includewinners * added test for override used in s2sConfig for ext.prebid.targeting.includewinners value * fixes for response cache logic * added support for targeting cache props HB-3740 * added test for adserverTargeting change * updates to address Jira ticket revision * update to build the vastUrl object by utilizing hb_cache_host + hb_cache_path, since the hb_cache_hostpath will be suppressed soon * removed single quotes around object literal keys to match formatting * added new standard targeting keys for video * removed code added by jsnellbaker per his recommendation because our change should make it unnecessary
1 parent 1a4c964 commit 9d5b9b4

File tree

4 files changed

+279
-10
lines changed

4 files changed

+279
-10
lines changed

modules/prebidServerBidAdapter/index.js

+37-2
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ config.setDefaults({
7171
* @property {boolean} [cacheMarkup] whether to cache the adm result
7272
* @property {string} [adapter] adapter code to use for S2S
7373
* @property {string} [syncEndpoint] endpoint URL for syncing cookies
74+
* @property {Object} [extPrebid] properties will be merged into request.ext.prebid
7475
* @property {AdapterOptions} [adapterOptions] adds arguments to resulting OpenRTB payload to Prebid Server
7576
*/
7677
function setS2sConfig(options) {
@@ -483,8 +484,23 @@ const OPEN_RTB_PROTOCOL = {
483484
tmax: _s2sConfig.timeout,
484485
imp: imps,
485486
test: getConfig('debug') ? 1 : 0,
487+
ext: {
488+
prebid: {
489+
targeting: {
490+
// includewinners is always true for openrtb
491+
includewinners: true,
492+
// includebidderkeys always false for openrtb
493+
includebidderkeys: false
494+
}
495+
}
496+
}
486497
};
487498

499+
// s2sConfig video.ext.prebid is passed through openrtb to PBS
500+
if (_s2sConfig.extPrebid && typeof _s2sConfig.extPrebid === 'object') {
501+
request.ext.prebid = Object.assign(request.ext.prebid, _s2sConfig.extPrebid);
502+
}
503+
488504
_appendSiteAppDevice(request);
489505

490506
const digiTrust = _getDigiTrustQueryParams();
@@ -493,7 +509,7 @@ const OPEN_RTB_PROTOCOL = {
493509
}
494510

495511
if (!utils.isEmpty(aliases)) {
496-
request.ext = { prebid: { aliases } };
512+
request.ext.prebid.aliases = aliases;
497513
}
498514

499515
if (bidRequests && bidRequests[0].userId && typeof bidRequests[0].userId === 'object') {
@@ -571,10 +587,29 @@ const OPEN_RTB_PROTOCOL = {
571587
bidRequest.serverResponseTimeMs = serverResponseTimeMs;
572588
}
573589

590+
const extPrebidTargeting = utils.deepAccess(bid, 'ext.prebid.targeting');
591+
592+
// If ext.prebid.targeting exists, add it as a property value named 'adserverTargeting'
593+
if (extPrebidTargeting && typeof extPrebidTargeting === 'object') {
594+
bidObject.adserverTargeting = extPrebidTargeting;
595+
}
596+
574597
if (utils.deepAccess(bid, 'ext.prebid.type') === VIDEO) {
575598
bidObject.mediaType = VIDEO;
599+
600+
// try to get cache values from 'response.ext.prebid.cache'
601+
// else try 'bid.ext.prebid.targeting' as fallback
602+
if (bid.ext.prebid.cache && typeof bid.ext.prebid.cache.vastXml === 'object' && bid.ext.prebid.cache.vastXml.cacheId && bid.ext.prebid.cache.vastXml.url) {
603+
bidObject.videoCacheKey = bid.ext.prebid.cache.vastXml.cacheId;
604+
bidObject.vastUrl = bid.ext.prebid.cache.vastXml.url;
605+
} else if (extPrebidTargeting && extPrebidTargeting.hb_uuid && extPrebidTargeting.hb_cache_host && extPrebidTargeting.hb_cache_path) {
606+
bidObject.videoCacheKey = extPrebidTargeting.hb_uuid;
607+
// build url using key and cache host
608+
bidObject.vastUrl = `https://${extPrebidTargeting.hb_cache_host}${extPrebidTargeting.hb_cache_path}?uuid=${extPrebidTargeting.hb_uuid}`;
609+
}
610+
576611
if (bid.adm) { bidObject.vastXml = bid.adm; }
577-
if (bid.nurl) { bidObject.vastUrl = bid.nurl; }
612+
if (!bidObject.vastUrl && bid.nurl) { bidObject.vastUrl = bid.nurl; }
578613
} else { // banner
579614
if (bid.adm && bid.nurl) {
580615
bidObject.ad = bid.adm;

src/auction.js

+12-7
Original file line numberDiff line numberDiff line change
@@ -500,14 +500,8 @@ function setupBidTargeting(bidObject, bidderRequest) {
500500
keyValues = getKeyValueTargetingPairs(bidObject.bidderCode, bidObject, bidReq);
501501
}
502502

503-
let cacheTargetKeys = {};
504-
if (bidObject.videoCacheKey) {
505-
cacheTargetKeys.hb_uuid = bidObject.videoCacheKey;
506-
cacheTargetKeys.hb_cache_id = bidObject.videoCacheKey;
507-
}
508-
509503
// use any targeting provided as defaults, otherwise just set from getKeyValueTargetingPairs
510-
bidObject.adserverTargeting = Object.assign(bidObject.adserverTargeting || {}, cacheTargetKeys, keyValues);
504+
bidObject.adserverTargeting = Object.assign(bidObject.adserverTargeting || {}, keyValues);
511505
}
512506

513507
export function getStandardBidderSettings(mediaType) {
@@ -572,6 +566,17 @@ export function getStandardBidderSettings(mediaType) {
572566
}
573567
},
574568
]
569+
570+
if (mediaType === 'video') {
571+
[CONSTANTS.TARGETING_KEYS.UUID, CONSTANTS.TARGETING_KEYS.CACHE_ID].forEach(item => {
572+
bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD][CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING].push({
573+
key: item,
574+
val: function val(bidResponse) {
575+
return bidResponse.videoCacheKey;
576+
}
577+
})
578+
});
579+
}
575580
}
576581
return bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD];
577582
}

src/constants.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@
6262
"SIZE": "hb_size",
6363
"DEAL": "hb_deal",
6464
"SOURCE": "hb_source",
65-
"FORMAT": "hb_format"
65+
"FORMAT": "hb_format",
66+
"UUID": "hb_uuid",
67+
"CACHE_ID": "hb_cache_id"
6668
},
6769
"NATIVE_KEYS": {
6870
"title": "hb_native_title",

test/spec/modules/prebidServerBidAdapter_spec.js

+227
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,10 @@ describe('S2S Adapter', function () {
654654
prebid: {
655655
aliases: {
656656
brealtime: 'appnexus'
657+
},
658+
targeting: {
659+
includebidderkeys: false,
660+
includewinners: true
657661
}
658662
}
659663
});
@@ -684,6 +688,10 @@ describe('S2S Adapter', function () {
684688
prebid: {
685689
aliases: {
686690
[alias]: 'appnexus'
691+
},
692+
targeting: {
693+
includebidderkeys: false,
694+
includewinners: true
687695
}
688696
}
689697
});
@@ -822,6 +830,146 @@ describe('S2S Adapter', function () {
822830
expect(requestBid.user.ext.tpid.foo).is.equal('abc123');
823831
expect(requestBid.user.ext.tpid.unifiedid).is.equal('1234');
824832
})
833+
834+
it('always add ext.prebid.targeting.includebidderkeys: false for ORTB', function () {
835+
const s2sConfig = Object.assign({}, CONFIG, {
836+
endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction',
837+
adapterOptions: {
838+
appnexus: {
839+
key: 'value'
840+
}
841+
}
842+
});
843+
const _config = {
844+
s2sConfig: s2sConfig,
845+
device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' },
846+
app: { bundle: 'com.test.app' },
847+
};
848+
849+
config.setConfig(_config);
850+
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
851+
const requestBid = JSON.parse(requests[0].requestBody);
852+
853+
expect(requestBid.ext.prebid.targeting).to.haveOwnProperty('includebidderkeys');
854+
expect(requestBid.ext.prebid.targeting.includebidderkeys).to.equal(false);
855+
});
856+
857+
it('always add ext.prebid.targeting.includewinners: true for ORTB', function () {
858+
const s2sConfig = Object.assign({}, CONFIG, {
859+
endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction',
860+
adapterOptions: {
861+
appnexus: {
862+
key: 'value'
863+
}
864+
}
865+
});
866+
const _config = {
867+
s2sConfig: s2sConfig,
868+
device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' },
869+
app: { bundle: 'com.test.app' },
870+
};
871+
872+
config.setConfig(_config);
873+
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
874+
const requestBid = JSON.parse(requests[0].requestBody);
875+
876+
expect(requestBid.ext.prebid.targeting).to.haveOwnProperty('includewinners');
877+
expect(requestBid.ext.prebid.targeting.includewinners).to.equal(true);
878+
});
879+
880+
it('adds s2sConfig video.ext.prebid to request for ORTB', function () {
881+
const s2sConfig = Object.assign({}, CONFIG, {
882+
endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction',
883+
extPrebid: {
884+
foo: 'bar'
885+
}
886+
});
887+
const _config = {
888+
s2sConfig: s2sConfig,
889+
device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' },
890+
app: { bundle: 'com.test.app' },
891+
};
892+
893+
config.setConfig(_config);
894+
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
895+
const requestBid = JSON.parse(requests[0].requestBody);
896+
897+
expect(requestBid).to.haveOwnProperty('ext');
898+
expect(requestBid.ext).to.haveOwnProperty('prebid');
899+
expect(requestBid.ext.prebid).to.deep.equal({
900+
foo: 'bar',
901+
targeting: {
902+
includewinners: true,
903+
includebidderkeys: false
904+
}
905+
});
906+
});
907+
908+
it('overrides request.ext.prebid properties using s2sConfig video.ext.prebid values for ORTB', function () {
909+
const s2sConfig = Object.assign({}, CONFIG, {
910+
endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction',
911+
extPrebid: {
912+
targeting: {
913+
includewinners: false,
914+
includebidderkeys: true
915+
}
916+
}
917+
});
918+
const _config = {
919+
s2sConfig: s2sConfig,
920+
device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' },
921+
app: { bundle: 'com.test.app' },
922+
};
923+
924+
config.setConfig(_config);
925+
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
926+
const requestBid = JSON.parse(requests[0].requestBody);
927+
928+
expect(requestBid).to.haveOwnProperty('ext');
929+
expect(requestBid.ext).to.haveOwnProperty('prebid');
930+
expect(requestBid.ext.prebid).to.deep.equal({
931+
targeting: {
932+
includewinners: false,
933+
includebidderkeys: true
934+
}
935+
});
936+
});
937+
938+
it('overrides request.ext.prebid properties using s2sConfig video.ext.prebid values for ORTB', function () {
939+
const s2sConfig = Object.assign({}, CONFIG, {
940+
endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction',
941+
extPrebid: {
942+
cache: {
943+
vastxml: 'vastxml-set-though-extPrebid.cache.vastXml'
944+
},
945+
targeting: {
946+
includewinners: false,
947+
includebidderkeys: false
948+
}
949+
}
950+
});
951+
const _config = {
952+
s2sConfig: s2sConfig,
953+
device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' },
954+
app: { bundle: 'com.test.app' },
955+
};
956+
957+
config.setConfig(_config);
958+
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
959+
const requestBid = JSON.parse(requests[0].requestBody);
960+
961+
expect(requestBid).to.haveOwnProperty('ext');
962+
expect(requestBid.ext).to.haveOwnProperty('prebid');
963+
expect(requestBid.ext.prebid).to.deep.equal({
964+
cache: {
965+
vastxml: 'vastxml-set-though-extPrebid.cache.vastXml'
966+
},
967+
targeting: {
968+
includewinners: false,
969+
includebidderkeys: false
970+
}
971+
});
972+
});
825973
});
826974

827975
describe('response handler', function () {
@@ -1058,6 +1206,85 @@ describe('S2S Adapter', function () {
10581206
expect(response).to.have.property('cpm', 10);
10591207
});
10601208

1209+
it('handles response cache from ext.prebid.cache.vastXml', function () {
1210+
const s2sConfig = Object.assign({}, CONFIG, {
1211+
endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param'
1212+
});
1213+
config.setConfig({s2sConfig});
1214+
const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO);
1215+
cacheResponse.seatbid.forEach(item => {
1216+
item.bid[0].ext.prebid.cache = {
1217+
vastXml: {
1218+
cacheId: 'abcd1234',
1219+
url: 'https://prebid-cache.net/cache?uuid=abcd1234'
1220+
}
1221+
}
1222+
});
1223+
server.respondWith(JSON.stringify(cacheResponse));
1224+
adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
1225+
server.respond();
1226+
1227+
sinon.assert.calledOnce(addBidResponse);
1228+
const response = addBidResponse.firstCall.args[1];
1229+
1230+
expect(response).to.have.property('statusMessage', 'Bid available');
1231+
expect(response).to.have.property('videoCacheKey', 'abcd1234');
1232+
expect(response).to.have.property('vastUrl', 'https://prebid-cache.net/cache?uuid=abcd1234');
1233+
});
1234+
1235+
it('add adserverTargeting object to bids when ext.prebid.targeting is defined', function () {
1236+
const s2sConfig = Object.assign({}, CONFIG, {
1237+
endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param'
1238+
});
1239+
config.setConfig({s2sConfig});
1240+
const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO);
1241+
const targetingTestData = {
1242+
hb_cache_path: '/cache',
1243+
hb_cache_host: 'prebid-cache.testurl.com'
1244+
};
1245+
1246+
cacheResponse.seatbid.forEach(item => {
1247+
item.bid[0].ext.prebid.targeting = targetingTestData
1248+
});
1249+
server.respondWith(JSON.stringify(cacheResponse));
1250+
adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
1251+
server.respond();
1252+
1253+
sinon.assert.calledOnce(addBidResponse);
1254+
const response = addBidResponse.firstCall.args[1];
1255+
1256+
expect(response).to.have.property('adserverTargeting');
1257+
expect(response.adserverTargeting).to.deep.equal({
1258+
'hb_cache_path': '/cache',
1259+
'hb_cache_host': 'prebid-cache.testurl.com'
1260+
});
1261+
});
1262+
1263+
it('handles response cache from ext.prebid.targeting', function () {
1264+
const s2sConfig = Object.assign({}, CONFIG, {
1265+
endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param'
1266+
});
1267+
config.setConfig({s2sConfig});
1268+
const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO);
1269+
cacheResponse.seatbid.forEach(item => {
1270+
item.bid[0].ext.prebid.targeting = {
1271+
hb_uuid: 'a5ad3993',
1272+
hb_cache_host: 'prebid-cache.net',
1273+
hb_cache_path: '/cache'
1274+
}
1275+
});
1276+
server.respondWith(JSON.stringify(cacheResponse));
1277+
adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
1278+
server.respond();
1279+
1280+
sinon.assert.calledOnce(addBidResponse);
1281+
const response = addBidResponse.firstCall.args[1];
1282+
1283+
expect(response).to.have.property('statusMessage', 'Bid available');
1284+
expect(response).to.have.property('videoCacheKey', 'a5ad3993');
1285+
expect(response).to.have.property('vastUrl', 'https://prebid-cache.net/cache?uuid=a5ad3993');
1286+
});
1287+
10611288
it('should log warning for unsupported bidder', function () {
10621289
server.respondWith(JSON.stringify(RESPONSE_UNSUPPORTED_BIDDER));
10631290

0 commit comments

Comments
 (0)