diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js
index 9476b7a76a3..5794dc86f3a 100644
--- a/modules/prebidServerBidAdapter/index.js
+++ b/modules/prebidServerBidAdapter/index.js
@@ -366,10 +366,55 @@ let nativeEventTrackerMethodMap = {
*/
let bidIdMap = {};
let nativeAssetCache = {}; // store processed native params to preserve
+
+/**
+ * map wurl to auction id and adId for use in the BID_WON event
+ */
+let wurlMap = {};
+
+/**
+ * @param {string} auctionId
+ * @param {string} adId generated value set to bidObject.adId by bidderFactory Bid()
+ * @param {string} wurl events.winurl passed from prebidServer as wurl
+ */
+function addWurl(auctionId, adId, wurl) {
+ if ([auctionId, adId].every(utils.isStr)) {
+ wurlMap[`${auctionId}${adId}`] = wurl;
+ }
+}
+
+/**
+ * @param {string} auctionId
+ * @param {string} adId generated value set to bidObject.adId by bidderFactory Bid()
+ */
+function removeWurl(auctionId, adId) {
+ if ([auctionId, adId].every(utils.isStr)) {
+ wurlMap[`${auctionId}${adId}`] = undefined;
+ }
+}
+/**
+ * @param {string} auctionId
+ * @param {string} adId generated value set to bidObject.adId by bidderFactory Bid()
+ * @return {(string|undefined)} events.winurl which was passed as wurl
+ */
+function getWurl(auctionId, adId) {
+ if ([auctionId, adId].every(utils.isStr)) {
+ return wurlMap[`${auctionId}${adId}`];
+ }
+}
+
+/**
+ * remove all cached wurls
+ */
+export function resetWurlMap() {
+ wurlMap = {};
+}
+
const OPEN_RTB_PROTOCOL = {
buildRequest(s2sBidRequest, bidRequests, adUnits) {
let imps = [];
let aliases = {};
+ const firstBidRequest = bidRequests[0];
// transform ad unit into array of OpenRTB impression objects
adUnits.forEach(adUnit => {
@@ -522,7 +567,7 @@ const OPEN_RTB_PROTOCOL = {
Object.assign(imp, mediaTypes);
// if storedAuctionResponse has been set, pass SRID
- const storedAuctionResponseBid = find(bidRequests[0].bids, bid => (bid.adUnitCode === adUnit.code && bid.storedAuctionResponse));
+ const storedAuctionResponseBid = find(firstBidRequest.bids, bid => (bid.adUnitCode === adUnit.code && bid.storedAuctionResponse));
if (storedAuctionResponseBid) {
utils.deepSetValue(imp, 'ext.prebid.storedauctionresponse.id', storedAuctionResponseBid.storedAuctionResponse.toString());
}
@@ -544,6 +589,8 @@ const OPEN_RTB_PROTOCOL = {
test: getConfig('debug') ? 1 : 0,
ext: {
prebid: {
+ // set ext.prebid.auctiontimestamp with the auction timestamp. Data type is long integer.
+ auctiontimestamp: firstBidRequest.auctionStart,
targeting: {
// includewinners is always true for openrtb
includewinners: true,
@@ -571,9 +618,9 @@ const OPEN_RTB_PROTOCOL = {
request.cur = [adServerCur[0]];
}
- _appendSiteAppDevice(request, bidRequests[0].refererInfo.referer);
+ _appendSiteAppDevice(request, firstBidRequest.refererInfo.referer);
- const digiTrust = _getDigiTrustQueryParams(bidRequests && bidRequests[0]);
+ const digiTrust = _getDigiTrustQueryParams(firstBidRequest);
if (digiTrust) {
utils.deepSetValue(request, 'user.ext.digitrust', digiTrust);
}
@@ -596,19 +643,19 @@ const OPEN_RTB_PROTOCOL = {
}
if (bidRequests) {
- if (bidRequests[0].gdprConsent) {
+ if (firstBidRequest.gdprConsent) {
// note - gdprApplies & consentString may be undefined in certain use-cases for consentManagement module
let gdprApplies;
- if (typeof bidRequests[0].gdprConsent.gdprApplies === 'boolean') {
- gdprApplies = bidRequests[0].gdprConsent.gdprApplies ? 1 : 0;
+ if (typeof firstBidRequest.gdprConsent.gdprApplies === 'boolean') {
+ gdprApplies = firstBidRequest.gdprConsent.gdprApplies ? 1 : 0;
}
utils.deepSetValue(request, 'regs.ext.gdpr', gdprApplies);
- utils.deepSetValue(request, 'user.ext.consent', bidRequests[0].gdprConsent.consentString);
+ utils.deepSetValue(request, 'user.ext.consent', firstBidRequest.gdprConsent.consentString);
}
// US Privacy (CCPA) support
- if (bidRequests[0].uspConsent) {
- utils.deepSetValue(request, 'regs.ext.us_privacy', bidRequests[0].uspConsent);
+ if (firstBidRequest.uspConsent) {
+ utils.deepSetValue(request, 'regs.ext.us_privacy', firstBidRequest.uspConsent);
}
}
@@ -658,10 +705,28 @@ const OPEN_RTB_PROTOCOL = {
bidRequest.serverResponseTimeMs = serverResponseTimeMs;
}
- const extPrebidTargeting = utils.deepAccess(bid, 'ext.prebid.targeting');
+ // Look for seatbid[].bid[].ext.prebid.bidid and place it in the bidResponse object for use in analytics adapters as 'pbsBidId'
+ const bidId = utils.deepAccess(bid, 'ext.prebid.bidid');
+ if (utils.isStr(bidId)) {
+ bidObject.pbsBidId = bidId;
+ }
+
+ // store wurl by auctionId and adId so it can be accessed from the BID_WON event handler
+ if (utils.isStr(utils.deepAccess(bid, 'ext.prebid.events.win'))) {
+ addWurl(bidRequest.auctionId, bidObject.adId, utils.deepAccess(bid, 'ext.prebid.events.win'));
+ }
+
+ let 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') {
+ // The removal of hb_winurl and hb_bidid targeting values is temporary
+ // once we get through the transition, this block will be removed.
+ if (utils.isPlainObject(extPrebidTargeting)) {
+ // If wurl exists, remove hb_winurl and hb_bidid targeting attributes
+ if (utils.isStr(utils.deepAccess(bid, 'ext.prebid.events.win'))) {
+ extPrebidTargeting = utils.getDefinedParams(extPrebidTargeting, Object.keys(extPrebidTargeting)
+ .filter(i => (i.indexOf('hb_winurl') === -1 && i.indexOf('hb_bidid') === -1)));
+ }
bidObject.adserverTargeting = extPrebidTargeting;
}
@@ -773,6 +838,21 @@ const OPEN_RTB_PROTOCOL = {
}
};
+/**
+ * BID_WON event to request the wurl
+ * @param {Bid} bid the winning bid object
+ */
+function bidWonHandler(bid) {
+ const wurl = getWurl(bid.auctionId, bid.adId);
+ if (utils.isStr(wurl)) {
+ utils.logMessage(`Invoking image pixel for wurl on BID_WIN: "${wurl}"`);
+ utils.triggerPixel(wurl);
+
+ // remove from wurl cache, since the wurl url was called
+ removeWurl(bid.auctionId, bid.adId);
+ }
+}
+
/**
* Bidder adapter for Prebid Server
*/
@@ -856,6 +936,9 @@ export function PrebidServer() {
doClientSideSyncs(requestedBidders);
}
+ // Listen for bid won to call wurl
+ events.on(EVENTS.BID_WON, bidWonHandler);
+
return Object.assign(this, {
callBids: baseAdapter.callBids,
setBidderCode: baseAdapter.setBidderCode,
diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js
index 8f30a38723b..00ad14dd316 100644
--- a/modules/rubiconAnalyticsAdapter.js
+++ b/modules/rubiconAnalyticsAdapter.js
@@ -87,7 +87,7 @@ function sendMessage(auctionId, bidWonId) {
function formatBid(bid) {
return utils.pick(bid, [
'bidder',
- 'bidId', bidId => utils.deepAccess(bid, 'bidResponse.seatBidId') || bidId,
+ 'bidId', bidId => utils.deepAccess(bid, 'bidResponse.pbsBidId') || utils.deepAccess(bid, 'bidResponse.seatBidId') || bidId,
'status',
'error',
'source', (source, bid) => {
diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js
index 3c17f609a37..f09bc51e2c5 100644
--- a/modules/rubiconBidAdapter.js
+++ b/modules/rubiconBidAdapter.js
@@ -339,6 +339,9 @@ export const spec = {
utils.deepSetValue(data.imp[0], 'ext.prebid.storedauctionresponse.id', bidRequest.storedAuctionResponse.toString());
}
+ // set ext.prebid.auctiontimestamp using auction time
+ utils.deepSetValue(data.imp[0], 'ext.prebid.auctiontimestamp', bidderRequest.auctionStart);
+
return {
method: 'POST',
url: VIDEO_ENDPOINT,
diff --git a/src/auction.js b/src/auction.js
index 6166e59bbf0..b82b4752479 100644
--- a/src/auction.js
+++ b/src/auction.js
@@ -483,7 +483,7 @@ export const callPrebidCache = hook('async', function(auctionInstance, bidRespon
afterBidAdded();
}
}
- });
+ }, bidderRequest);
}, 'callPrebidCache');
// Postprocess the bids so that all the universal properties exist, no matter which bidder they came from.
diff --git a/src/videoCache.js b/src/videoCache.js
index 46bf74ee553..9f1fd7e4117 100644
--- a/src/videoCache.js
+++ b/src/videoCache.js
@@ -10,7 +10,8 @@
*/
import { ajax } from './ajax.js';
-import { config } from '../src/config.js';
+import { config } from './config.js';
+import * as utils from './utils.js';
/**
* @typedef {object} CacheableUrlBid
@@ -70,6 +71,10 @@ function toStorageRequest(bid) {
if (config.getConfig('cache.vasttrack')) {
payload.bidder = bid.bidder;
payload.bidid = bid.requestId;
+ // function has a thisArg set to bidderRequest for accessing the auctionStart
+ if (utils.isPlainObject(this) && this.hasOwnProperty('auctionStart')) {
+ payload.timestamp = this.auctionStart;
+ }
}
if (typeof bid.customCacheKey === 'string' && bid.customCacheKey !== '') {
@@ -126,11 +131,12 @@ function shimStorageCallback(done) {
*
* @param {CacheableBid[]} bids A list of bid objects which should be cached.
* @param {videoCacheStoreCallback} [done] An optional callback which should be executed after
+ * @param {BidderRequest} [bidderRequest]
* the data has been stored in the cache.
*/
-export function store(bids, done) {
+export function store(bids, done, bidderRequest) {
const requestData = {
- puts: bids.map(toStorageRequest)
+ puts: bids.map(toStorageRequest, bidderRequest)
};
ajax(config.getConfig('cache.url'), shimStorageCallback(done), JSON.stringify(requestData), {
diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js
index 4744bef0ee3..f66c87582f5 100644
--- a/test/spec/modules/prebidServerBidAdapter_spec.js
+++ b/test/spec/modules/prebidServerBidAdapter_spec.js
@@ -1,5 +1,5 @@
import { expect } from 'chai';
-import { PrebidServer as Adapter, resetSyncedStatus } from 'modules/prebidServerBidAdapter/index.js';
+import { PrebidServer as Adapter, resetSyncedStatus, resetWurlMap } from 'modules/prebidServerBidAdapter/index.js';
import adapterManager from 'src/adapterManager.js';
import * as utils from 'src/utils.js';
import { ajax } from 'src/ajax.js';
@@ -261,7 +261,12 @@ const RESPONSE_OPENRTB = {
'w': 300,
'h': 250,
'ext': {
- 'prebid': { 'type': 'banner' },
+ 'prebid': {
+ 'type': 'banner',
+ 'event': {
+ 'win': 'http://wurl.org?id=333'
+ }
+ },
'bidder': {
'appnexus': {
'brand_id': 1,
@@ -304,6 +309,7 @@ const RESPONSE_OPENRTB_VIDEO = {
ext: {
prebid: {
type: 'video',
+ bidid: '654321'
},
bidder: {
appnexus: {
@@ -432,6 +438,7 @@ describe('S2S Adapter', function () {
done = sinon.spy();
beforeEach(function () {
+ config.resetConfig();
adapter = new Adapter();
BID_REQUESTS = [
{
@@ -481,16 +488,15 @@ describe('S2S Adapter', function () {
done.resetHistory();
});
+ after(function () {
+ config.resetConfig();
+ });
+
describe('request function', function () {
beforeEach(function () {
- config.resetConfig();
resetSyncedStatus();
});
- afterEach(function () {
- config.resetConfig();
- });
-
it('should not add outstrean without renderer', function () {
let ortb2Config = utils.deepClone(CONFIG);
ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction';
@@ -509,7 +515,6 @@ describe('S2S Adapter', function () {
describe('gdpr tests', function () {
afterEach(function () {
- config.resetConfig();
$$PREBID_GLOBAL$$.requestBids.removeAll();
});
@@ -608,7 +613,6 @@ describe('S2S Adapter', function () {
describe('us_privacy (ccpa) consent data', function () {
afterEach(function () {
- config.resetConfig();
$$PREBID_GLOBAL$$.requestBids.removeAll();
});
@@ -653,7 +657,6 @@ describe('S2S Adapter', function () {
describe('gdpr and us_privacy (ccpa) consent data', function () {
afterEach(function () {
- config.resetConfig();
$$PREBID_GLOBAL$$.requestBids.removeAll();
});
@@ -955,6 +958,7 @@ describe('S2S Adapter', function () {
aliases: {
brealtime: 'appnexus'
},
+ auctiontimestamp: 1510852447530,
targeting: {
includebidderkeys: false,
includewinners: true
@@ -989,6 +993,7 @@ describe('S2S Adapter', function () {
aliases: {
[alias]: 'appnexus'
},
+ auctiontimestamp: 1510852447530,
targeting: {
includebidderkeys: false,
includewinners: true
@@ -1240,6 +1245,7 @@ describe('S2S Adapter', function () {
expect(requestBid).to.haveOwnProperty('ext');
expect(requestBid.ext).to.haveOwnProperty('prebid');
expect(requestBid.ext.prebid).to.deep.equal({
+ auctiontimestamp: 1510852447530,
foo: 'bar',
targeting: {
includewinners: true,
@@ -1271,6 +1277,7 @@ describe('S2S Adapter', function () {
expect(requestBid).to.haveOwnProperty('ext');
expect(requestBid.ext).to.haveOwnProperty('prebid');
expect(requestBid.ext.prebid).to.deep.equal({
+ auctiontimestamp: 1510852447530,
targeting: {
includewinners: false,
includebidderkeys: true
@@ -1304,6 +1311,7 @@ describe('S2S Adapter', function () {
expect(requestBid).to.haveOwnProperty('ext');
expect(requestBid.ext).to.haveOwnProperty('prebid');
expect(requestBid.ext.prebid).to.deep.equal({
+ auctiontimestamp: 1510852447530,
cache: {
vastxml: 'vastxml-set-though-extPrebid.cache.vastXml'
},
@@ -1459,67 +1467,52 @@ describe('S2S Adapter', function () {
});
describe('response handler', function () {
- let server;
- let logWarnSpy;
-
beforeEach(function () {
- server = sinon.fakeServer.create();
sinon.stub(utils, 'triggerPixel');
sinon.stub(utils, 'insertUserSyncIframe');
sinon.stub(utils, 'logError');
sinon.stub(events, 'emit');
- logWarnSpy = sinon.spy(utils, 'logWarn');
});
afterEach(function () {
- server.restore();
utils.triggerPixel.restore();
utils.insertUserSyncIframe.restore();
utils.logError.restore();
events.emit.restore();
- logWarnSpy.restore();
});
// TODO: test dependent on pbjs_api_spec. Needs to be isolated
it('does not call addBidResponse and calls done when ad unit not set', function () {
- server.respondWith(JSON.stringify(RESPONSE_NO_BID_NO_UNIT));
-
config.setConfig({ s2sConfig: CONFIG });
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
- server.respond();
+ server.requests[0].respond(200, {}, JSON.stringify(RESPONSE_NO_BID_NO_UNIT));
sinon.assert.notCalled(addBidResponse);
sinon.assert.calledOnce(done);
});
it('does not call addBidResponse and calls done when server requests cookie sync', function () {
- server.respondWith(JSON.stringify(RESPONSE_NO_COOKIE));
-
config.setConfig({ s2sConfig: CONFIG });
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
- server.respond();
+ server.requests[0].respond(200, {}, JSON.stringify(RESPONSE_NO_COOKIE));
sinon.assert.notCalled(addBidResponse);
sinon.assert.calledOnce(done);
});
it('does not call addBidResponse and calls done when ad unit is set', function () {
- server.respondWith(JSON.stringify(RESPONSE_NO_BID_UNIT_SET));
-
config.setConfig({ s2sConfig: CONFIG });
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
- server.respond();
+ server.requests[0].respond(200, {}, JSON.stringify(RESPONSE_NO_BID_UNIT_SET));
sinon.assert.notCalled(addBidResponse);
sinon.assert.calledOnce(done);
});
it('registers successful bids and calls done when there are less bids than requests', function () {
- server.respondWith(JSON.stringify(RESPONSE_OPENRTB));
-
config.setConfig({ s2sConfig: CONFIG });
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
- server.respond();
+ server.requests[0].respond(200, {}, JSON.stringify(RESPONSE_OPENRTB));
sinon.assert.calledOnce(addBidResponse);
sinon.assert.calledOnce(done);
@@ -1533,11 +1526,9 @@ describe('S2S Adapter', function () {
});
it('should have dealId in bidObject', function () {
- server.respondWith(JSON.stringify(RESPONSE_OPENRTB));
-
config.setConfig({ s2sConfig: CONFIG });
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
- server.respond();
+ server.requests[0].respond(200, {}, JSON.stringify(RESPONSE_OPENRTB));
const response = addBidResponse.firstCall.args[1];
expect(response).to.have.property('dealId', 'test-dealid');
});
@@ -1553,9 +1544,8 @@ describe('S2S Adapter', function () {
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();
+ server.requests[0].respond(200, {}, JSON.stringify(cacheResponse));
sinon.assert.calledOnce(addBidResponse);
const response = addBidResponse.firstCall.args[1];
@@ -1567,9 +1557,8 @@ describe('S2S Adapter', function () {
});
it('should set the bidResponse currency to whats in the PBS response', function() {
- server.respondWith(JSON.stringify(RESPONSE_OPENRTB));
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
- server.respond();
+ server.requests[0].respond(200, {}, JSON.stringify(RESPONSE_OPENRTB));
sinon.assert.calledOnce(addBidResponse);
const pbjsResponse = addBidResponse.firstCall.args[1];
expect(pbjsResponse).to.have.property('currency', 'EUR');
@@ -1578,9 +1567,8 @@ describe('S2S Adapter', function () {
it('should set the default bidResponse currency when not specified in OpenRTB', function() {
let modifiedResponse = utils.deepClone(RESPONSE_OPENRTB);
modifiedResponse.cur = '';
- server.respondWith(JSON.stringify(modifiedResponse));
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
- server.respond();
+ server.requests[0].respond(200, {}, JSON.stringify(modifiedResponse));
sinon.assert.calledOnce(addBidResponse);
const pbjsResponse = addBidResponse.firstCall.args[1];
expect(pbjsResponse).to.have.property('currency', 'USD');
@@ -1597,11 +1585,9 @@ describe('S2S Adapter', function () {
item.bid[0].ext.prebid.targeting = targetingTestData
});
- server.respondWith(JSON.stringify(cacheResponse));
-
config.setConfig({ s2sConfig: CONFIG });
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
- server.respond();
+ server.requests[0].respond(200, {}, JSON.stringify(cacheResponse));
sinon.assert.calledOnce(addBidResponse);
const response = addBidResponse.firstCall.args[1];
expect(response).to.have.property('adserverTargeting').that.deep.equals({ 'foo': 'bar' });
@@ -1613,11 +1599,9 @@ describe('S2S Adapter', function () {
};
sinon.stub(adapterManager, 'getBidAdapter').callsFake(() => rubiconAdapter);
- server.respondWith(JSON.stringify(RESPONSE_NO_PBS_COOKIE));
-
config.setConfig({ s2sConfig: CONFIG });
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
- server.respond();
+ server.requests[0].respond(200, {}, JSON.stringify(RESPONSE_NO_PBS_COOKIE));
sinon.assert.calledOnce(rubiconAdapter.registerSyncs);
@@ -1635,9 +1619,8 @@ describe('S2S Adapter', function () {
});
config.setConfig({ s2sConfig });
- server.respondWith(JSON.stringify(RESPONSE_OPENRTB));
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
- server.respond();
+ server.requests[0].respond(200, {}, JSON.stringify(RESPONSE_OPENRTB));
sinon.assert.calledOnce(rubiconAdapter.registerSyncs);
@@ -1650,9 +1633,8 @@ describe('S2S Adapter', function () {
});
config.setConfig({ s2sConfig });
- server.respondWith(JSON.stringify(RESPONSE_OPENRTB));
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
- server.respond();
+ server.requests[0].respond(200, {}, JSON.stringify(RESPONSE_OPENRTB));
sinon.assert.calledOnce(events.emit);
const event = events.emit.firstCall.args;
@@ -1675,9 +1657,8 @@ describe('S2S Adapter', function () {
});
config.setConfig({ s2sConfig });
- server.respondWith(JSON.stringify(RESPONSE_OPENRTB_VIDEO));
adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
- server.respond();
+ server.requests[0].respond(200, {}, JSON.stringify(RESPONSE_OPENRTB_VIDEO));
sinon.assert.calledOnce(addBidResponse);
const response = addBidResponse.firstCall.args[1];
@@ -1703,9 +1684,9 @@ describe('S2S Adapter', function () {
}
}
});
- server.respondWith(JSON.stringify(cacheResponse));
+
adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
- server.respond();
+ server.requests[0].respond(200, {}, JSON.stringify(cacheResponse));
sinon.assert.calledOnce(addBidResponse);
const response = addBidResponse.firstCall.args[1];
@@ -1729,9 +1710,8 @@ describe('S2S Adapter', function () {
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();
+ server.requests[0].respond(200, {}, JSON.stringify(cacheResponse));
sinon.assert.calledOnce(addBidResponse);
const response = addBidResponse.firstCall.args[1];
@@ -1756,9 +1736,8 @@ describe('S2S Adapter', function () {
hb_cache_path: '/cache'
}
});
- server.respondWith(JSON.stringify(cacheResponse));
adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
- server.respond();
+ server.requests[0].respond(200, {}, JSON.stringify(cacheResponse));
sinon.assert.calledOnce(addBidResponse);
const response = addBidResponse.firstCall.args[1];
@@ -1768,6 +1747,77 @@ describe('S2S Adapter', function () {
expect(response).to.have.property('vastUrl', 'https://prebid-cache.net/cache?uuid=a5ad3993');
});
+ it('handles response cache from ext.prebid.targeting with wurl', 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.events = {
+ win: 'https://wurl.com?a=1&b=2'
+ };
+ item.bid[0].ext.prebid.targeting = {
+ hb_uuid: 'a5ad3993',
+ hb_cache_host: 'prebid-cache.net',
+ hb_cache_path: '/cache'
+ }
+ });
+ adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
+ server.requests[0].respond(200, {}, JSON.stringify(cacheResponse));
+
+ sinon.assert.calledOnce(addBidResponse);
+ const response = addBidResponse.firstCall.args[1];
+ expect(response).to.have.property('pbsBidId', '654321');
+ });
+
+ it('handles response cache from ext.prebid.targeting with wurl and removes invalid 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.events = {
+ win: 'https://wurl.com?a=1&b=2'
+ };
+ item.bid[0].ext.prebid.targeting = {
+ hb_uuid: 'a5ad3993',
+ hb_cache_host: 'prebid-cache.net',
+ hb_cache_path: '/cache',
+ hb_winurl: 'https://hbwinurl.com?a=1&b=2',
+ hb_bidid: '1234567890',
+ }
+ });
+ adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
+ server.requests[0].respond(200, {}, JSON.stringify(cacheResponse));
+
+ sinon.assert.calledOnce(addBidResponse);
+ const response = addBidResponse.firstCall.args[1];
+
+ expect(response.adserverTargeting).to.deep.equal({
+ hb_uuid: 'a5ad3993',
+ hb_cache_host: 'prebid-cache.net',
+ hb_cache_path: '/cache'
+ });
+ });
+
+ it('add request property pbsBidId with ext.prebid.bidid value', function () {
+ const s2sConfig = Object.assign({}, CONFIG, {
+ endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param'
+ });
+ config.setConfig({ s2sConfig });
+ const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO);
+
+ adapter.callBids(VIDEO_REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
+ server.requests[0].respond(200, {}, JSON.stringify(cacheResponse));
+
+ sinon.assert.calledOnce(addBidResponse);
+ const response = addBidResponse.firstCall.args[1];
+
+ expect(response).to.have.property('pbsBidId', '654321');
+ });
+
it('handles OpenRTB native responses', function () {
sinon.stub(utils, 'getBidRequest').returns({
adUnitCode: 'div-gpt-ad-1460505748561-0',
@@ -1779,9 +1829,8 @@ describe('S2S Adapter', function () {
});
config.setConfig({ s2sConfig });
- server.respondWith(JSON.stringify(RESPONSE_OPENRTB_NATIVE));
adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
- server.respond();
+ server.requests[0].respond(200, {}, JSON.stringify(RESPONSE_OPENRTB_NATIVE));
sinon.assert.calledOnce(addBidResponse);
const response = addBidResponse.firstCall.args[1];
@@ -1796,6 +1845,98 @@ describe('S2S Adapter', function () {
});
});
+ describe('bid won events', function () {
+ let uniqueIdCount = 0;
+ let triggerPixelStub;
+ const staticUniqueIds = ['1000', '1001', '1002', '1003'];
+
+ before(function () {
+ triggerPixelStub = sinon.stub(utils, 'triggerPixel');
+ });
+
+ beforeEach(function () {
+ resetWurlMap();
+ sinon.stub(utils, 'insertUserSyncIframe');
+ sinon.stub(utils, 'logError');
+ sinon.stub(utils, 'getUniqueIdentifierStr').callsFake(() => {
+ uniqueIdCount++;
+ return staticUniqueIds[uniqueIdCount - 1];
+ });
+ triggerPixelStub.resetHistory();
+
+ config.setConfig({
+ s2sConfig: Object.assign({}, CONFIG, {
+ endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'
+ })
+ });
+ });
+
+ afterEach(function () {
+ utils.triggerPixel.resetHistory();
+ utils.insertUserSyncIframe.restore();
+ utils.logError.restore();
+ utils.getUniqueIdentifierStr.restore();
+ uniqueIdCount = 0;
+ });
+
+ after(function () {
+ triggerPixelStub.restore();
+ });
+
+ it('should call triggerPixel if wurl is defined', function () {
+ const clonedResponse = utils.deepClone(RESPONSE_OPENRTB);
+ clonedResponse.seatbid[0].bid[0].ext.prebid.events = {
+ win: 'https://wurl.org'
+ };
+
+ adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
+ server.requests[0].respond(200, {}, JSON.stringify(clonedResponse));
+
+ events.emit(CONSTANTS.EVENTS.BID_WON, {
+ auctionId: '173afb6d132ba3',
+ adId: '1000'
+ });
+
+ sinon.assert.calledOnce(addBidResponse);
+ expect(utils.triggerPixel.called).to.be.true;
+ expect(utils.triggerPixel.getCall(0).args[0]).to.include('https://wurl.org');
+ });
+
+ it('should not call triggerPixel if the wurl cache does not contain the winning bid', function () {
+ const clonedResponse = utils.deepClone(RESPONSE_OPENRTB);
+ clonedResponse.seatbid[0].bid[0].ext.prebid.events = {
+ win: 'https://wurl.org'
+ };
+
+ adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
+ server.requests[0].respond(200, {}, JSON.stringify(clonedResponse));
+
+ events.emit(CONSTANTS.EVENTS.BID_WON, {
+ auctionId: '173afb6d132ba3',
+ adId: 'missingAdId'
+ });
+
+ sinon.assert.calledOnce(addBidResponse)
+ expect(utils.triggerPixel.called).to.be.false;
+ });
+
+ it('should not call triggerPixel if wurl is undefined', function () {
+ const clonedResponse = utils.deepClone(RESPONSE_OPENRTB);
+ clonedResponse.seatbid[0].bid[0].ext.prebid.events = {};
+
+ adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax);
+ server.requests[0].respond(200, {}, JSON.stringify(clonedResponse));
+
+ events.emit(CONSTANTS.EVENTS.BID_WON, {
+ auctionId: '173afb6d132ba3',
+ adId: '1060'
+ });
+
+ sinon.assert.calledOnce(addBidResponse)
+ expect(utils.triggerPixel.called).to.be.false;
+ });
+ })
+
describe('s2sConfig', function () {
let logErrorSpy;
@@ -1901,6 +2042,14 @@ describe('S2S Adapter', function () {
});
it('should return proper defaults', function () {
+ const options = {
+ accountId: 'abc',
+ bidders: ['rubicon'],
+ defaultVendor: 'rubicon',
+ timeout: 750
+ };
+
+ config.setConfig({ s2sConfig: options });
expect(config.getConfig('s2sConfig')).to.deep.equal({
'accountId': 'abc',
'adapter': 'prebidServer',
diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js
index 99928b41d7b..11ed17df5f8 100644
--- a/test/spec/modules/rubiconBidAdapter_spec.js
+++ b/test/spec/modules/rubiconBidAdapter_spec.js
@@ -1461,6 +1461,7 @@ describe('the rubicon adapter', function () {
expect(post.site.content.language).to.equal('en');
expect(imp.ext.rubicon.video.skip).to.equal(1);
expect(imp.ext.rubicon.video.skipafter).to.equal(15);
+ expect(imp.ext.prebid.auctiontimestamp).to.equal(1472239426000);
expect(post.user.ext.consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A==');
expect(post.user.ext.eids[0].source).to.equal('liveintent.com');
expect(post.user.ext.eids[0].uids[0].id).to.equal('0000-1111-2222-3333');
diff --git a/test/spec/videoCache_spec.js b/test/spec/videoCache_spec.js
index 7d706947416..6bb214af8a0 100644
--- a/test/spec/videoCache_spec.js
+++ b/test/spec/videoCache_spec.js
@@ -5,6 +5,50 @@ import { server } from 'test/mocks/xhr.js';
const should = chai.should();
+function getMockBid(bidder, auctionId, bidderRequestId) {
+ return {
+ 'bidder': bidder,
+ 'params': {
+ 'placementId': '10433394',
+ 'member': 123,
+ 'keywords': {
+ 'foo': ['bar', 'baz'],
+ 'fizz': ['buzz']
+ }
+ },
+ 'bid_id': '12345abc',
+ 'adUnitCode': 'div-gpt-ad-1460505748561-0',
+ 'mediaTypes': {
+ 'banner': {
+ 'sizes': [[300, 250]]
+ }
+ },
+ 'transactionId': '4ef956ad-fd83-406d-bd35-e4bb786ab86c',
+ 'sizes': [300, 250],
+ 'bidId': '123',
+ 'bidderRequestId': bidderRequestId,
+ 'auctionId': auctionId,
+ 'storedAuctionResponse': 11111
+ };
+}
+
+function getMockBidRequest(bidder = 'appnexus', auctionId = '173afb6d132ba3', bidderRequestId = '3d1063078dfcc8') {
+ return {
+ 'bidderCode': bidder,
+ 'auctionId': auctionId,
+ 'bidderRequestId': bidderRequestId,
+ 'tid': '437fbbf5-33f5-487a-8e16-a7112903cfe5',
+ 'bids': [getMockBid(bidder, auctionId, bidderRequestId)],
+ 'auctionStart': 1510852447530,
+ 'timeout': 5000,
+ 'src': 's2s',
+ 'doneCbCallCount': 0,
+ 'refererInfo': {
+ 'referer': 'http://mytestpage.com'
+ }
+ }
+}
+
describe('The video cache', function () {
function assertError(callbackSpy) {
callbackSpy.calledOnce.should.equal(true);
@@ -192,6 +236,61 @@ describe('The video cache', function () {
JSON.parse(request.requestBody).should.deep.equal(payload);
});
+ it('should include additional params in request payload should config.cache.vasttrack be true and bidderRequest argument was defined', () => {
+ config.setConfig({
+ cache: {
+ url: 'https://prebid.adnxs.com/pbc/v1/cache',
+ vasttrack: true
+ }
+ });
+
+ const customKey1 = 'vasttrack_123';
+ const customKey2 = 'vasttrack_abc';
+ const vastXml1 = 'testvast1';
+ const vastXml2 = 'testvast2';
+
+ const bids = [{
+ vastXml: vastXml1,
+ ttl: 25,
+ customCacheKey: customKey1,
+ requestId: '12345abc',
+ bidder: 'appnexus'
+ }, {
+ vastXml: vastXml2,
+ ttl: 25,
+ customCacheKey: customKey2,
+ requestId: 'cba54321',
+ bidder: 'rubicon'
+ }];
+
+ store(bids, function () { }, getMockBidRequest());
+ const request = server.requests[0];
+ request.method.should.equal('POST');
+ request.url.should.equal('https://prebid.adnxs.com/pbc/v1/cache');
+ request.requestHeaders['Content-Type'].should.equal('text/plain;charset=utf-8');
+ let payload = {
+ puts: [{
+ type: 'xml',
+ value: vastXml1,
+ ttlseconds: 25,
+ key: customKey1,
+ bidid: '12345abc',
+ bidder: 'appnexus',
+ timestamp: 1510852447530
+ }, {
+ type: 'xml',
+ value: vastXml2,
+ ttlseconds: 25,
+ key: customKey2,
+ bidid: 'cba54321',
+ bidder: 'rubicon',
+ timestamp: 1510852447530
+ }]
+ };
+
+ JSON.parse(request.requestBody).should.deep.equal(payload);
+ });
+
function assertRequestMade(bid, expectedValue) {
store([bid], function () { });