Skip to content

Commit

Permalink
PBS Bid Adapter oRTB caching and video updates (prebid#3528)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Isaac Dettman authored Mar 5, 2019
1 parent 1a4c964 commit 9d5b9b4
Show file tree
Hide file tree
Showing 4 changed files with 279 additions and 10 deletions.
39 changes: 37 additions & 2 deletions modules/prebidServerBidAdapter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ config.setDefaults({
* @property {boolean} [cacheMarkup] whether to cache the adm result
* @property {string} [adapter] adapter code to use for S2S
* @property {string} [syncEndpoint] endpoint URL for syncing cookies
* @property {Object} [extPrebid] properties will be merged into request.ext.prebid
* @property {AdapterOptions} [adapterOptions] adds arguments to resulting OpenRTB payload to Prebid Server
*/
function setS2sConfig(options) {
Expand Down Expand Up @@ -483,8 +484,23 @@ const OPEN_RTB_PROTOCOL = {
tmax: _s2sConfig.timeout,
imp: imps,
test: getConfig('debug') ? 1 : 0,
ext: {
prebid: {
targeting: {
// includewinners is always true for openrtb
includewinners: true,
// includebidderkeys always false for openrtb
includebidderkeys: false
}
}
}
};

// s2sConfig video.ext.prebid is passed through openrtb to PBS
if (_s2sConfig.extPrebid && typeof _s2sConfig.extPrebid === 'object') {
request.ext.prebid = Object.assign(request.ext.prebid, _s2sConfig.extPrebid);
}

_appendSiteAppDevice(request);

const digiTrust = _getDigiTrustQueryParams();
Expand All @@ -493,7 +509,7 @@ const OPEN_RTB_PROTOCOL = {
}

if (!utils.isEmpty(aliases)) {
request.ext = { prebid: { aliases } };
request.ext.prebid.aliases = aliases;
}

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

const extPrebidTargeting = utils.deepAccess(bid, 'ext.prebid.targeting');

// If ext.prebid.targeting exists, add it as a property value named 'adserverTargeting'
if (extPrebidTargeting && typeof extPrebidTargeting === 'object') {
bidObject.adserverTargeting = extPrebidTargeting;
}

if (utils.deepAccess(bid, 'ext.prebid.type') === VIDEO) {
bidObject.mediaType = VIDEO;

// try to get cache values from 'response.ext.prebid.cache'
// else try 'bid.ext.prebid.targeting' as fallback
if (bid.ext.prebid.cache && typeof bid.ext.prebid.cache.vastXml === 'object' && bid.ext.prebid.cache.vastXml.cacheId && bid.ext.prebid.cache.vastXml.url) {
bidObject.videoCacheKey = bid.ext.prebid.cache.vastXml.cacheId;
bidObject.vastUrl = bid.ext.prebid.cache.vastXml.url;
} else if (extPrebidTargeting && extPrebidTargeting.hb_uuid && extPrebidTargeting.hb_cache_host && extPrebidTargeting.hb_cache_path) {
bidObject.videoCacheKey = extPrebidTargeting.hb_uuid;
// build url using key and cache host
bidObject.vastUrl = `https://${extPrebidTargeting.hb_cache_host}${extPrebidTargeting.hb_cache_path}?uuid=${extPrebidTargeting.hb_uuid}`;
}

if (bid.adm) { bidObject.vastXml = bid.adm; }
if (bid.nurl) { bidObject.vastUrl = bid.nurl; }
if (!bidObject.vastUrl && bid.nurl) { bidObject.vastUrl = bid.nurl; }
} else { // banner
if (bid.adm && bid.nurl) {
bidObject.ad = bid.adm;
Expand Down
19 changes: 12 additions & 7 deletions src/auction.js
Original file line number Diff line number Diff line change
Expand Up @@ -500,14 +500,8 @@ function setupBidTargeting(bidObject, bidderRequest) {
keyValues = getKeyValueTargetingPairs(bidObject.bidderCode, bidObject, bidReq);
}

let cacheTargetKeys = {};
if (bidObject.videoCacheKey) {
cacheTargetKeys.hb_uuid = bidObject.videoCacheKey;
cacheTargetKeys.hb_cache_id = bidObject.videoCacheKey;
}

// use any targeting provided as defaults, otherwise just set from getKeyValueTargetingPairs
bidObject.adserverTargeting = Object.assign(bidObject.adserverTargeting || {}, cacheTargetKeys, keyValues);
bidObject.adserverTargeting = Object.assign(bidObject.adserverTargeting || {}, keyValues);
}

export function getStandardBidderSettings(mediaType) {
Expand Down Expand Up @@ -572,6 +566,17 @@ export function getStandardBidderSettings(mediaType) {
}
},
]

if (mediaType === 'video') {
[CONSTANTS.TARGETING_KEYS.UUID, CONSTANTS.TARGETING_KEYS.CACHE_ID].forEach(item => {
bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD][CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING].push({
key: item,
val: function val(bidResponse) {
return bidResponse.videoCacheKey;
}
})
});
}
}
return bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD];
}
Expand Down
4 changes: 3 additions & 1 deletion src/constants.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@
"SIZE": "hb_size",
"DEAL": "hb_deal",
"SOURCE": "hb_source",
"FORMAT": "hb_format"
"FORMAT": "hb_format",
"UUID": "hb_uuid",
"CACHE_ID": "hb_cache_id"
},
"NATIVE_KEYS": {
"title": "hb_native_title",
Expand Down
227 changes: 227 additions & 0 deletions test/spec/modules/prebidServerBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,10 @@ describe('S2S Adapter', function () {
prebid: {
aliases: {
brealtime: 'appnexus'
},
targeting: {
includebidderkeys: false,
includewinners: true
}
}
});
Expand Down Expand Up @@ -684,6 +688,10 @@ describe('S2S Adapter', function () {
prebid: {
aliases: {
[alias]: 'appnexus'
},
targeting: {
includebidderkeys: false,
includewinners: true
}
}
});
Expand Down Expand Up @@ -822,6 +830,146 @@ describe('S2S Adapter', function () {
expect(requestBid.user.ext.tpid.foo).is.equal('abc123');
expect(requestBid.user.ext.tpid.unifiedid).is.equal('1234');
})

it('always add ext.prebid.targeting.includebidderkeys: false for ORTB', function () {
const s2sConfig = Object.assign({}, CONFIG, {
endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction',
adapterOptions: {
appnexus: {
key: 'value'
}
}
});
const _config = {
s2sConfig: s2sConfig,
device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' },
app: { bundle: 'com.test.app' },
};

config.setConfig(_config);
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
const requestBid = JSON.parse(requests[0].requestBody);

expect(requestBid.ext.prebid.targeting).to.haveOwnProperty('includebidderkeys');
expect(requestBid.ext.prebid.targeting.includebidderkeys).to.equal(false);
});

it('always add ext.prebid.targeting.includewinners: true for ORTB', function () {
const s2sConfig = Object.assign({}, CONFIG, {
endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction',
adapterOptions: {
appnexus: {
key: 'value'
}
}
});
const _config = {
s2sConfig: s2sConfig,
device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' },
app: { bundle: 'com.test.app' },
};

config.setConfig(_config);
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
const requestBid = JSON.parse(requests[0].requestBody);

expect(requestBid.ext.prebid.targeting).to.haveOwnProperty('includewinners');
expect(requestBid.ext.prebid.targeting.includewinners).to.equal(true);
});

it('adds s2sConfig video.ext.prebid to request for ORTB', function () {
const s2sConfig = Object.assign({}, CONFIG, {
endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction',
extPrebid: {
foo: 'bar'
}
});
const _config = {
s2sConfig: s2sConfig,
device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' },
app: { bundle: 'com.test.app' },
};

config.setConfig(_config);
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
const requestBid = JSON.parse(requests[0].requestBody);

expect(requestBid).to.haveOwnProperty('ext');
expect(requestBid.ext).to.haveOwnProperty('prebid');
expect(requestBid.ext.prebid).to.deep.equal({
foo: 'bar',
targeting: {
includewinners: true,
includebidderkeys: false
}
});
});

it('overrides request.ext.prebid properties using s2sConfig video.ext.prebid values for ORTB', function () {
const s2sConfig = Object.assign({}, CONFIG, {
endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction',
extPrebid: {
targeting: {
includewinners: false,
includebidderkeys: true
}
}
});
const _config = {
s2sConfig: s2sConfig,
device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' },
app: { bundle: 'com.test.app' },
};

config.setConfig(_config);
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
const requestBid = JSON.parse(requests[0].requestBody);

expect(requestBid).to.haveOwnProperty('ext');
expect(requestBid.ext).to.haveOwnProperty('prebid');
expect(requestBid.ext.prebid).to.deep.equal({
targeting: {
includewinners: false,
includebidderkeys: true
}
});
});

it('overrides request.ext.prebid properties using s2sConfig video.ext.prebid values for ORTB', function () {
const s2sConfig = Object.assign({}, CONFIG, {
endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction',
extPrebid: {
cache: {
vastxml: 'vastxml-set-though-extPrebid.cache.vastXml'
},
targeting: {
includewinners: false,
includebidderkeys: false
}
}
});
const _config = {
s2sConfig: s2sConfig,
device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' },
app: { bundle: 'com.test.app' },
};

config.setConfig(_config);
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
const requestBid = JSON.parse(requests[0].requestBody);

expect(requestBid).to.haveOwnProperty('ext');
expect(requestBid.ext).to.haveOwnProperty('prebid');
expect(requestBid.ext.prebid).to.deep.equal({
cache: {
vastxml: 'vastxml-set-though-extPrebid.cache.vastXml'
},
targeting: {
includewinners: false,
includebidderkeys: false
}
});
});
});

describe('response handler', function () {
Expand Down Expand Up @@ -1058,6 +1206,85 @@ describe('S2S Adapter', function () {
expect(response).to.have.property('cpm', 10);
});

it('handles response cache from ext.prebid.cache.vastXml', function () {
const s2sConfig = Object.assign({}, CONFIG, {
endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param'
});
config.setConfig({s2sConfig});
const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO);
cacheResponse.seatbid.forEach(item => {
item.bid[0].ext.prebid.cache = {
vastXml: {
cacheId: 'abcd1234',
url: 'https://prebid-cache.net/cache?uuid=abcd1234'
}
}
});
server.respondWith(JSON.stringify(cacheResponse));
adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
server.respond();

sinon.assert.calledOnce(addBidResponse);
const response = addBidResponse.firstCall.args[1];

expect(response).to.have.property('statusMessage', 'Bid available');
expect(response).to.have.property('videoCacheKey', 'abcd1234');
expect(response).to.have.property('vastUrl', 'https://prebid-cache.net/cache?uuid=abcd1234');
});

it('add adserverTargeting object to bids when ext.prebid.targeting is defined', function () {
const s2sConfig = Object.assign({}, CONFIG, {
endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param'
});
config.setConfig({s2sConfig});
const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO);
const targetingTestData = {
hb_cache_path: '/cache',
hb_cache_host: 'prebid-cache.testurl.com'
};

cacheResponse.seatbid.forEach(item => {
item.bid[0].ext.prebid.targeting = targetingTestData
});
server.respondWith(JSON.stringify(cacheResponse));
adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
server.respond();

sinon.assert.calledOnce(addBidResponse);
const response = addBidResponse.firstCall.args[1];

expect(response).to.have.property('adserverTargeting');
expect(response.adserverTargeting).to.deep.equal({
'hb_cache_path': '/cache',
'hb_cache_host': 'prebid-cache.testurl.com'
});
});

it('handles response cache from ext.prebid.targeting', function () {
const s2sConfig = Object.assign({}, CONFIG, {
endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param'
});
config.setConfig({s2sConfig});
const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO);
cacheResponse.seatbid.forEach(item => {
item.bid[0].ext.prebid.targeting = {
hb_uuid: 'a5ad3993',
hb_cache_host: 'prebid-cache.net',
hb_cache_path: '/cache'
}
});
server.respondWith(JSON.stringify(cacheResponse));
adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
server.respond();

sinon.assert.calledOnce(addBidResponse);
const response = addBidResponse.firstCall.args[1];

expect(response).to.have.property('statusMessage', 'Bid available');
expect(response).to.have.property('videoCacheKey', 'a5ad3993');
expect(response).to.have.property('vastUrl', 'https://prebid-cache.net/cache?uuid=a5ad3993');
});

it('should log warning for unsupported bidder', function () {
server.respondWith(JSON.stringify(RESPONSE_UNSUPPORTED_BIDDER));

Expand Down

0 comments on commit 9d5b9b4

Please sign in to comment.