diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 7ffaf9988dd..3d191fdfcd8 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -281,157 +281,6 @@ function _appendSiteAppDevice(request) { } } -function transformHeightWidth(adUnit) { - let sizesObj = []; - let sizes = utils.parseSizesInput(adUnit.sizes); - sizes.forEach(size => { - let heightWidth = size.split('x'); - let sizeObj = { - 'w': parseInt(heightWidth[0]), - 'h': parseInt(heightWidth[1]) - }; - sizesObj.push(sizeObj); - }); - return sizesObj; -} - -/* - * Protocol spec for legacy endpoint - * e.g., https:///v1/auction - */ -const LEGACY_PROTOCOL = { - - buildRequest(s2sBidRequest, bidRequests, adUnits) { - adUnits.forEach(adUnit => { - adUnit.bids.forEach(bid => { - const adapter = adapterManager.bidderRegistry[bid.bidder]; - if (adapter && adapter.getSpec().transformBidParams) { - bid.params = adapter.getSpec().transformBidParams(bid.params, isOpenRtb()); - } - }); - }); - - // pbs expects an ad_unit.video attribute if the imp is video - adUnits.forEach(adUnit => { - adUnit.sizes = transformHeightWidth(adUnit); - const videoMediaType = utils.deepAccess(adUnit, 'mediaTypes.video'); - if (videoMediaType) { - adUnit.video = Object.assign({}, videoMediaType); - delete adUnit.mediaTypes; - // default is assumed to be 'banner' so if there is a video type - // we assume video only until PBS can support multi-format auction - adUnit.media_types = [VIDEO]; - } - }); - - const request = { - account_id: _s2sConfig.accountId, - tid: s2sBidRequest.tid, - max_bids: _s2sConfig.maxBids, - timeout_millis: _s2sConfig.timeout, - secure: _s2sConfig.secure, - cache_markup: _s2sConfig.cacheMarkup === 1 || _s2sConfig.cacheMarkup === 2 ? _s2sConfig.cacheMarkup : 0, - url: utils.getTopWindowUrl(), - prebid_version: '$prebid.version$', - ad_units: adUnits, - is_debug: !!getConfig('debug'), - }; - - _appendSiteAppDevice(request); - - let digiTrust = _getDigiTrustQueryParams(bidRequests && bidRequests[0]); - if (digiTrust) { - request.digiTrust = digiTrust; - } - - return request; - }, - - interpretResponse(result, bidderRequests) { - const bids = []; - if (result.status === 'OK' || result.status === 'no_cookie') { - if (result.bidder_status) { - result.bidder_status.forEach(bidder => { - if (bidder.error) { - utils.logWarn(`Prebid Server returned error: '${bidder.error}' for ${bidder.bidder}`); - } - - bidderRequests.filter(bidderRequest => bidderRequest.bidderCode === bidder.bidder) - .forEach(bidderRequest => - (bidderRequest.bids || []).forEach(bid => - bid.serverResponseTimeMs = bidder.response_time_ms - ) - ) - }); - } - - if (result.bids) { - result.bids.forEach(bidObj => { - const bidRequest = utils.getBidRequest(bidObj.bid_id, bidderRequests); - const cpm = bidObj.price; - const status = cpm !== 0 ? STATUS.GOOD : STATUS.NO_BID; - let bidObject = createBid(status, bidRequest); - - bidObject.source = TYPE; - bidObject.creative_id = bidObj.creative_id; - bidObject.bidderCode = bidObj.bidder; - bidObject.cpm = cpm; - if (bidObj.cache_id) { - bidObject.cache_id = bidObj.cache_id; - } - if (bidObj.cache_url) { - bidObject.cache_url = bidObj.cache_url; - } - // From ORTB see section 4.2.3: adm Optional means of conveying ad markup in case the bid wins; supersedes the win notice if markup is included in both. - if (bidObj.media_type === VIDEO) { - bidObject.mediaType = VIDEO; - if (bidObj.adm) { - bidObject.vastXml = bidObj.adm; - } - if (bidObj.nurl) { - bidObject.vastUrl = bidObj.nurl; - } - // when video bid is already cached by Prebid Server, videoCacheKey and vastUrl should be provided properly - if (bidObj.cache_id && bidObj.cache_url) { - bidObject.videoCacheKey = bidObj.cache_id; - bidObject.vastUrl = bidObj.cache_url; - } - } else { - if (bidObj.adm && bidObj.nurl) { - bidObject.ad = bidObj.adm; - bidObject.ad += utils.createTrackPixelHtml(decodeURIComponent(bidObj.nurl)); - } else if (bidObj.adm) { - bidObject.ad = bidObj.adm; - } else if (bidObj.nurl) { - bidObject.adUrl = bidObj.nurl; - } - } - - bidObject.width = bidObj.width; - bidObject.height = bidObj.height; - bidObject.adserverTargeting = bidObj.ad_server_targeting; - if (bidObj.deal_id) { - bidObject.dealId = bidObj.deal_id; - } - bidObject.requestId = bidRequest.bidId || bidRequest.bid_Id; - bidObject.creativeId = bidObj.creative_id; - - // TODO: Remove when prebid-server returns ttl, currency and netRevenue - bidObject.ttl = (bidObj.ttl) ? bidObj.ttl : DEFAULT_S2S_TTL; - bidObject.currency = (bidObj.currency) ? bidObj.currency : DEFAULT_S2S_CURRENCY; - bidObject.netRevenue = (bidObj.netRevenue) ? bidObj.netRevenue : DEFAULT_S2S_NETREVENUE; - - if (result.burl) { bidObject.burl = result.burl; } - - bids.push({ adUnit: bidObj.code, bid: bidObject }); - }); - } - } - - return bids; - } -}; - // https://iabtechlab.com/wp-content/uploads/2016/07/OpenRTB-Native-Ads-Specification-Final-1.2.pdf#page=40 let nativeDataIdMap = { sponsoredBy: 1, // sponsored @@ -624,7 +473,7 @@ const OPEN_RTB_PROTOCOL = { const ext = adUnit.bids.reduce((acc, bid) => { const adapter = adapterManager.bidderRegistry[bid.bidder]; if (adapter && adapter.getSpec().transformBidParams) { - bid.params = adapter.getSpec().transformBidParams(bid.params, isOpenRtb()); + bid.params = adapter.getSpec().transformBidParams(bid.params, true); } acc[bid.bidder] = (_s2sConfig.adapterOptions && _s2sConfig.adapterOptions[bid.bidder]) ? Object.assign({}, bid.params, _s2sConfig.adapterOptions[bid.bidder]) : bid.params; return acc; @@ -893,29 +742,6 @@ const OPEN_RTB_PROTOCOL = { } }; -const isOpenRtb = () => { - const OPEN_RTB_PATH = '/openrtb2/'; - - const endpoint = (_s2sConfig && _s2sConfig.endpoint) || ''; - return ~endpoint.indexOf(OPEN_RTB_PATH); -} - -/* - * Returns the required protocol adapter to communicate with the configured - * endpoint. The adapter is an object containing `buildRequest` and - * `interpretResponse` functions. - * - * Usage: - * // build JSON payload to send to server - * const request = protocol().buildRequest(s2sBidRequest, adUnits); - * - * // turn server response into bid object array - * const bids = protocol().interpretResponse(response, bidRequests, requestedBidders); - */ -const protocolAdapter = () => { - return isOpenRtb() ? OPEN_RTB_PROTOCOL : LEGACY_PROTOCOL; -}; - /** * Bidder adapter for Prebid Server */ @@ -943,7 +769,7 @@ export function PrebidServer() { queueSync(_s2sConfig.bidders, consent); } - const request = protocolAdapter().buildRequest(s2sBidRequest, bidRequests, validAdUnits); + const request = OPEN_RTB_PROTOCOL.buildRequest(s2sBidRequest, bidRequests, validAdUnits); const requestJson = request && JSON.stringify(request); if (request && requestJson) { ajax( @@ -966,7 +792,7 @@ export function PrebidServer() { try { result = JSON.parse(response); - bids = protocolAdapter().interpretResponse( + bids = OPEN_RTB_PROTOCOL.interpretResponse( result, bidderRequests, requestedBidders diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index c823e5aa370..f96d9136e6d 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -15,7 +15,7 @@ let CONFIG = { bidders: ['appnexus'], timeout: 1000, cacheMarkup: 2, - endpoint: 'https://prebid.adnxs.com/pbs/v1/auction' + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }; const REQUEST = { @@ -158,67 +158,6 @@ const OUTSTREAM_VIDEO_REQUEST = { let BID_REQUESTS; -const RESPONSE = { - 'tid': '437fbbf5-33f5-487a-8e16-a7112903cfe5', - 'status': 'OK', - 'bidder_status': [ - { - 'bidder': 'appnexus', - 'response_time_ms': 52, - 'num_bids': 1 - } - ], - 'bids': [ - { - 'bid_id': '123', - 'code': 'div-gpt-ad-1460505748561-0', - 'creative_id': '29681110', - 'bidder': 'appnexus', - 'price': 0.5, - 'adm': '', - 'width': 300, - 'height': 250, - 'deal_id': 'test-dealid', - 'ad_server_targeting': { - 'foo': 'bar' - }, - 'cache_id': '7654321', - 'cache_url': 'http://www.test.com/cache?uuid=7654321', - } - ] -}; - -const VIDEO_RESPONSE = { - 'tid': '437fbbf5-33f5-487a-8e16-a7112903cfe5', - 'status': 'OK', - 'bidder_status': [ - { - 'bidder': 'appnexus', - 'response_time_ms': 52, - 'num_bids': 1 - } - ], - 'bids': [ - { - 'bid_id': '123', - 'code': 'div-gpt-ad-1460505748561-0', - 'creative_id': '29681110', - 'bidder': 'appnexus', - 'price': 0.5, - 'adm': '', - 'width': 300, - 'height': 250, - 'deal_id': 'test-dealid', - 'ad_server_targeting': { - 'foo': 'bar' - }, - 'media_type': 'video', - 'cache_id': 'video_cache_id', - 'cache_url': 'video_cache_url', - } - ] -}; - const RESPONSE_NO_BID_NO_UNIT = { 'tid': '437fbbf5-33f5-487a-8e16-a7112903cfe5', 'status': 'OK', @@ -294,26 +233,6 @@ const RESPONSE_NO_PBS_COOKIE = { }] }; -const RESPONSE_NO_PBS_COOKIE_ERROR = { - 'tid': '882fe33e-2981-4257-bd44-bd3b0394545f', - 'status': 'no_cookie', - 'bidder_status': [{ - 'bidder': 'rubicon', - 'no_cookie': true, - 'usersync': { - 'url': 'https://pixel.rubiconproject.com/exchange/sync.php?p=prebid', - 'type': 'jsonp' - } - }, { - 'bidder': 'pubmatic', - 'no_cookie': true, - 'usersync': { - 'url': '', - 'type': 'iframe' - } - }] -}; - const RESPONSE_OPENRTB = { 'id': 'c7dcf14f', 'seatbid': [ @@ -329,6 +248,7 @@ const RESPONSE_OPENRTB = { 'iurl': 'http://lax1-ib.adnxs.com/cr?id=2968111', 'cid': '958', 'crid': '2968111', + 'dealid': 'test-dealid', 'w': 300, 'h': 250, 'ext': { @@ -368,6 +288,7 @@ const RESPONSE_OPENRTB_VIDEO = { iurl: 'http://lax1-ib.adnxs.com/cr?id=81877115', cid: '3535', crid: '81877115', + dealid: 'test-dealid', w: 1, h: 1, ext: { @@ -482,15 +403,6 @@ const RESPONSE_OPENRTB_NATIVE = { ] }; -const RESPONSE_UNSUPPORTED_BIDDER = { - 'tid': '437fbbf5-33f5-487a-8e16-a7112903cfe5', - 'status': 'OK', - 'bidder_status': [{ - 'bidder': '33Across', - 'error': 'Unsupported bidder' - }] -}; - describe('S2S Adapter', function () { let adapter, addBidResponse = sinon.spy(), @@ -569,15 +481,6 @@ describe('S2S Adapter', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); - it('exists converts types', function () { - config.setConfig({s2sConfig: CONFIG}); - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); - const requestBid = JSON.parse(requests[0].requestBody); - expect(requestBid).to.have.property('cache_markup', 2); - expect(requestBid.ad_units[0].bids[0].params.placementId).to.exist.and.to.be.a('number'); - expect(requestBid.ad_units[0].bids[0].params.member).to.exist.and.to.be.a('string'); - }); - describe('gdpr tests', function () { afterEach(function () { config.resetConfig(); @@ -677,16 +580,6 @@ describe('S2S Adapter', function () { }); }); - it('sets invalid cacheMarkup value to 0', function () { - const s2sConfig = Object.assign({}, CONFIG, { - cacheMarkup: 999 - }); - config.setConfig({s2sConfig: s2sConfig}); - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); - const requestBid = JSON.parse(requests[0].requestBody); - expect(requestBid).to.have.property('cache_markup', 0); - }); - it('adds digitrust id is present and user is not optout', function () { let ortb2Config = utils.deepClone(CONFIG); ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; @@ -946,27 +839,6 @@ describe('S2S Adapter', function () { key: 'fizz', value: ['buzz'] }]); - - config.resetConfig(); - const oldS2sConfig = Object.assign({}, CONFIG); - config.setConfig({s2sConfig: oldS2sConfig}); - - const myRequest2 = utils.deepClone(REQUEST); - myRequest2.ad_units[0].bids[0].params.keywords = { - foo: ['bar', 'baz'], - fizz: ['buzz'] - }; - - adapter.callBids(myRequest2, BID_REQUESTS, addBidResponse, done, ajax); - const requestBid2 = JSON.parse(requests[1].requestBody); - - expect(requestBid2.ad_units[0].bids[0].params.keywords).to.exist.and.to.deep.equal([{ - key: 'foo', - value: ['bar', 'baz'] - }, { - key: 'fizz', - value: ['buzz'] - }]); }); it('adds limit to the cookie_sync request if userSyncLimit is greater than 0', function () { @@ -1287,47 +1159,6 @@ describe('S2S Adapter', function () { }); // TODO: test dependent on pbjs_api_spec. Needs to be isolated - it('registers bids and calls BIDDER_DONE', function () { - server.respondWith(JSON.stringify(RESPONSE)); - - config.setConfig({s2sConfig: CONFIG}); - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); - server.respond(); - sinon.assert.calledOnce(addBidResponse); - - sinon.assert.calledOnce(events.emit); - const event = events.emit.firstCall.args; - expect(event[0]).to.equal(CONSTANTS.EVENTS.BIDDER_DONE); - expect(event[1].bids[0]).to.have.property('serverResponseTimeMs', 52); - - const response = addBidResponse.firstCall.args[1]; - expect(response).to.have.property('statusMessage', 'Bid available'); - expect(response).to.have.property('cpm', 0.5); - expect(response).to.have.property('requestId', '123'); - expect(response).to.not.have.property('videoCacheKey'); - expect(response).to.have.property('cache_id', '7654321'); - expect(response).to.have.property('cache_url', 'http://www.test.com/cache?uuid=7654321'); - expect(response).to.not.have.property('vastUrl'); - }); - - it('registers video bids', function () { - server.respondWith(JSON.stringify(VIDEO_RESPONSE)); - - config.setConfig({s2sConfig: CONFIG}); - 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('cpm', 0.5); - expect(response).to.have.property('requestId', '123'); - expect(response).to.have.property('videoCacheKey', 'video_cache_id'); - expect(response).to.have.property('cache_id', 'video_cache_id'); - expect(response).to.have.property('cache_url', 'video_cache_url'); - expect(response).to.have.property('vastUrl', 'video_cache_url'); - }); - it('does not call addBidResponse and calls done when ad unit not set', function () { server.respondWith(JSON.stringify(RESPONSE_NO_BID_NO_UNIT)); @@ -1362,7 +1193,7 @@ describe('S2S Adapter', function () { }); it('registers successful bids and calls done when there are less bids than requests', function () { - server.respondWith(JSON.stringify(RESPONSE)); + server.respondWith(JSON.stringify(RESPONSE_OPENRTB)); config.setConfig({s2sConfig: CONFIG}); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); @@ -1380,7 +1211,7 @@ describe('S2S Adapter', function () { }); it('should have dealId in bidObject', function () { - server.respondWith(JSON.stringify(RESPONSE)); + server.respondWith(JSON.stringify(RESPONSE_OPENRTB)); config.setConfig({s2sConfig: CONFIG}); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); @@ -1390,13 +1221,28 @@ describe('S2S Adapter', function () { }); it('should pass through default adserverTargeting if present in bidObject', function () { - server.respondWith(JSON.stringify(RESPONSE)); - config.setConfig({s2sConfig: CONFIG}); - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + const cacheResponse = utils.deepClone(RESPONSE_OPENRTB); + 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').that.deep.equals({'foo': 'bar'}); + + expect(response).to.have.property('adserverTargeting'); + expect(response.adserverTargeting).to.deep.equal({ + 'hb_cache_path': '/cache', + 'hb_cache_host': 'prebid-cache.testurl.com' + }); }); it('registers client user syncs when client bid adapter is present', function () { @@ -1436,25 +1282,6 @@ describe('S2S Adapter', function () { adapterManager.getBidAdapter.restore(); }); - it('registers bid responses when server requests cookie sync', function () { - server.respondWith(JSON.stringify(RESPONSE_NO_PBS_COOKIE)); - - config.setConfig({s2sConfig: CONFIG}); - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); - server.respond(); - sinon.assert.calledOnce(addBidResponse); - - const ad_unit_code = addBidResponse.firstCall.args[0]; - expect(ad_unit_code).to.equal('div-gpt-ad-1460505748561-0'); - - const response = addBidResponse.firstCall.args[1]; - expect(response).to.have.property('statusMessage', 'Bid available'); - expect(response).to.have.property('source', 's2s'); - - const bid_request_passed = addBidResponse.firstCall.args[1]; - expect(bid_request_passed).to.have.property('requestId', '123'); - }); - it('handles OpenRTB responses and call BIDDER_DONE', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' @@ -1476,6 +1303,8 @@ describe('S2S Adapter', function () { expect(response).to.have.property('bidderCode', 'appnexus'); expect(response).to.have.property('requestId', '123'); expect(response).to.have.property('cpm', 0.5); + expect(response).to.not.have.property('vastUrl'); + expect(response).to.not.have.property('videoCacheKey'); }); it('handles OpenRTB video responses', function () { @@ -1603,25 +1432,6 @@ describe('S2S Adapter', function () { utils.getBidRequest.restore(); }); - - it('should log warning for unsupported bidder', function () { - server.respondWith(JSON.stringify(RESPONSE_UNSUPPORTED_BIDDER)); - - const s2sConfig = Object.assign({}, CONFIG, { - bidders: ['33Across'] - }); - - const _config = { - s2sConfig: s2sConfig, - } - - config.setConfig(_config); - config.setConfig({s2sConfig: CONFIG}); - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); - server.respond(); - - sinon.assert.calledOnce(logWarnSpy); - }); }); describe('s2sConfig', function () { @@ -1641,7 +1451,7 @@ describe('S2S Adapter', function () { bidders: ['appnexus'], timeout: 1000, adapter: 'prebidServer', - endpoint: 'https://prebid.adnxs.com/pbs/v1/auction' + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }; config.setConfig({ s2sConfig: options }); @@ -1654,7 +1464,7 @@ describe('S2S Adapter', function () { enabled: true, timeout: 1000, adapter: 's2s', - endpoint: 'https://prebid.adnxs.com/pbs/v1/auction' + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }; config.setConfig({ s2sConfig: options }); @@ -1757,7 +1567,7 @@ describe('S2S Adapter', function () { bidders: ['rubicon'], defaultVendor: 'rubicon', endpoint: '//prebid-server.rubiconproject.com/openrtb2/auction', - syncEndpoint: '//prebid-server.rubiconproject.com/cookie_sync' + syncEndpoint: '//prebid-server.rubiconproject.com/cookie_sync', }) });