From 539cf591b19e18ca737a6f2194b067d3ef86d3b4 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Thu, 1 Nov 2018 11:20:40 -0700 Subject: [PATCH 01/18] updates to both the rubicon adapter and the prebid server for ORTB requirements --- modules/rubiconBidAdapter.js | 252 ++++++++++++++---- .../modules/prebidServerBidAdapter_spec.js | 62 +++++ 2 files changed, 264 insertions(+), 50 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 7f219886057..b16fb45ee8d 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -11,11 +11,10 @@ function isSecure() { // use protocol relative urls for http or https export const FASTLANE_ENDPOINT = '//fastlane.rubiconproject.com/a/api/fastlane.json'; -export const VIDEO_ENDPOINT = '//fastlane-adv.rubiconproject.com/v1/auction/video'; +export const VIDEO_ENDPOINT = '//prebid-server.rubiconproject.com/openrtb/auction'; +export const ORTB_ENDPOINT = '//fastlane-adv.rubiconproject.com/v1/auction/video'; export const SYNC_ENDPOINT = 'https://eus.rubiconproject.com/usync.html'; -const TIMEOUT_BUFFER = 500; - var sizeMap = { 1: '468x60', 2: '728x90', @@ -82,7 +81,6 @@ utils._each(sizeMap, (item, key) => sizeMap[item] = key); export const spec = { code: 'rubicon', - aliases: ['rubiconLite'], supportedMediaTypes: [BANNER, VIDEO], /** * @param {object} bid @@ -97,7 +95,15 @@ export const spec = { return false; } - return !!bidType(bid, true); + // validate account, site, zone have numeric values + for (let i = 0, props = ['accountId', 'siteId', 'zoneId']; i < props.length; i++) { + bid.params[props[i]] = parseInt(bid.params[props[i]]); + if (isNaN(bid.params[props[i]])) { + utils.logError('Rubicon bid adapter Error: wrong format of accountId or siteId or zoneId.'); + return false; + } + } + return !!bidType(bid, true) }, /** * @param {BidRequest[]} bidRequests @@ -110,58 +116,74 @@ export const spec = { const videoRequests = bidRequests.filter(bidRequest => bidType(bidRequest) === 'video').map(bidRequest => { bidRequest.startTime = new Date().getTime(); - let params = bidRequest.params; - let size = parseSizes(bidRequest); - - let data = { - page_url: _getPageUrl(bidRequest, bidderRequest), - resolution: _getScreenResolution(), - account_id: params.accountId, - integration: INTEGRATION, - 'x_source.tid': bidRequest.transactionId, - timeout: bidderRequest.timeout - (Date.now() - bidderRequest.auctionStart + TIMEOUT_BUFFER), - stash_creatives: true, - slots: [] - }; - - // Define the slot object - let slotData = { - site_id: params.siteId, - zone_id: params.zoneId, - position: params.position === 'atf' || params.position === 'btf' ? params.position : 'unknown', - floor: parseFloat(params.floor) > 0.01 ? params.floor : 0.01, - element_id: bidRequest.adUnitCode, - name: bidRequest.adUnitCode, - width: size[0], - height: size[1], - size_id: utils.deepAccess(bidRequest, `mediaTypes.${VIDEO}.context`) === 'outstream' ? 203 : params.video.size_id + const data = { + id: bidRequest.transactionId, + test: config.getConfig('debug') ? 1 : 0, + cur: ['USD'], + source: { + tid: bidRequest.transactionId + }, + tmax: config.getConfig('TTL') || 1000, + imp: [{ + id: bidRequest.adUnitCode, + secure: isSecure() || bidRequest.params.secure ? 1 : 0, + ext: { + rubicon: bidRequest.params + }, + video: utils.deepAccess(bidRequest, 'mediaTypes.video') + }], + ext: { + prebid: { + targeting: { + includewinners: true + } + } + }, + vastxml: { + ttlseconds: 300 + } }; - if (params.video) { - data.ae_pass_through_parameters = params.video.aeParams; - slotData.language = params.video.language; - } - - if (params.inventory && typeof params.inventory === 'object') { - slotData.inventory = params.inventory; - } + appendSiteAppDevice(data); - if (params.keywords && Array.isArray(params.keywords)) { - slotData.keywords = params.keywords; - } + addVideoParameters(data, bidRequest); - if (params.visitor && typeof params.visitor === 'object') { - slotData.visitor = params.visitor; + const digiTrust = getDigiTrustQueryParams(); + if (digiTrust) { + data.user = { + ext: { + digitrust: digiTrust + } + }; } - data.slots.push(slotData); - if (bidderRequest.gdprConsent) { - // add 'gdpr' only if 'gdprApplies' is defined + // note - gdprApplies & consentString may be undefined in certain use-cases for consentManagement module + let gdprApplies; if (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') { - data.gdpr = Number(bidderRequest.gdprConsent.gdprApplies); + gdprApplies = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; + } + + if (data.regs) { + if (data.regs.ext) { + data.regs.ext.gdpr = gdprApplies; + } else { + data.regs.ext = {gdpr: gdprApplies}; + } + } else { + data.regs = {ext: {gdpr: gdprApplies}}; + } + + const consentString = bidderRequest.gdprConsent.consentString; + if (data.user) { + if (data.user.ext) { + data.user.ext.consent = consentString; + } else { + data.user.ext = {consent: consentString}; + } + } else { + data.user = {ext: {consent: consentString}}; } - data.gdpr_consent = bidderRequest.gdprConsent.consentString; } return { @@ -375,6 +397,62 @@ export const spec = { return []; } + // video response from PBS Java openRTB + if (responseObj.seatbid) { + const responseErrors = utils.deepAccess(responseObj, 'ext.errors.rubicon'); + if (Array.isArray(responseErrors) && responseErrors.length > 0) { + responseErrors.forEach(error => { + utils.logError('Got error from PBS Java openRTB: ' + error); + }); + } + const bids = []; + responseObj.seatbid.forEach(seatbid => { + (seatbid.bid || []).forEach(bid => { + let bidObject = { + requestId: bidRequest.bidId, + currency: responseObj.cur || 'USD', + creativeId: bid.crid, + cpm: bid.price || 0, + bidderCode: seatbid.seat, + ttl: 300, + netRevenue: config.getConfig('rubicon.netRevenue') || false, + width: bid.w || utils.deepAccess(bidRequest, 'mediaTypes.video.w') || utils.deepAccess(bidRequest, 'params.video.playerWidth'), + height: bid.h || utils.deepAccess(bidRequest, 'mediaTypes.video.h') || utils.deepAccess(bidRequest, 'params.video.playerHeight'), + }; + + if (bid.dealid) { + bidObject.dealId = bid.dealid; + } + + let serverResponseTimeMs = utils.deepAccess(responseObj, 'ext.responsetimemillis.rubicon'); + if (bidRequest && serverResponseTimeMs) { + bidRequest.serverResponseTimeMs = serverResponseTimeMs; + } + + if (utils.deepAccess(bid, 'ext.prebid.type') === VIDEO) { + bidObject.mediaType = VIDEO; + if (bid.adm) { + bidObject.vastXml = bid.adm; + } + if (bid.nurl) { + bidObject.vastUrl = bid.nurl; + } + const videoCacheKey = utils.deepAccess(bid, 'ext.prebid.targeting.hb_uuid'); + if (videoCacheKey) { + bidObject.vastUrl = videoCacheKey; + bidObject.videoCacheKey = videoCacheKey; + } + } else { + utils.logError('Prebid Server Java openRTB returns response with media type other than video for video request.'); + } + + bids.push(bidObject); + }); + }); + + return bids; + } + let ads = responseObj.ads; // video ads array is wrapped in an object @@ -469,11 +547,17 @@ export const spec = { * @return {Object} params bid params */ transformBidParams: function(params, isOpenRtb) { - return utils.convertTypes({ + const convertedParams = utils.convertTypes({ 'accountId': 'number', 'siteId': 'number', 'zoneId': 'number' }, params); + + if (isOpenRtb) { + + } + + return convertedParams; } }; @@ -501,13 +585,14 @@ function _getDigiTrustQueryParams() { /** * @param {BidRequest} bidRequest + * @param bidderRequest * @returns {string} */ function _getPageUrl(bidRequest, bidderRequest) { let pageUrl = config.getConfig('pageUrl'); if (bidRequest.params.referrer) { pageUrl = bidRequest.params.referrer; - } else if (!pageUrl) { + } else if (!pageUrl && bidderRequest.refererInfo) { pageUrl = bidderRequest.refererInfo.referer; } return bidRequest.params.secure ? pageUrl.replace(/^http:/i, 'https:') : pageUrl; @@ -557,6 +642,73 @@ function parseSizes(bid) { return masSizeOrdering(sizes); } +function getDigiTrustQueryParams() { + function getDigiTrustId() { + let digiTrustUser = window.DigiTrust && (config.getConfig('digiTrustId') || window.DigiTrust.getUser({member: 'T9QSFKPDN9'})); + return (digiTrustUser && digiTrustUser.success && digiTrustUser.identity) || null; + } + + let digiTrustId = getDigiTrustId(); + // Verify there is an ID and this user has not opted out + if (!digiTrustId || (digiTrustId.privacy && digiTrustId.privacy.optout)) { + return null; + } + return { + id: digiTrustId.id, + keyv: digiTrustId.keyv, + pref: 0 + }; +} + +function appendSiteAppDevice(request) { + if (!request) return; + + // ORTB specifies app OR site + if (typeof config.getConfig('app') === 'object') { + request.app = config.getConfig('app'); + } else { + request.site = { + page: utils.getTopWindowUrl() + } + } + if (typeof config.getConfig('device') === 'object') { + request.device = config.getConfig('device'); + } +} + +function addVideoParameters(data, bidRequest) { + if (typeof data.imp[0].video === 'object' && data.imp[0].video.skip === undefined) { + data.imp[0].video.skip = bidRequest.params.video.skip; + } + if (typeof data.imp[0].video === 'object' && data.imp[0].video.skipafter === undefined) { + data.imp[0].video.skipafter = bidRequest.params.video.skipdelay; + } + if (typeof data.imp[0].video === 'object' && data.imp[0].video.pos === undefined) { + data.imp[0].video.pos = bidRequest.params.position === 'atf' ? 1 : bidRequest.params.position === 'btf' ? 3 : 0; + } + if (data.imp[0].bidfloor === undefined) { + data.imp[0].bidfloor = parseFloat(bidRequest.params.floor) > 0.01 ? bidRequest.params.floor : 0.01; + } + if (typeof data.imp[0].video === 'object' && data.imp[0].video.w === undefined) { + if (bidRequest.params.video.playerWidth) { + data.imp[0].video.w = bidRequest.params.video.playerWidth; + } else if (Array.isArray(utils.deepAccess(bidRequest, 'mediaTypes.video.playerSize')) && bidRequest.mediaTypes.video.playerSize.length === 1) { + data.imp[0].video.w = bidRequest.mediaTypes.video.playerSize[0][0]; + } else if (Array.isArray(bidRequest.sizes) && bidRequest.sizes.length > 0 && Array.isArray(bidRequest.sizes[0]) && bidRequest.sizes[0].length > 1) { + data.imp[0].video.w = bidRequest.sizes[0][0]; + } + } + if (typeof data.imp[0].video === 'object' && data.imp[0].video.h === undefined) { + if (bidRequest.params.video.playerWidth) { + data.imp[0].video.h = bidRequest.params.video.playerHeight; + } else if (Array.isArray(utils.deepAccess(bidRequest, 'mediaTypes.video.playerSize')) && bidRequest.mediaTypes.video.playerSize.length === 1) { + data.imp[0].video.h = bidRequest.mediaTypes.video.playerSize[0][1]; + } else if (Array.isArray(bidRequest.sizes) && bidRequest.sizes.length > 0 && Array.isArray(bidRequest.sizes[0]) && bidRequest.sizes[0].length > 1) { + data.imp[0].video.h = bidRequest.sizes[0][1]; + } + } +} + function mapSizes(sizes) { return utils.parseSizesInput(sizes) // map sizes while excluding non-matches diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 7c3a356c9e8..d36f134ddb9 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -712,6 +712,63 @@ describe('S2S Adapter', function () { value: ['buzz'] }]); }); + + it('s2sConfig \'video.ext.prebid\' is passed through openrtb to PBS', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + }); + config.setConfig({s2sConfig: s2sConfig}); + + const myRequest = utils.deepClone(REQUEST); + adapter.callBids(myRequest, BID_REQUESTS, addBidResponse, done, ajax); + const requestBid = JSON.parse(requests[0].requestBody); + }); + + it('converts rubicon params to expected format for PBS', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + }); + config.setConfig({s2sConfig: s2sConfig}); + + const myRequest = utils.deepClone(REQUEST); + myRequest.ad_units[0].bids[0].params.usePaymentRule = true; + + adapter.callBids(myRequest, BID_REQUESTS, addBidResponse, done, ajax); + const requestBid = JSON.parse(requests[0].requestBody); + + expect(requestBid.imp[0].ext.appnexus).to.exist; + expect(requestBid.imp[0].ext.appnexus.placement_id).to.exist.and.to.equal(10433394); + expect(requestBid.imp[0].ext.appnexus.use_pmt_rule).to.exist.and.to.be.true; + expect(requestBid.imp[0].ext.appnexus.member).to.exist; + expect(requestBid.imp[0].ext.appnexus.keywords).to.exist.and.to.deep.equal([{ + key: 'foo', + value: ['bar', 'baz'] + }, { + 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'] + }]); + }); }); describe('response handler', function () { @@ -1100,5 +1157,10 @@ describe('S2S Adapter', function () { expect(vendorConfig).to.have.property('syncEndpoint', '//prebid-server.rubiconproject.com/cookie_sync'); expect(vendorConfig).to.have.property('timeout', 750); }); + + it('should configure the s2sConfig when video.ext.prebid is set', function() { + // s2sConfig 'video.ext.prebid' support + + }) }); }); From 1ab0300c64079c98b192b9a8bcba97296a2c2fff Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Wed, 14 Nov 2018 14:30:39 -0800 Subject: [PATCH 02/18] updates to bid and auction data to reflect oRTB structure --- karma.conf.maker.js | 13 +- modules/rubiconBidAdapter.js | 11 +- test/spec/modules/rubiconBidAdapter_spec.js | 453 +++++++------------- 3 files changed, 167 insertions(+), 310 deletions(-) diff --git a/karma.conf.maker.js b/karma.conf.maker.js index 7faf91f0847..3d512852c38 100644 --- a/karma.conf.maker.js +++ b/karma.conf.maker.js @@ -112,7 +112,14 @@ module.exports = function(codeCoverage, browserstack, watchMode, file) { var webpackConfig = newWebpackConfig(codeCoverage); var plugins = newPluginsArray(browserstack); - var files = file ? ['test/helpers/prebidGlobal.js', file] : ['test/test_index.js']; + // var files = file ? ['test/helpers/prebidGlobal.js', file] : ['test/test_index.js']; + var files = file ? ['test/helpers/prebidGlobal.js', '', file] : ['test/test_index.js']; + + var preprocessors = files.reduce(function(aggregate, filepath) { + aggregate[filepath] = ['webpack', 'sourcemap'] + return aggregate + }, {}) + // This file opens the /debug.html tab automatically. // It has no real value unless you're running --watch, and intend to do some debugging in the browser. if (watchMode) { @@ -135,9 +142,7 @@ module.exports = function(codeCoverage, browserstack, watchMode, file) { // preprocess matching files before serving them to the browser // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor - preprocessors: { - 'test/test_index.js': ['webpack', 'sourcemap'] - }, + preprocessors: preprocessors, // web server port port: 9876, diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index b16fb45ee8d..2f72579be6f 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -90,17 +90,12 @@ export const spec = { if (typeof bid.params !== 'object') { return false; } - - if (!/^\d+$/.test(bid.params.accountId)) { - return false; - } - // validate account, site, zone have numeric values for (let i = 0, props = ['accountId', 'siteId', 'zoneId']; i < props.length; i++) { - bid.params[props[i]] = parseInt(bid.params[props[i]]); + bid.params[props[i]] = parseInt(bid.params[props[i]]) if (isNaN(bid.params[props[i]])) { - utils.logError('Rubicon bid adapter Error: wrong format of accountId or siteId or zoneId.'); - return false; + utils.logError('Rubicon bid adapter Error: wrong format of accountId or siteId or zoneId.') + return false } } return !!bidType(bid, true) diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 70790eaa46f..10b824f0056 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1,15 +1,9 @@ import {expect} from 'chai'; -import adapterManager from 'src/adaptermanager'; -import {spec, masSizeOrdering, resetUserSync, hasVideoMediaType, FASTLANE_ENDPOINT} from 'modules/rubiconBidAdapter'; +import {spec, masSizeOrdering, resetUserSync, hasVideoMediaType} from 'modules/rubiconBidAdapter'; import {parse as parseQuery} from 'querystring'; -import {newBidder} from 'src/adapters/bidderFactory'; -import {userSync} from 'src/userSync'; import {config} from 'src/config'; -import * as utils from 'src/utils'; import find from 'core-js/library/fn/array/find'; -var CONSTANTS = require('src/constants.json'); - const INTEGRATION = `pbjs_lite_v$prebid.version$`; // $prebid.version$ will be substituted in by gulp in built prebid describe('the rubicon adapter', function () { @@ -149,20 +143,27 @@ describe('the rubicon adapter', function () { let bid = bidderRequest.bids[0]; bid.mediaTypes = { video: { - context: 'instream' + context: 'instream', + mimes: ['video/mp4', 'video/x-flv'], + minduration: 15, + maxduration: 30, + startdelay: 0, + playbackmethod: [2], + w: 640, + h: 480, + skip: 1, + skipafter: 15, + pos: 1, + protocols: [1, 2, 3, 4, 5, 6] } }; bid.params.video = { 'language': 'en', - 'p_aso.video.ext.skip': true, - 'p_aso.video.ext.skipdelay': 15, - 'playerHeight': 320, + 'skip': 1, + 'skipafter': 15, + 'playerHeight': 480, 'playerWidth': 640, 'size_id': 201, - 'aeParams': { - 'p_aso.video.ext.skip': '1', - 'p_aso.video.ext.skipdelay': '15' - } }; } @@ -174,15 +175,11 @@ describe('the rubicon adapter', function () { bid.mediaType = 'video'; bid.params.video = { 'language': 'en', - 'p_aso.video.ext.skip': true, - 'p_aso.video.ext.skipdelay': 15, + 'skip': 1, + 'skipafter': 15, 'playerHeight': 320, 'playerWidth': 640, 'size_id': 201, - 'aeParams': { - 'p_aso.video.ext.skip': '1', - 'p_aso.video.ext.skipdelay': '15' - } }; } @@ -209,50 +206,19 @@ describe('the rubicon adapter', function () { context: 'outstream' }, }; + bid.params.accountId = 14062; + bid.params.siteId = 70608; + bid.params.zoneId = 335918; bid.params.video = { 'language': 'en', - 'p_aso.video.ext.skip': true, - 'p_aso.video.ext.skipdelay': 15, + 'skip': 1, + 'skipafter': 15, 'playerHeight': 320, 'playerWidth': 640, 'size_id': 203, 'aeParams': { 'p_aso.video.ext.skip': '1', - 'p_aso.video.ext.skipdelay': '15' - } - }; - } - - function createVideoBidderRequestNoPlayer() { - let bid = bidderRequest.bids[0]; - bid.mediaTypes = { - video: { - context: 'instream' - }, - }; - bid.params.video = { - 'language': 'en', - 'p_aso.video.ext.skip': true, - 'p_aso.video.ext.skipdelay': 15, - 'size_id': 201, - 'aeParams': { - 'p_aso.video.ext.skip': '1', - 'p_aso.video.ext.skipdelay': '15' - } - }; - } - - function createLegacyVideoBidderRequestNoPlayer() { - let bid = bidderRequest.bids[0]; - bid.mediaType = 'video'; - bid.params.video = { - 'language': 'en', - 'p_aso.video.ext.skip': true, - 'p_aso.video.ext.skipdelay': 15, - 'size_id': 201, - 'aeParams': { - 'p_aso.video.ext.skip': '1', - 'p_aso.video.ext.skipdelay': '15' + 'p_aso.video.ext.skipafter': '15' } }; } @@ -434,8 +400,8 @@ describe('the rubicon adapter', function () { sandbox.stub(Math, 'random').callsFake(() => 0.1); delete bidderRequest.bids[0].params.latLong; - [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - data = parseQuery(request.data); + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + let data = parseQuery(request.data); expect(request.url).to.equal('//fastlane.rubiconproject.com/a/api/fastlane.json'); @@ -450,8 +416,8 @@ describe('the rubicon adapter', function () { }); bidderRequest.bids[0].params.latLong = []; - let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - let data = parseQuery(request.data); + [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + data = parseQuery(request.data); expect(request.url).to.equal('//fastlane.rubiconproject.com/a/api/fastlane.json'); @@ -480,7 +446,6 @@ describe('the rubicon adapter', function () { bidderRequest = Object.assign({refererInfo}, bidderRequest); delete bidderRequest.bids[0].params.referrer; let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - let data = parseQuery(request.data); expect(parseQuery(request.data).rf).to.exist; expect(parseQuery(request.data).rf).to.equal('http://www.prebid.org'); @@ -655,7 +620,6 @@ describe('the rubicon adapter', function () { }); describe('digiTrustId config', function () { - var origGetConfig; beforeEach(function () { window.DigiTrust = { getUser: sandbox.spy() @@ -1012,13 +976,13 @@ describe('the rubicon adapter', function () { bidCopy4.params.video = { 'language': 'en', 'p_aso.video.ext.skip': true, - 'p_aso.video.ext.skipdelay': 15, + 'p_aso.video.ext.skipafter': 15, 'playerHeight': 320, 'playerWidth': 640, 'size_id': 201, 'aeParams': { 'p_aso.video.ext.skip': '1', - 'p_aso.video.ext.skipdelay': '15' + 'p_aso.video.ext.skipafter': '15' } }; bidderRequest.bids.push(bidCopy4); @@ -1040,58 +1004,26 @@ describe('the rubicon adapter', function () { let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let post = request.data; - let url = request.url; - - expect(url).to.equal('//fastlane-adv.rubiconproject.com/v1/auction/video'); - - expect(post).to.have.property('page_url').that.is.a('string'); - expect(post.resolution).to.match(/\d+x\d+/); - expect(post.account_id).to.equal('14062'); - expect(post.integration).to.equal(INTEGRATION); - expect(post['x_source.tid']).to.equal('d45dd707-a418-42ec-b8a7-b70a6c6fab0b'); - expect(post).to.have.property('timeout').that.is.a('number'); - expect(post.timeout < 5000).to.equal(true); - expect(post.stash_creatives).to.equal(true); - expect(post.gdpr_consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); - expect(post.gdpr).to.equal(1); - - expect(post).to.have.property('ae_pass_through_parameters'); - expect(post.ae_pass_through_parameters) - .to.have.property('p_aso.video.ext.skip') - .that.equals('1'); - expect(post.ae_pass_through_parameters) - .to.have.property('p_aso.video.ext.skipdelay') - .that.equals('15'); - - expect(post).to.have.property('slots') - .with.length.of(1); + // url will be changed to Prebid Serve Java openRTB url + // let url = request.url; + // + // expect(url).to.equal('//fastlane-adv.rubiconproject.com/v1/auction/video'); - let slot = post.slots[0]; - - expect(slot.site_id).to.equal('70608'); - expect(slot.zone_id).to.equal('335918'); - expect(slot.position).to.equal('atf'); - expect(slot.floor).to.equal(0.01); - expect(slot.element_id).to.equal(bidderRequest.bids[0].adUnitCode); - expect(slot.name).to.equal(bidderRequest.bids[0].adUnitCode); - expect(slot.language).to.equal('en'); - expect(slot.width).to.equal(640); - expect(slot.height).to.equal(320); - expect(slot.size_id).to.equal(201); - - expect(slot).to.have.property('inventory').that.is.an('object'); - expect(slot.inventory).to.have.property('rating').that.equals('5-star'); - expect(slot.inventory).to.have.property('prodtype').that.deep.equals(['tech', 'mobile']); - - expect(slot).to.have.property('keywords') - .that.is.an('array') - .of.length(3) - .that.deep.equals(['a', 'b', 'c']); - - expect(slot).to.have.property('visitor').that.is.an('object'); - expect(slot.visitor).to.have.property('ucat').that.equals('new'); - expect(slot.visitor).to.have.property('lastsearch').that.equals('iphone'); - expect(slot.visitor).to.have.property('likes').that.deep.equals(['sports', 'video games']); + expect(post).to.have.property('imp') + .with.length.of(1); + let imp = post.imp[0]; + expect(imp.id).to.equal(bidderRequest.bids[0].adUnitCode); + expect(imp.ext.rubicon.video.playerWidth).to.equal(640); + expect(imp.ext.rubicon.video.playerHeight).to.equal(320); + expect(imp.ext.rubicon.video.size_id).to.equal(201); + expect(imp.ext.rubicon.video.language).to.equal('en'); + expect(imp.ext.rubicon.video.skip).to.equal(1); + expect(imp.ext.rubicon.video.skipafter).to.equal(15); + expect(post.user.ext.consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); + expect(post.regs.ext.gdpr).to.equal(1); + expect(post).to.have.property('ext').that.is.an('object'); + expect(post.vastxml.ttlseconds).to.equal(300); + expect(post.ext.prebid.targeting.includewinners).to.equal(true); }); it('should make a well-formed video request', function () { @@ -1104,122 +1036,90 @@ describe('the rubicon adapter', function () { let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let post = request.data; - let url = request.url; - - expect(url).to.equal('//fastlane-adv.rubiconproject.com/v1/auction/video'); - - expect(post).to.have.property('page_url').that.is.a('string'); - expect(post.resolution).to.match(/\d+x\d+/); - expect(post.account_id).to.equal('14062'); - expect(post.integration).to.equal(INTEGRATION); - expect(post['x_source.tid']).to.equal('d45dd707-a418-42ec-b8a7-b70a6c6fab0b'); - expect(post).to.have.property('timeout').that.is.a('number'); - expect(post.timeout < 5000).to.equal(true); - expect(post.stash_creatives).to.equal(true); - expect(post.gdpr_consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); - expect(post.gdpr).to.equal(1); - - expect(post).to.have.property('ae_pass_through_parameters'); - expect(post.ae_pass_through_parameters) - .to.have.property('p_aso.video.ext.skip') - .that.equals('1'); - expect(post.ae_pass_through_parameters) - .to.have.property('p_aso.video.ext.skipdelay') - .that.equals('15'); - - expect(post).to.have.property('slots') - .with.length.of(1); + // url will be changed to Prebid Serve Java openRTB url + // let url = request.url; + // + // expect(url).to.equal('//fastlane-adv.rubiconproject.com/v1/auction/video'); - let slot = post.slots[0]; - - expect(slot.site_id).to.equal('70608'); - expect(slot.zone_id).to.equal('335918'); - expect(slot.position).to.equal('atf'); - expect(slot.floor).to.equal(0.01); - expect(slot.element_id).to.equal(bidderRequest.bids[0].adUnitCode); - expect(slot.name).to.equal(bidderRequest.bids[0].adUnitCode); - expect(slot.language).to.equal('en'); - expect(slot.width).to.equal(640); - expect(slot.height).to.equal(320); - expect(slot.size_id).to.equal(201); - - expect(slot).to.have.property('inventory').that.is.an('object'); - expect(slot.inventory).to.have.property('rating').that.equals('5-star'); - expect(slot.inventory).to.have.property('prodtype').that.deep.equals(['tech', 'mobile']); - - expect(slot).to.have.property('keywords') - .that.is.an('array') - .of.length(3) - .that.deep.equals(['a', 'b', 'c']); - - expect(slot).to.have.property('visitor').that.is.an('object'); - expect(slot.visitor).to.have.property('ucat').that.equals('new'); - expect(slot.visitor).to.have.property('lastsearch').that.equals('iphone'); - expect(slot.visitor).to.have.property('likes').that.deep.equals(['sports', 'video games']); + expect(post).to.have.property('imp') + .with.length.of(1); + let imp = post.imp[0]; + expect(imp.id).to.equal(bidderRequest.bids[0].adUnitCode); + expect(imp.video.w).to.equal(640); + expect(imp.video.h).to.equal(480); + expect(imp.video.pos).to.equal(1); + expect(imp.video.context).to.equal('instream'); + expect(imp.video.minduration).to.equal(15); + expect(imp.video.maxduration).to.equal(30); + expect(imp.video.startdelay).to.equal(0); + expect(imp.video.skip).to.equal(1); + expect(imp.video.skipafter).to.equal(15); + expect(imp.ext.rubicon.video.playerWidth).to.equal(640); + expect(imp.ext.rubicon.video.playerHeight).to.equal(480); + expect(imp.ext.rubicon.video.size_id).to.equal(201); + expect(imp.ext.rubicon.video.language).to.equal('en'); + expect(imp.ext.rubicon.video.skip).to.equal(1); + expect(imp.ext.rubicon.video.skipafter).to.equal(15); + expect(post.user.ext.consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); + expect(post.regs.ext.gdpr).to.equal(1); + expect(post).to.have.property('ext').that.is.an('object'); + expect(post.vastxml.ttlseconds).to.equal(300); + expect(post.ext.prebid.targeting.includewinners).to.equal(true); }); it('should send request with proper ad position', function () { createVideoBidderRequest(); - var positionBidderRequest = clone(bidderRequest); - positionBidderRequest.bids[0].params.position = 'atf'; + let positionBidderRequest = clone(bidderRequest); + positionBidderRequest.bids[0].mediaTypes.video.pos = '1'; let [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); - let post = request.data; - let slot = post.slots[0]; - - expect(slot.position).to.equal('atf'); - - positionBidderRequest = clone(bidderRequest); - positionBidderRequest.bids[0].params.position = 'btf'; - [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); - post = request.data; - slot = post.slots[0]; - - expect(slot.position).to.equal('btf'); - positionBidderRequest = clone(bidderRequest); - positionBidderRequest.bids[0].params.position = 'unknown'; - [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); - post = request.data; - slot = post.slots[0]; - - expect(slot.position).to.equal('unknown'); - - positionBidderRequest = clone(bidderRequest); - positionBidderRequest.bids[0].params.position = '123'; - [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); - post = request.data; - slot = post.slots[0]; - - expect(slot.position).to.equal('unknown'); - - positionBidderRequest = clone(bidderRequest); - delete positionBidderRequest.bids[0].params.position; - expect(positionBidderRequest.bids[0].params.position).to.equal(undefined); - [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); - post = request.data; - slot = post.slots[0]; - - expect(slot.position).to.equal('unknown'); + expect(request.data.imp[0].video.pos).to.equal('1'); }); - it('should allow a floor price override', function () { - createVideoBidderRequest(); - + it('should validate bid request and do the conversion when the type of accountId, siteId and zoneId is string', function () { + const bidRequest = { + mediaTypes: { + video: { + context: 'instream' + } + }, + params: { + accountId: '1001', + siteId: '123', + zoneId: '456', + video: { + size_id: 201 + } + }, + sizes: [[300, 250]] + }; sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 ); + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); - var floorBidderRequest = clone(bidderRequest); - - // enter an explicit floor price // - floorBidderRequest.bids[0].params.floor = 3.25; - - let [request] = spec.buildRequests(floorBidderRequest.bids, floorBidderRequest); - let post = request.data; - - let floor = post.slots[0].floor; - - expect(floor).to.equal(3.25); + it('should not validate bid request when the type of accountId, siteId and zoneId is string', function () { + const bidRequest = { + mediaTypes: { + video: { + context: 'instream' + } + }, + params: { + accountId: 'hello', + siteId: '123', + zoneId: '456', + video: { + size_id: 201 + } + }, + sizes: [[300, 250]] + }; + sandbox.stub(Date, 'now').callsFake(() => + bidderRequest.auctionStart + 100 + ); + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); it('should validate bid request with invalid video if a mediaTypes banner property is defined', function () { @@ -1234,12 +1134,14 @@ describe('the rubicon adapter', function () { }, params: { accountId: 1001, + siteId: 123, + zoneId: 456, video: { size_id: 201 } }, sizes: [[300, 250]] - } + }; sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 ); @@ -1303,54 +1205,7 @@ describe('the rubicon adapter', function () { let [request] = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy); expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(true); - expect(request.data.slots[0].size_id).to.equal(203); - }); - - it('should send request as banner when invalid video bid in multiple mediaType bidRequest', function () { - createVideoBidderRequestNoVideo(); - - let bid = bidderRequest.bids[0]; - bid.mediaTypes.banner = { - sizes: [[300, 250]] - }; - - sandbox.stub(Date, 'now').callsFake(() => - bidderRequest.auctionStart + 100 - ); - - const bidRequestCopy = clone(bidderRequest); - - let requests = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy); - expect(requests.length).to.equal(1); - expect(requests[0].url).to.equal(FASTLANE_ENDPOINT); - }); - - it('should get size from bid.sizes too', () => { - createVideoBidderRequestNoPlayer(); - sandbox.stub(Date, 'now').callsFake(() => - bidderRequest.auctionStart + 100 - ); - - const bidRequestCopy = clone(bidderRequest); - - let [request] = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy); - - expect(request.data.slots[0].width).to.equal(300); - expect(request.data.slots[0].height).to.equal(250); - }); - - it('should get size from bid.sizes too with legacy config mediaType', function () { - createLegacyVideoBidderRequestNoPlayer(); - sandbox.stub(Date, 'now').callsFake(() => - bidderRequest.auctionStart + 100 - ); - - const bidRequestCopy = clone(bidderRequest); - - let [request] = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy); - - expect(request.data.slots[0].width).to.equal(300); - expect(request.data.slots[0].height).to.equal(250); + expect(request.data.imp[0].ext.rubicon.video.size_id).to.equal(203); }); }); @@ -1964,30 +1819,31 @@ describe('the rubicon adapter', function () { it('should register a successful bid', function () { let response = { - 'status': 'ok', - 'ads': { - '/19968336/header-bid-tag-0': [ - { - 'status': 'ok', - 'cpm': 1, - 'tier': 'tier0200', - 'targeting': { - 'rpfl_8000': '201_tier0200', - 'rpfl_elemid': '/19968336/header-bid-tag-0' + cur: 'USD', + seatbid: [{ + bid: [{ + id: '0', + impid: 'instream_video1', + price: 2, + crid: '4259970', + ext: { + bidder: { + rp: { + mime: 'application/javascript', + size_id: 201 + } }, - 'impression_id': 'a40fe16e-d08d-46a9-869d-2e1573599e0c', - 'site_id': 88888, - 'zone_id': 54321, - 'creative_type': 'video', - 'creative_depot_url': 'https://fastlane-adv.rubiconproject.com/v1/creative/a40fe16e-d08d-46a9-869d-2e1573599e0c.xml', - 'ad_id': 999999, - 'creative_id': 'crid-999999', - 'size_id': 201, - 'advertiser': 12345 + prebid: { + targeting: { + hb_uuid: '0c498f63-5111-4bed-98e2-9be7cb932a64' + }, + type: 'video' + } } - ] - }, - 'account_id': 7780 + }], + group: 0, + seat: 'rubicon' + }], }; let bids = spec.interpretResponse({body: response}, { @@ -1996,16 +1852,17 @@ describe('the rubicon adapter', function () { expect(bids).to.be.lengthOf(1); - expect(bids[0].creativeId).to.equal('crid-999999'); - expect(bids[0].cpm).to.equal(1); + expect(bids[0].creativeId).to.equal('4259970'); + expect(bids[0].cpm).to.equal(2); expect(bids[0].ttl).to.equal(300); expect(bids[0].netRevenue).to.equal(false); - expect(bids[0].vastUrl).to.equal( - 'https://fastlane-adv.rubiconproject.com/v1/creative/a40fe16e-d08d-46a9-869d-2e1573599e0c.xml' - ); - expect(bids[0].impression_id).to.equal('a40fe16e-d08d-46a9-869d-2e1573599e0c'); + expect(bids[0].vastUrl).to.equal('0c498f63-5111-4bed-98e2-9be7cb932a64'); + expect(bids[0].videoCacheKey).to.equal('0c498f63-5111-4bed-98e2-9be7cb932a64'); expect(bids[0].mediaType).to.equal('video'); - expect(bids[0].videoCacheKey).to.equal('a40fe16e-d08d-46a9-869d-2e1573599e0c'); + expect(bids[0].bidderCode).to.equal('rubicon'); + expect(bids[0].currency).to.equal('USD'); + expect(bids[0].width).to.equal(640); + expect(bids[0].height).to.equal(480); }); }); }); From b880ee8b19545810c4ae6449143c95381d46e36d Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Wed, 14 Nov 2018 17:59:17 -0800 Subject: [PATCH 03/18] merged pbjs commits --- modules/prebidServerBidAdapter/config.js | 2 -- modules/prebidServerBidAdapter/index.js | 33 ++++++++++-------------- modules/rubiconBidAdapter.js | 3 +-- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/modules/prebidServerBidAdapter/config.js b/modules/prebidServerBidAdapter/config.js index 74eb4a95744..835d09e2010 100644 --- a/modules/prebidServerBidAdapter/config.js +++ b/modules/prebidServerBidAdapter/config.js @@ -2,7 +2,6 @@ export const S2S_VENDORS = { 'appnexus': { adapter: 'prebidServer', - cookieSet: false, enabled: true, endpoint: '//prebid.adnxs.com/pbs/v1/openrtb2/auction', syncEndpoint: '//prebid.adnxs.com/pbs/v1/cookie_sync', @@ -10,7 +9,6 @@ export const S2S_VENDORS = { }, 'rubicon': { adapter: 'prebidServer', - cookieSet: false, enabled: true, endpoint: '//prebid-server.rubiconproject.com/openrtb2/auction', syncEndpoint: '//prebid-server.rubiconproject.com/cookie_sync', diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 5d23a0366ea..80724630da9 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -3,7 +3,6 @@ import bidfactory from 'src/bidfactory'; import * as utils from 'src/utils'; import { ajax } from 'src/ajax'; import { STATUS, S2S, EVENTS } from 'src/constants'; -import { cookieSet } from 'src/cookie.js'; import adaptermanager from 'src/adaptermanager'; import { config } from 'src/config'; import { VIDEO } from 'src/mediaTypes'; @@ -44,7 +43,6 @@ 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 {string} [cookieSetUrl] url for cookie set library, if passed then cookieSet is enabled */ function setS2sConfig(options) { if (options.defaultVendor) { @@ -355,10 +353,8 @@ const LEGACY_PROTOCOL = { * Protocol spec for OpenRTB endpoint * e.g., https:///v1/openrtb2/auction */ +let bidIdMap = {}; const OPEN_RTB_PROTOCOL = { - - bidMap: {}, - buildRequest(s2sBidRequest, bidRequests, adUnits) { let imps = []; let aliases = {}; @@ -368,8 +364,7 @@ const OPEN_RTB_PROTOCOL = { adUnit.bids.forEach(bid => { // OpenRTB response contains the adunit code and bidder name. These are // combined to create a unique key for each bid since an id isn't returned - const key = `${adUnit.code}${bid.bidder}`; - this.bidMap[key] = bid; + bidIdMap[`${adUnit.code}${bid.bidder}`] = bid.bid_id; // check for and store valid aliases to add to the request if (adaptermanager.aliasRegistry[bid.bidder]) { @@ -481,17 +476,22 @@ const OPEN_RTB_PROTOCOL = { // a seatbid object contains a `bid` array and a `seat` string response.seatbid.forEach(seatbid => { (seatbid.bid || []).forEach(bid => { - const bidRequest = utils.getBidRequest( - this.bidMap[`${bid.impid}${seatbid.seat}`].bid_id, - bidderRequests - ); + let bidRequest; + let key = `${bid.impid}${seatbid.seat}`; + if (bidIdMap[key]) { + bidRequest = utils.getBidRequest( + bidIdMap[key], + bidderRequests + ); + } const cpm = bid.price; const status = cpm !== 0 ? STATUS.GOOD : STATUS.NO_BID; - let bidObject = bidfactory.createBid(status, bidRequest); + let bidObject = bidfactory.createBid(status, bidRequest || { + bidder: seatbid.seat, + src: TYPE + }); - bidObject.source = TYPE; - bidObject.bidderCode = seatbid.seat; bidObject.cpm = cpm; let serverResponseTimeMs = utils.deepAccess(response, ['ext', 'responsetimemillis', seatbid.seat].join('.')); @@ -618,11 +618,6 @@ export function PrebidServer() { }); bidderRequests.forEach(bidderRequest => events.emit(EVENTS.BIDDER_DONE, bidderRequest)); - - if (result.status === 'no_cookie' && _s2sConfig.cookieSet && typeof _s2sConfig.cookieSetUrl === 'string') { - // cookie sync - cookieSet(_s2sConfig.cookieSetUrl); - } } catch (error) { utils.logError(error); } diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 2f72579be6f..e39d8a0158c 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -11,8 +11,7 @@ function isSecure() { // use protocol relative urls for http or https export const FASTLANE_ENDPOINT = '//fastlane.rubiconproject.com/a/api/fastlane.json'; -export const VIDEO_ENDPOINT = '//prebid-server.rubiconproject.com/openrtb/auction'; -export const ORTB_ENDPOINT = '//fastlane-adv.rubiconproject.com/v1/auction/video'; +export const VIDEO_ENDPOINT = '//prebid-server.rubiconproject.com/openrtb2/auction'; export const SYNC_ENDPOINT = 'https://eus.rubiconproject.com/usync.html'; var sizeMap = { From 83eea005261c1a5669d01e0e2937e72fa73297b7 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Thu, 24 Jan 2019 12:42:42 -0800 Subject: [PATCH 04/18] reverted karma conf --- karma.conf.maker.js | 13 ++--- .../modules/prebidServerBidAdapter_spec.js | 57 ------------------- 2 files changed, 4 insertions(+), 66 deletions(-) diff --git a/karma.conf.maker.js b/karma.conf.maker.js index 9d07de360f6..649a13a16fc 100644 --- a/karma.conf.maker.js +++ b/karma.conf.maker.js @@ -114,14 +114,7 @@ module.exports = function(codeCoverage, browserstack, watchMode, file) { var webpackConfig = newWebpackConfig(codeCoverage); var plugins = newPluginsArray(browserstack); - // var files = file ? ['test/helpers/prebidGlobal.js', file] : ['test/test_index.js']; - var files = file ? ['test/helpers/prebidGlobal.js', '', file] : ['test/test_index.js']; - - var preprocessors = files.reduce(function(aggregate, filepath) { - aggregate[filepath] = ['webpack', 'sourcemap'] - return aggregate - }, {}) - + var files = file ? ['test/helpers/prebidGlobal.js', file] : ['test/test_index.js']; // This file opens the /debug.html tab automatically. // It has no real value unless you're running --watch, and intend to do some debugging in the browser. if (watchMode) { @@ -145,7 +138,9 @@ module.exports = function(codeCoverage, browserstack, watchMode, file) { // preprocess matching files before serving them to the browser // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor - preprocessors: preprocessors, + preprocessors: { + 'test/test_index.js': ['webpack', 'sourcemap'] + }, // web server port port: 9876, diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index ee2e7f6306c..4f7467cf9b1 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -739,63 +739,6 @@ describe('S2S Adapter', function () { }]); }); - it('s2sConfig \'video.ext.prebid\' is passed through openrtb to PBS', function () { - const s2sConfig = Object.assign({}, CONFIG, { - endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' - }); - config.setConfig({s2sConfig: s2sConfig}); - - const myRequest = utils.deepClone(REQUEST); - adapter.callBids(myRequest, BID_REQUESTS, addBidResponse, done, ajax); - const requestBid = JSON.parse(requests[0].requestBody); - }); - - it('converts rubicon params to expected format for PBS', function () { - const s2sConfig = Object.assign({}, CONFIG, { - endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' - }); - config.setConfig({s2sConfig: s2sConfig}); - - const myRequest = utils.deepClone(REQUEST); - myRequest.ad_units[0].bids[0].params.usePaymentRule = true; - - adapter.callBids(myRequest, BID_REQUESTS, addBidResponse, done, ajax); - const requestBid = JSON.parse(requests[0].requestBody); - - expect(requestBid.imp[0].ext.appnexus).to.exist; - expect(requestBid.imp[0].ext.appnexus.placement_id).to.exist.and.to.equal(10433394); - expect(requestBid.imp[0].ext.appnexus.use_pmt_rule).to.exist.and.to.be.true; - expect(requestBid.imp[0].ext.appnexus.member).to.exist; - expect(requestBid.imp[0].ext.appnexus.keywords).to.exist.and.to.deep.equal([{ - key: 'foo', - value: ['bar', 'baz'] - }, { - 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 () { let cookieSyncConfig = utils.deepClone(CONFIG); cookieSyncConfig.syncEndpoint = 'https://prebid.adnxs.com/pbs/v1/cookie_sync'; From 46cee5a687ec7a85992da6eef35709b4e2761e45 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Thu, 24 Jan 2019 14:30:08 -0800 Subject: [PATCH 05/18] always add ext.prebid.targeting.includewinners: true for openrtb --- modules/prebidServerBidAdapter/index.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 006349b773d..c981fc6ce5c 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -483,6 +483,13 @@ const OPEN_RTB_PROTOCOL = { tmax: _s2sConfig.timeout, imp: imps, test: getConfig('debug') ? 1 : 0, + ext: { + prebid: { + targeting: { + includewinners: true + } + } + } }; _appendSiteAppDevice(request); @@ -493,7 +500,7 @@ const OPEN_RTB_PROTOCOL = { } if (!utils.isEmpty(aliases)) { - request.ext = { prebid: { aliases } }; + request.ext.prebid.aliases = aliases; } if (bidRequests && bidRequests[0].gdprConsent) { From f56e72a4813136d3e8af641636a327bafdead76f Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Thu, 24 Jan 2019 14:50:23 -0800 Subject: [PATCH 06/18] add unit test for ext.prebid.targeting.includewinners for openrtb --- .../modules/prebidServerBidAdapter_spec.js | 45 +++++++++++++------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 4f7467cf9b1..7e8ce470475 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -650,13 +650,10 @@ describe('S2S Adapter', function () { const requestBid = JSON.parse(requests[0].requestBody); - expect(requestBid.ext).to.deep.equal({ - prebid: { - aliases: { - brealtime: 'appnexus' - } - } - }); + expect(requestBid.ext.prebid).to.hasOwnProperty('aliases'); + expect(requestBid.ext.prebid.aliases).to.deep.equal({ + brealtime: 'appnexus' + }) }); it('adds dynamic aliases to request', function () { @@ -680,13 +677,10 @@ describe('S2S Adapter', function () { const requestBid = JSON.parse(requests[0].requestBody); - expect(requestBid.ext).to.deep.equal({ - prebid: { - aliases: { - [alias]: 'appnexus' - } - } - }); + expect(requestBid.ext.prebid).to.hasOwnProperty('aliases'); + expect(requestBid.ext.prebid.aliases).to.deep.equal({ + [alias]: 'appnexus' + }) }); it('converts appnexus params to expected format for PBS', function () { @@ -802,6 +796,29 @@ describe('S2S Adapter', function () { expect(requestBid.imp[0].ext.appnexus).to.haveOwnProperty('key'); expect(requestBid.imp[0].ext.appnexus.key).to.be.equal('value') }); + + 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); + }); }); describe('response handler', function () { From 1ddea2239ee36daada448a8e1e7a7b0e0407733a Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Thu, 24 Jan 2019 15:16:53 -0800 Subject: [PATCH 07/18] added s2sConfig 'video.ext.prebid' support and unit test --- modules/prebidServerBidAdapter/index.js | 8 ++++++ .../modules/prebidServerBidAdapter_spec.js | 28 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index c981fc6ce5c..8664004699c 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -492,6 +492,14 @@ const OPEN_RTB_PROTOCOL = { } }; + if (utils.deepAccess(_s2sConfig, 'video.ext.prebid')) { + request.video = { + ext: { + prebid: utils.deepAccess(_s2sConfig, 'video.ext.prebid') + } + } + } + _appendSiteAppDevice(request); const digiTrust = _getDigiTrustQueryParams(); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 7e8ce470475..46f7a394e3a 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -819,6 +819,34 @@ describe('S2S Adapter', function () { 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', + video: { + ext: { + prebid: { + 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.video).to.haveOwnProperty('ext'); + expect(requestBid.video.ext).to.haveOwnProperty('prebid'); + expect(requestBid.video.ext.prebid).to.deep.equal({ + foo: 'bar' + }); + }); }); describe('response handler', function () { From 99a8344e9bbc594c3b265f21b7b0edca893f7642 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Thu, 24 Jan 2019 15:20:37 -0800 Subject: [PATCH 08/18] added code comments for changes to ORTB --- modules/prebidServerBidAdapter/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 8664004699c..d74046e5c90 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -483,6 +483,7 @@ const OPEN_RTB_PROTOCOL = { tmax: _s2sConfig.timeout, imp: imps, test: getConfig('debug') ? 1 : 0, + // ext.prebid.targeting.includewinners is always true for openrtb endpoints ext: { prebid: { targeting: { @@ -492,6 +493,7 @@ const OPEN_RTB_PROTOCOL = { } }; + // s2sConfig video.ext.prebid is passed through openrtb to PBS if (utils.deepAccess(_s2sConfig, 'video.ext.prebid')) { request.video = { ext: { From ab2da6d1e552e6ccb5de3e6dbbd41e0802bceba4 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Thu, 24 Jan 2019 16:00:03 -0800 Subject: [PATCH 09/18] handle response.ext.prebid.cache values and added unit test --- modules/prebidServerBidAdapter/index.js | 5 +++++ .../modules/prebidServerBidAdapter_spec.js | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index d74046e5c90..1508f64d52c 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -575,6 +575,11 @@ const OPEN_RTB_PROTOCOL = { bidRequest.serverResponseTimeMs = serverResponseTimeMs; } + // making response.ext.prebid.cache values available to prebid core, renderers, and analytics adapters + if (typeof utils.deepAccess(response, 'ext.prebid.cache') === 'object') { + bidObject.cache = utils.deepAccess(response, 'ext.prebid.cache'); + } + if (utils.deepAccess(bid, 'ext.prebid.type') === VIDEO) { bidObject.mediaType = VIDEO; if (bid.adm) { bidObject.vastXml = bid.adm; } diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 46f7a394e3a..c0f96735331 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1083,6 +1083,24 @@ describe('S2S Adapter', function () { expect(response).to.have.property('cpm', 10); }); + it('handles response cache object ext.prebid.cache', function () { + const s2sConfig = Object.assign({}, CONFIG, { + endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' + }); + config.setConfig({s2sConfig}); + const cacheResponse = utils.deepClone(RESPONSE_OPENRTB) + cacheResponse.ext.prebid = { cache: { cachedurl: 'https://foo.com/1234567890' } } + 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.haveOwnProperty('cache'); + expect(typeof response.cache).to.equal('object'); + expect(response.cache).to.deep.equal({ cachedurl: 'https://foo.com/1234567890' }) + }); + it('should log warning for unsupported bidder', function () { server.respondWith(JSON.stringify(RESPONSE_UNSUPPORTED_BIDDER)); From 8ebebe8a8735fe1de3aa70dbecf9dcdc4506be23 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Fri, 25 Jan 2019 11:07:54 -0800 Subject: [PATCH 10/18] update to merged video.ext.prebid with request.ext.prebid --- modules/prebidServerBidAdapter/index.js | 8 ++------ test/spec/modules/prebidServerBidAdapter_spec.js | 6 +++--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 1508f64d52c..d1849dcc574 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -494,12 +494,8 @@ const OPEN_RTB_PROTOCOL = { }; // s2sConfig video.ext.prebid is passed through openrtb to PBS - if (utils.deepAccess(_s2sConfig, 'video.ext.prebid')) { - request.video = { - ext: { - prebid: utils.deepAccess(_s2sConfig, 'video.ext.prebid') - } - } + if (typeof utils.deepAccess(_s2sConfig, 'video.ext.prebid') === 'object') { + request.ext.prebid = utils.deepAccess(_s2sConfig, 'video.ext.prebid') } _appendSiteAppDevice(request); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index c0f96735331..d1cb4abe9c3 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -841,9 +841,9 @@ describe('S2S Adapter', function () { adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(requests[0].requestBody); - expect(requestBid.video).to.haveOwnProperty('ext'); - expect(requestBid.video.ext).to.haveOwnProperty('prebid'); - expect(requestBid.video.ext.prebid).to.deep.equal({ + expect(requestBid).to.haveOwnProperty('ext'); + expect(requestBid.ext).to.haveOwnProperty('prebid'); + expect(requestBid.ext.prebid).to.deep.equal({ foo: 'bar' }); }); From 9779105ac34d0d27f0f6596dd49bee892bc0accd Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Fri, 25 Jan 2019 11:11:36 -0800 Subject: [PATCH 11/18] update to merge includewinners --- modules/prebidServerBidAdapter/index.js | 2 +- test/spec/modules/prebidServerBidAdapter_spec.js | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index d1849dcc574..8ae30a3e678 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -495,7 +495,7 @@ const OPEN_RTB_PROTOCOL = { // s2sConfig video.ext.prebid is passed through openrtb to PBS if (typeof utils.deepAccess(_s2sConfig, 'video.ext.prebid') === 'object') { - request.ext.prebid = utils.deepAccess(_s2sConfig, 'video.ext.prebid') + request.ext.prebid = Object.assign(request.ext.prebid, utils.deepAccess(_s2sConfig, 'video.ext.prebid')); } _appendSiteAppDevice(request); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index d1cb4abe9c3..ceb54cda9cb 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -844,7 +844,10 @@ describe('S2S Adapter', function () { expect(requestBid).to.haveOwnProperty('ext'); expect(requestBid.ext).to.haveOwnProperty('prebid'); expect(requestBid.ext.prebid).to.deep.equal({ - foo: 'bar' + foo: 'bar', + targeting: { + includewinners: true + } }); }); }); From 6da900e81db0529858ec3a432ea320e342febcdb Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Fri, 25 Jan 2019 11:20:07 -0800 Subject: [PATCH 12/18] added test for override used in s2sConfig for ext.prebid.targeting.includewinners value --- .../modules/prebidServerBidAdapter_spec.js | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index ceb54cda9cb..0ef62f6da69 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -850,6 +850,38 @@ describe('S2S Adapter', function () { } }); }); + + 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', + video: { + ext: { + prebid: { + targeting: { + includewinners: 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({ + targeting: { + includewinners: false + } + }); + }); }); describe('response handler', function () { From cd5655f6b31b1901c6d98158c5e5038395df2911 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Fri, 25 Jan 2019 13:13:31 -0800 Subject: [PATCH 13/18] fixes for response cache logic --- modules/prebidServerBidAdapter/index.js | 19 +++++--- .../modules/prebidServerBidAdapter_spec.js | 44 ++++++++++++++++--- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 8ae30a3e678..c3592f08bed 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -571,15 +571,22 @@ const OPEN_RTB_PROTOCOL = { bidRequest.serverResponseTimeMs = serverResponseTimeMs; } - // making response.ext.prebid.cache values available to prebid core, renderers, and analytics adapters - if (typeof utils.deepAccess(response, 'ext.prebid.cache') === 'object') { - bidObject.cache = utils.deepAccess(response, 'ext.prebid.cache'); - } - 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 (typeof bid.ext.prebid.targeting === 'object' && bid.ext.prebid.targeting.hb_uuid && bid.ext.prebid.targeting.hb_cache_hostpath) { + bidObject.videoCacheKey = bid.ext.prebid.targeting.hb_uuid; + // build url using key and cache host + bidObject.vastUrl = `${bid.ext.prebid.targeting.hb_cache_hostpath}?uuid=${bid.ext.prebid.targeting.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; diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 0ef62f6da69..6986164e02f 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1118,22 +1118,54 @@ describe('S2S Adapter', function () { expect(response).to.have.property('cpm', 10); }); - it('handles response cache object ext.prebid.cache', function () { + 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) - cacheResponse.ext.prebid = { cache: { cachedurl: 'https://foo.com/1234567890' } } + 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.haveOwnProperty('cache'); - expect(typeof response.cache).to.equal('object'); - expect(response.cache).to.deep.equal({ cachedurl: 'https://foo.com/1234567890' }) + + 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('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_hostpath: 'https://prebid-cache.net/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 () { From c164d9e952f7725caea06091a40025e7de9d75b5 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Wed, 6 Feb 2019 11:54:00 -0800 Subject: [PATCH 14/18] added support for targeting cache props HB-3740 --- modules/prebidServerBidAdapter/index.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index c3592f08bed..8809bad07a4 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -571,6 +571,11 @@ const OPEN_RTB_PROTOCOL = { bidRequest.serverResponseTimeMs = serverResponseTimeMs; } + // if ext.prebid.targeting exists append adserverTargeting + if (utils.deepAccess(bid, 'ext.prebid.targeting')) { + bidObject.adserverTargeting = bid.ext.prebid.targeting; + } + if (utils.deepAccess(bid, 'ext.prebid.type') === VIDEO) { bidObject.mediaType = VIDEO; From df76400aa6b83d46d9c1321e6f564ff1aa8dff55 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Wed, 6 Feb 2019 12:15:25 -0800 Subject: [PATCH 15/18] added test for adserverTargeting change --- .../modules/prebidServerBidAdapter_spec.js | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 6986164e02f..e537997a35a 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1144,6 +1144,33 @@ describe('S2S Adapter', function () { 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: 'hb-cachepath', + hb_cache_host: 'hb-cachehost', + hb_cache_path_foo: 'hb-cachepath-rubicon', + hb_cache_host_foo: 'hb-cachehost-rubicon' + }; + + 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(targetingTestData); + }); + it('handles response cache from ext.prebid.targeting', function () { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebidserverurl/openrtb2/auction?querystring=param' From 1b38dba0f78a59b5b2f97ec249f9e5468e3779f2 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Thu, 7 Feb 2019 14:27:23 -0800 Subject: [PATCH 16/18] updates to address Jira ticket revision --- modules/prebidServerBidAdapter/index.js | 25 +++-- .../modules/prebidServerBidAdapter_spec.js | 95 +++++++++++++++---- 2 files changed, 90 insertions(+), 30 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 8809bad07a4..a6d53f7600a 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -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) { @@ -483,19 +484,21 @@ const OPEN_RTB_PROTOCOL = { tmax: _s2sConfig.timeout, imp: imps, test: getConfig('debug') ? 1 : 0, - // ext.prebid.targeting.includewinners is always true for openrtb endpoints ext: { prebid: { targeting: { - includewinners: true + // 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 (typeof utils.deepAccess(_s2sConfig, 'video.ext.prebid') === 'object') { - request.ext.prebid = Object.assign(request.ext.prebid, utils.deepAccess(_s2sConfig, 'video.ext.prebid')); + if (_s2sConfig.extPrebid && typeof _s2sConfig.extPrebid === 'object') { + request.ext.prebid = Object.assign(request.ext.prebid, _s2sConfig.extPrebid); } _appendSiteAppDevice(request); @@ -571,9 +574,11 @@ const OPEN_RTB_PROTOCOL = { bidRequest.serverResponseTimeMs = serverResponseTimeMs; } - // if ext.prebid.targeting exists append adserverTargeting - if (utils.deepAccess(bid, 'ext.prebid.targeting')) { - bidObject.adserverTargeting = bid.ext.prebid.targeting; + 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) { @@ -584,10 +589,10 @@ const OPEN_RTB_PROTOCOL = { 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 (typeof bid.ext.prebid.targeting === 'object' && bid.ext.prebid.targeting.hb_uuid && bid.ext.prebid.targeting.hb_cache_hostpath) { - bidObject.videoCacheKey = bid.ext.prebid.targeting.hb_uuid; + } else if (extPrebidTargeting && extPrebidTargeting.hb_uuid && extPrebidTargeting.hb_cache_hostpath) { + bidObject.videoCacheKey = extPrebidTargeting.hb_uuid; // build url using key and cache host - bidObject.vastUrl = `${bid.ext.prebid.targeting.hb_cache_hostpath}?uuid=${bid.ext.prebid.targeting.hb_uuid}`; + bidObject.vastUrl = `${extPrebidTargeting.hb_cache_hostpath}?uuid=${extPrebidTargeting.hb_uuid}`; } if (bid.adm) { bidObject.vastXml = bid.adm; } diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index e537997a35a..488b7df04e1 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -797,6 +797,29 @@ describe('S2S Adapter', function () { expect(requestBid.imp[0].ext.appnexus.key).to.be.equal('value') }); + 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', @@ -823,12 +846,8 @@ describe('S2S Adapter', function () { 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', - video: { - ext: { - prebid: { - foo: 'bar' - } - } + extPrebid: { + foo: 'bar' } }); const _config = { @@ -846,7 +865,8 @@ describe('S2S Adapter', function () { expect(requestBid.ext.prebid).to.deep.equal({ foo: 'bar', targeting: { - includewinners: true + includewinners: true, + includebidderkeys: false } }); }); @@ -854,13 +874,43 @@ describe('S2S Adapter', function () { 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', - video: { - ext: { - prebid: { - targeting: { - includewinners: false - } - } + 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 } } }); @@ -877,8 +927,12 @@ describe('S2S Adapter', function () { 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 + includewinners: false, + includebidderkeys: false } }); }); @@ -1151,10 +1205,8 @@ describe('S2S Adapter', function () { config.setConfig({s2sConfig}); const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO); const targetingTestData = { - hb_cache_path: 'hb-cachepath', - hb_cache_host: 'hb-cachehost', - hb_cache_path_foo: 'hb-cachepath-rubicon', - hb_cache_host_foo: 'hb-cachehost-rubicon' + hb_cache_path: '/cache', + hb_cache_host: 'https://prebid-cache.testurl.com' }; cacheResponse.seatbid.forEach(item => { @@ -1168,7 +1220,10 @@ describe('S2S Adapter', function () { const response = addBidResponse.firstCall.args[1]; expect(response).to.have.property('adserverTargeting'); - expect(response.adserverTargeting).to.deep.equal(targetingTestData); + expect(response.adserverTargeting).to.deep.equal({ + 'hb_cache_path': '/cache', + 'hb_cache_host': 'https://prebid-cache.testurl.com' + }); }); it('handles response cache from ext.prebid.targeting', function () { From f7c0ec8c126aa1a37218859f2e1d8dcafc3ad828 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Fri, 8 Feb 2019 08:20:22 -0800 Subject: [PATCH 17/18] update to build the vastUrl object by utilizing hb_cache_host + hb_cache_path, since the hb_cache_hostpath will be suppressed soon --- modules/prebidServerBidAdapter/index.js | 4 ++-- test/spec/modules/prebidServerBidAdapter_spec.js | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index a6d53f7600a..1527e1e9fd4 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -589,10 +589,10 @@ const OPEN_RTB_PROTOCOL = { 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_hostpath) { + } 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 = `${extPrebidTargeting.hb_cache_hostpath}?uuid=${extPrebidTargeting.hb_uuid}`; + bidObject.vastUrl = `https://${extPrebidTargeting.hb_cache_host}${extPrebidTargeting.hb_cache_path}?uuid=${extPrebidTargeting.hb_uuid}`; } if (bid.adm) { bidObject.vastXml = bid.adm; } diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 488b7df04e1..31d5784d54c 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1206,7 +1206,7 @@ describe('S2S Adapter', function () { const cacheResponse = utils.deepClone(RESPONSE_OPENRTB_VIDEO); const targetingTestData = { hb_cache_path: '/cache', - hb_cache_host: 'https://prebid-cache.testurl.com' + hb_cache_host: 'prebid-cache.testurl.com' }; cacheResponse.seatbid.forEach(item => { @@ -1222,7 +1222,7 @@ describe('S2S Adapter', function () { expect(response).to.have.property('adserverTargeting'); expect(response.adserverTargeting).to.deep.equal({ 'hb_cache_path': '/cache', - 'hb_cache_host': 'https://prebid-cache.testurl.com' + 'hb_cache_host': 'prebid-cache.testurl.com' }); }); @@ -1235,7 +1235,8 @@ describe('S2S Adapter', function () { cacheResponse.seatbid.forEach(item => { item.bid[0].ext.prebid.targeting = { hb_uuid: 'a5ad3993', - hb_cache_hostpath: 'https://prebid-cache.net/cache' + hb_cache_host: 'prebid-cache.net', + hb_cache_path: '/cache' } }); server.respondWith(JSON.stringify(cacheResponse)); From 7351deee1f9e224c5b06581c3339fb0e9bbda5b2 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Wed, 13 Feb 2019 09:40:53 -0800 Subject: [PATCH 18/18] removed single quotes around object literal keys to match formatting --- test/spec/modules/prebidServerBidAdapter_spec.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 71cb3550758..f14c171ee6c 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -655,9 +655,9 @@ describe('S2S Adapter', function () { aliases: { brealtime: 'appnexus' }, - 'targeting': { - 'includebidderkeys': false, - 'includewinners': true + targeting: { + includebidderkeys: false, + includewinners: true } } }); @@ -689,9 +689,9 @@ describe('S2S Adapter', function () { aliases: { [alias]: 'appnexus' }, - 'targeting': { - 'includebidderkeys': false, - 'includewinners': true + targeting: { + includebidderkeys: false, + includewinners: true } } });