diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 3f0bf3a4826..1480bc164b5 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -157,32 +157,6 @@ function newRenderer(adUnitCode, rtbBid, rendererOptions = {}) { return renderer; } -/* Turn keywords parameter into ut-compatible format */ -function getKeywords(keywords) { - let arrs = []; - - utils._each(keywords, (v, k) => { - if (utils.isArray(v)) { - let values = []; - utils._each(v, (val) => { - val = utils.getValueString('keywords.' + k, val); - if (val) { values.push(val); } - }); - v = values; - } else { - v = utils.getValueString('keywords.' + k, v); - if (utils.isStr(v)) { - v = [v]; - } else { - return; - } // unsuported types - don't send a key - } - arrs.push({key: k, value: v}); - }); - - return arrs; -} - /** * Unpack the Server's Bid into a Prebid-compatible one. * @param serverBid @@ -310,7 +284,7 @@ function bidToTag(bid) { tag.external_imp_id = bid.params.externalImpId; } if (!utils.isEmpty(bid.params.keywords)) { - tag.keywords = getKeywords(bid.params.keywords); + tag.keywords = utils.transformBidderParamKeywords(bid.params.keywords); } if (bid.mediaType === NATIVE || utils.deepAccess(bid, `mediaTypes.${NATIVE}`)) { diff --git a/modules/prebidServerBidAdapter.js b/modules/prebidServerBidAdapter.js index fff1331e572..6533d9d00ee 100644 --- a/modules/prebidServerBidAdapter.js +++ b/modules/prebidServerBidAdapter.js @@ -208,7 +208,8 @@ const paramTypes = { 'appnexus': { 'member': tryConvertString, 'invCode': tryConvertString, - 'placementId': tryConvertNumber + 'placementId': tryConvertNumber, + 'keywords': utils.transformBidderParamKeywords }, 'rubicon': { 'accountId': tryConvertNumber, @@ -503,7 +504,7 @@ const OPEN_RTB_PROTOCOL = { // get bidder params in form { : {...params} } const ext = adUnit.bids.reduce((acc, bid) => { - // TODO: move this bidder specific out to a more ideal location (submodule?); issue# pending + // TODO: move this bidder specific out to a more ideal location (submodule?); https://github.com/prebid/Prebid.js/issues/2420 // convert all AppNexus keys to underscore format for pbs if (bid.bidder === 'appnexus') { bid.params.use_pmt_rule = (typeof bid.params.usePaymentRule === 'boolean') ? bid.params.usePaymentRule : false; diff --git a/src/utils.js b/src/utils.js index 678f61de87e..5675ace6bf8 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1046,3 +1046,36 @@ export function isInteger(value) { export function convertCamelToUnderscore(value) { return value.replace(/(?:^|\.?)([A-Z])/g, function (x, y) { return '_' + y.toLowerCase() }).replace(/^_/, ''); } + +/** + * Converts an object of arrays (either strings or numbers) into an array of objects containing key and value properties + * normally read from bidder params + * eg { foo: ['bar', 'baz'], fizz: ['buzz'] } + * becomes [{ key: 'foo', value: ['bar', 'baz']}, {key: 'fizz', value: ['buzz']}] + * @param {Object{Arrays}} keywords object of arrays representing keyvalue pairs + * @param {string} paramName name of parent object (eg 'keywords') containing keyword data, used in error handling + */ +export function transformBidderParamKeywords(keywords, paramName = 'keywords') { + let arrs = []; + + exports._each(keywords, (v, k) => { + if (exports.isArray(v)) { + let values = []; + exports._each(v, (val) => { + val = exports.getValueString(paramName + '.' + k, val); + if (val) { values.push(val); } + }); + v = values; + } else { + v = exports.getValueString(paramName + '.' + k, v); + if (exports.isStr(v)) { + v = [v]; + } else { + return; + } // unsuported types - don't send a key + } + arrs.push({key: k, value: v}); + }); + + return arrs; +} diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index d528c9a3191..b155d61d8e5 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -347,7 +347,11 @@ describe('S2S Adapter', () => { 'bidder': 'appnexus', 'params': { 'placementId': '10433394', - 'member': 123 + 'member': 123, + 'keywords': { + 'foo': ['bar', 'baz'], + 'fizz': ['buzz'] + } }, 'bid_id': '123', 'adUnitCode': 'div-gpt-ad-1460505748561-0', @@ -598,7 +602,7 @@ describe('S2S Adapter', () => { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }); - config.setConfig({s2sConfig}); + config.setConfig({s2sConfig: s2sConfig}); const aliasBidder = { bidder: 'brealtime', @@ -625,7 +629,7 @@ describe('S2S Adapter', () => { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }); - config.setConfig({s2sConfig}); + config.setConfig({s2sConfig: s2sConfig}); const alias = 'foobar'; const aliasBidder = { @@ -655,10 +659,14 @@ describe('S2S Adapter', () => { const s2sConfig = Object.assign({}, CONFIG, { endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' }); - config.setConfig({s2sConfig}); + config.setConfig({s2sConfig: s2sConfig}); const myRequest = utils.deepClone(REQUEST); myRequest.ad_units[0].bids[0].params.usePaymentRule = true; + myRequest.ad_units[0].bids[0].params.keywords = { + foo: ['bar', 'baz'], + fizz: ['buzz'] + }; adapter.callBids(myRequest, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(requests[0].requestBody); @@ -667,6 +675,34 @@ describe('S2S Adapter', () => { 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'] + }]); }); });