From 031fa560069330891e49edb04d513a3a127f1c8a Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Tue, 15 Aug 2017 15:43:08 +0300 Subject: [PATCH 01/30] Add trustx adapter and tests for it --- modules/trustxBidAdapter.js | 210 ++++++++++++++++++ test/spec/modules/trustxBidAdapter_spec.js | 234 +++++++++++++++++++++ 2 files changed, 444 insertions(+) create mode 100644 modules/trustxBidAdapter.js create mode 100644 test/spec/modules/trustxBidAdapter_spec.js diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js new file mode 100644 index 00000000000..a570e564ea4 --- /dev/null +++ b/modules/trustxBidAdapter.js @@ -0,0 +1,210 @@ +var utils = require('src/utils.js'); +var bidfactory = require('src/bidfactory.js'); +var bidmanager = require('src/bidmanager.js'); +var adloader = require('src/adloader'); +var adaptermanager = require('src/adaptermanager'); + +var TrustxAdapter = function TrustxAdapter() { + var bidderCode = 'trustx'; + var reqHost = '//sofia.trustx.org'; + var reqPath = '/hb?'; + var LOG_ERROR_MESS = { + noAuid: 'Bid from response has no auid parameter - ', + noAdm: 'Bid from response has no adm parameter - ', + noBid: 'Array of bid objects is empty', + noPlacementCode: 'Can\'t find placementCode for bid with auid - ', + havePCodeFor: ', placementCode is available only for the following uids - ', + emptyUids: 'Uids should be not empty', + emptySeatbid: 'Seatbid array from response has empty item', + emptyResponse: 'Response is empty', + hasEmptySeatbidArray: 'Response has empty seatbid array', + hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' + }; + + function _makeHandler(auids, placementMap, onTimeoutWorker, needToSendStat) { + var cbName = bidderCode + '_callback_wrapper_' + auids.join('_'); + $$PREBID_GLOBAL$$[cbName] = function(resp) { + delete $$PREBID_GLOBAL$$[cbName]; + _responseProcessing(resp, auids, placementMap, onTimeoutWorker, needToSendStat); + }; + return '$$PREBID_GLOBAL$$.' + cbName; + } + + function _sendRequest(auids, placementMap, onTimeoutWorker) { + var query = []; + var path = reqPath; + var needToSendStat = Math.random() < 0.1; + query.push('u=' + encodeURIComponent(location.href)); + query.push('auids=' + encodeURIComponent(auids.join(','))); + query.push('cb=' + _makeHandler(auids, placementMap, onTimeoutWorker, needToSendStat)); + query.push('pt=' + (window.globalPrebidTrustxPriceType === 'net' ? 'net' : 'gross')); + query.push('hbstat=' + (needToSendStat ? '1' : '0')); + + adloader.loadScript(reqHost + path + query.join('&')); + } + + function _makeTimeoutWorker() { + var active = true; + var handler = function() { + worker.called = true; + $$PREBID_GLOBAL$$.offEvent('bidTimeout', handler); + }; + $$PREBID_GLOBAL$$.onEvent('bidTimeout', handler); + var worker = { + startTime: new Date().getTime(), + off: function() { + if (active && !this.called) { + active = false; + $$PREBID_GLOBAL$$.offEvent('bidTimeout', handler); + } + }, + called: false + }; + return worker; + } + + function _callBids(params) { + var auids = []; + var placementMap = {}; + var hasBid; + var bid; + var bids = params.bids || []; + for (var i = 0; i < bids.length; i++) { + bid = bids[i]; + if (bid && bid.bidder === bidderCode && bid.placementCode) { + hasBid = true; + if (bid.params && bid.params.uid) { + if (!placementMap[bid.params.uid]) { + placementMap[bid.params.uid] = [bid.placementCode]; + auids.push(bid.params.uid); + } else { + placementMap[bid.params.uid].push(bid.placementCode); + } + } + } + } + + if (auids.length) { + var onTimeoutWorker = _makeTimeoutWorker(); + _sendRequest(auids, placementMap, onTimeoutWorker); + } else if (hasBid) { + utils.logError(LOG_ERROR_MESS.emptyUids); + } + } + + function tryToSendStat(startTime, timeout, auids, isTimeouted, error) { + var protocol = location.protocol === 'https:' ? 'https:' : 'http:'; + var stat = { + wrapper: 'prebid', + tt: timeout, + ct: new Date().getTime() - startTime, + st: isTimeouted ? 'to' : error ? 'err' : 'ok', + cr: 10 + }; + stat = encodeURIComponent(JSON.stringify(stat)); + auids = encodeURIComponent(auids.join(',')); + (new Image()).src = protocol + '//sofia.trustx.org/hbstat?auids=' + auids + '&stat=' + stat + '&rnd=' + Math.random(); + } + + function _getBidFromResponse(resp) { + if (!resp) { + utils.logError(LOG_ERROR_MESS.emptySeatbid); + } else if (!resp.bid) { + utils.logError(LOG_ERROR_MESS.hasNoArrayOfBids + JSON.stringify(resp)); + } else if (!resp.bid[0]) { + utils.logError(LOG_ERROR_MESS.noBid); + } + return resp && resp.bid && resp.bid[0]; + } + + function _forEachPlacement(error, bid, placementCode) { + var bidObject; + if (error) { + bidObject = bidfactory.createBid(2); + } else { + bidObject = bidfactory.createBid(1); + bidObject.cpm = bid.price; + bidObject.ad = bid.adm; + bidObject.width = bid.w; + bidObject.height = bid.h; + if (bid.dealid) { + bidObject.dealId = bid.dealid; + } + } + bidObject.bidderCode = bidderCode; + bidmanager.addBidResponse(placementCode, bidObject); + } + + function _addBidResponse(bid, auids, placementMap) { + if (!bid) return; + var errorMessage, placementCodes; + if (!bid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(bid); + else { + placementCodes = placementMap.hasOwnProperty(bid.auid) && placementMap[bid.auid]; + if (!placementCodes) { + errorMessage = LOG_ERROR_MESS.noPlacementCode + bid.auid + LOG_ERROR_MESS.havePCodeFor + auids.join(','); + } + } + + if (!errorMessage) { + if (!bid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(bid); + + var l = placementCodes.length; + while (l--) { + _forEachPlacement(errorMessage, bid, placementCodes[l]); + } + + delete placementMap[bid.auid]; + } + + if (errorMessage) { + utils.logError(errorMessage); + } + } + + function _responseProcessing(resp, auids, placementMap, onTimeoutWorker, needToSendStat) { + var errorMessage; + + if (!resp) errorMessage = LOG_ERROR_MESS.emptyResponse; + else if (resp.seatbid && !resp.seatbid.length) errorMessage = LOG_ERROR_MESS.hasEmptySeatbidArray; + + if (!errorMessage) { + resp = resp.seatbid || []; + var l = resp.length; + while (l--) { + _addBidResponse(_getBidFromResponse(resp[l]), auids, placementMap); + } + } + + var n, bidObj; + for (var auid in placementMap) { + if (placementMap.hasOwnProperty(auid) && placementMap[auid]) { + n = placementMap[auid].length; + while (n--) { + bidObj = bidfactory.createBid(2); + bidObj.bidderCode = bidderCode; + bidmanager.addBidResponse(placementMap[auid][n], bidObj); + } + } + } + + onTimeoutWorker.off(); + needToSendStat && tryToSendStat( + onTimeoutWorker.startTime, + $$PREBID_GLOBAL$$.cbTimeout + $$PREBID_GLOBAL$$.timeoutBuffer, + auids, + onTimeoutWorker.called, + errorMessage + ); + + if (errorMessage) utils.logError(errorMessage); + } + + return { + callBids: _callBids + }; +}; + +adaptermanager.registerBidAdapter(new TrustxAdapter(), 'trustx'); + +module.exports = TrustxAdapter; diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js new file mode 100644 index 00000000000..e559a16d71a --- /dev/null +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -0,0 +1,234 @@ +describe('trustx adapter tests', function () { + var expect = require('chai').expect; + var assert = require('chai').assert; + var urlParse = require('url-parse'); + var querystringify = require('querystringify'); + + var adapter = require('modules/trustxBidAdapter'); + var bidmanager = require('src/bidmanager'); + var adLoader = require('src/adloader'); + var utils = require('src/utils'); + window.pbjs = window.pbjs || {}; + + if (typeof (pbjs) === 'undefined') { + var pbjs = window.pbjs; + } + let stubLoadScript; + beforeEach(function () { + stubLoadScript = sinon.stub(adLoader, 'loadScript'); + }); + afterEach(function () { + stubLoadScript.restore(); + }); + var logErrorSpy; + beforeEach(function () { + logErrorSpy = sinon.spy(utils, 'logError'); + }); + afterEach(function () { + logErrorSpy.restore(); + }); + describe('creation of request url', function () { + if (typeof (pbjs._bidsRequested) === 'undefined') { + pbjs._bidsRequested = []; + } + it('should fix parameter name', function () { + var params = { + bidderCode: 'trustx', + bids: [ + { + bidder: 'trustx', + params: { + uid: 5 + }, + placementCode: 'div-1' + }, + { + bidder: 'trustx', + params: { + uid: 6 + }, + placementCode: 'div-1' + }, + { + bidder: 'trustx', + params: {}, + placementCode: 'div-2' + }, + { + bidder: 'trustx', + params: { + uid: 6, + test: true + }, + placementCode: 'div-3' + }, + { + bidder: 'trustx', + placementCode: 'div-4' + } + ] + }; + adapter().callBids(params); + var bidUrl = stubLoadScript.getCall(0).args[0]; + sinon.assert.calledWith(stubLoadScript, bidUrl); + var parsedBidUrl = urlParse(bidUrl); + var parsedBidUrlQueryString = querystringify.parse(parsedBidUrl.query); + var generatedCallback = 'pbjs.trustx_callback_wrapper_5_6'; + expect(parsedBidUrl.hostname).to.equal('sofia.trustx.org'); + expect(parsedBidUrl.pathname).to.equal('/hb'); + expect(parsedBidUrlQueryString).to.have.property('auids').and.to.equal('5,6'); + expect(parsedBidUrlQueryString).to.have.property('u').and.to.equal(location.href); + expect(parsedBidUrlQueryString).to.have.property('cb').and.to.equal(generatedCallback); + }); + }); + describe('validate incoming params', function () { + if (typeof (pbjs._bidsRequested) === 'undefined') { + pbjs._bidsRequested = []; + } + it('has no correct item in config', function () { + var params = { + bidderCode: 'trustx', + bids: [ + { + bidder: 'trustx', + params: {}, + placementCode: 'div-1' + }, + { + bidder: 'trustx', + placementCode: 'div-1' + } + ] + }; + adapter().callBids(params); + sinon.assert.notCalled(stubLoadScript); + expect(logErrorSpy.getCall(0).args[0]).to.equal('Uids should be not empty'); + }); + }); + describe('handling of the callback response', function () { + if (typeof (pbjs._bidsReceived) === 'undefined') { + pbjs._bidsReceived = []; + } + if (typeof (pbjs._bidsRequested) === 'undefined') { + pbjs._bidsRequested = []; + } + if (typeof (pbjs._adsReceived) === 'undefined') { + pbjs._adsReceived = []; + } + var params = { + bidderCode: 'trustx', + bids: [ + { + bidder: 'trustx', + params: { + uid: 5 + }, + placementCode: '/19968336/header-bid-tag-0' + }, + { + bidder: 'trustx', + params: { + uid: 6 + }, + placementCode: '/19968336/header-bid-tag-1' + }, + { + bidder: 'trustx', + params: { + uid: 42 + }, + placementCode: '/19968336/header-bid-tag-2' + }, + { + bidder: 'trustx', + params: { + uid: 43 + }, + placementCode: '/19968336/header-bid-tag-3' + }, + { + bidder: 'trustx', + params: { + uid: 44 + }, + placementCode: '/19968336/header-bid-tag-4' + }, + { + bidder: 'trustx', + params: { + uid: 45 + }, + placementCode: '/19968336/header-bid-tag-5' + } + ] + }; + it('callback function should exist', function () { + adapter().callBids(params); + expect(pbjs['trustx_callback_wrapper_5_6_42_43_44_45']) + .to.exist.and.to.be.a('function'); + }); + it('bidmanager.addBidResponse should be called with correct arguments', function () { + var stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); + adapter().callBids(params); + var adUnits = []; + var unit = {}; + unit.bids = params.bids; + unit.code = '/19968336/header-bid-tag'; + adUnits.push(unit); + if (typeof (pbjs._bidsRequested) === 'undefined') { + pbjs._bidsRequested = [params]; + } else { + pbjs._bidsRequested.push(params); + } + pbjs.adUnits = adUnits; + var response = { + seatbid: [ + {bid: [{price: 1.15, adm: '
test content 1
', auid: 5, h: 90, w: 728}], seat: '1'}, + {bid: [{price: 0, auid: 6, h: 250, w: 300}], seat: '1'}, + {bid: [{price: 0, adm: '
test content 3
', h: 250, w: 300}], seat: '1'}, + undefined, + {bid: [], seat: '1'}, + {seat: '1'}, + {bid: [{price: 0, adm: '
test content 7
', auid: 46, h: 250, w: 300}], seat: '1'} + ] + }; + pbjs['trustx_callback_wrapper_5_6_42_43_44_45'](response); + var bidPlacementCode1 = stubAddBidResponse.getCall(1).args[0]; + var bidObject1 = stubAddBidResponse.getCall(1).args[1]; + var bidPlacementCode2 = stubAddBidResponse.getCall(0).args[0]; + var bidObject2 = stubAddBidResponse.getCall(0).args[1]; + var bidPlacementCode3 = stubAddBidResponse.getCall(2).args[0]; + var bidObject3 = stubAddBidResponse.getCall(2).args[1]; + var bidPlacementCode4 = stubAddBidResponse.getCall(3).args[0]; + var bidObject4 = stubAddBidResponse.getCall(3).args[1]; + var bidPlacementCode5 = stubAddBidResponse.getCall(4).args[0]; + var bidObject5 = stubAddBidResponse.getCall(4).args[1]; + var bidPlacementCode6 = stubAddBidResponse.getCall(5).args[0]; + var bidObject6 = stubAddBidResponse.getCall(5).args[1]; + expect(logErrorSpy.getCall(5).args[0]).to.equal('Bid from response has no adm parameter - {"price":0,"auid":6,"h":250,"w":300}'); + expect(logErrorSpy.getCall(4).args[0]).to.equal('Bid from response has no auid parameter - {"price":0,"adm":"<' + 'div>test content 3","h":250,"w":300}'); + expect(logErrorSpy.getCall(3).args[0]).to.equal('Seatbid array from response has empty item'); + expect(logErrorSpy.getCall(2).args[0]).to.equal('Array of bid objects is empty'); + expect(logErrorSpy.getCall(1).args[0]).to.equal('Seatbid from response has no array of bid objects - {"seat":"1"}'); + expect(logErrorSpy.getCall(0).args[0]).to.equal('Can\'t find placementCode for bid with auid - 46, placementCode is available only for the following uids - 5,6,42,43,44,45'); + expect(bidPlacementCode1).to.equal('/19968336/header-bid-tag-0'); + expect(bidObject1.cpm).to.equal(1.15); + expect(bidObject1.ad).to.equal('
test content 1
'); + expect(bidObject1.width).to.equal(728); + expect(bidObject1.height).to.equal(90); + expect(bidObject1.getStatusCode()).to.equal(1); + expect(bidObject1.bidderCode).to.equal('trustx'); + expect(bidPlacementCode2).to.equal('/19968336/header-bid-tag-1'); + expect(bidObject2.getStatusCode()).to.equal(2); + expect(bidPlacementCode3).to.equal('/19968336/header-bid-tag-2'); + expect(bidObject3.getStatusCode()).to.equal(2); + expect(bidPlacementCode4).to.equal('/19968336/header-bid-tag-3'); + expect(bidObject4.getStatusCode()).to.equal(2); + expect(bidPlacementCode5).to.equal('/19968336/header-bid-tag-4'); + expect(bidObject5.getStatusCode()).to.equal(2); + expect(bidPlacementCode6).to.equal('/19968336/header-bid-tag-5'); + expect(bidObject6.getStatusCode()).to.equal(2); + stubAddBidResponse.restore(); + }); + }); +}); From 319cadde088c6429e3cc5fd4c8d5873bdd0496fc Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Tue, 15 Aug 2017 16:30:23 +0300 Subject: [PATCH 02/30] update integration example --- integrationExamples/gpt/hello_world.html | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/integrationExamples/gpt/hello_world.html b/integrationExamples/gpt/hello_world.html index 0f5e24a301a..3732b84d534 100644 --- a/integrationExamples/gpt/hello_world.html +++ b/integrationExamples/gpt/hello_world.html @@ -22,11 +22,29 @@ // Replace this object to test a new Adapter! bids: [{ - bidder: 'appnexus', + bidder: 'trustx', params: { - placementId: '10433394' + uid: '42' } - }] + }, + { + bidder: 'trustx', + params: { + uid: '43' + } + }, + { + bidder: 'trustx', + params: { + uid: '44' + } + }, + { + bidder: 'trustx', + params: { + uid: '45' + } + }] }]; From 69ab1f4caf56069e42afc62d60068d9b4c93daf0 Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Sat, 9 Sep 2017 13:26:04 +0300 Subject: [PATCH 03/30] Update trustx adapter --- integrationExamples/gpt/hello_world.html | 24 ++-------- modules/trustxBidAdapter.js | 57 +++--------------------- 2 files changed, 10 insertions(+), 71 deletions(-) diff --git a/integrationExamples/gpt/hello_world.html b/integrationExamples/gpt/hello_world.html index 3732b84d534..0f5e24a301a 100644 --- a/integrationExamples/gpt/hello_world.html +++ b/integrationExamples/gpt/hello_world.html @@ -22,29 +22,11 @@ // Replace this object to test a new Adapter! bids: [{ - bidder: 'trustx', + bidder: 'appnexus', params: { - uid: '42' + placementId: '10433394' } - }, - { - bidder: 'trustx', - params: { - uid: '43' - } - }, - { - bidder: 'trustx', - params: { - uid: '44' - } - }, - { - bidder: 'trustx', - params: { - uid: '45' - } - }] + }] }]; diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index a570e564ea4..07141f92e24 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -21,48 +21,29 @@ var TrustxAdapter = function TrustxAdapter() { hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' }; - function _makeHandler(auids, placementMap, onTimeoutWorker, needToSendStat) { + function _makeHandler(auids, placementMap) { var cbName = bidderCode + '_callback_wrapper_' + auids.join('_'); $$PREBID_GLOBAL$$[cbName] = function(resp) { delete $$PREBID_GLOBAL$$[cbName]; - _responseProcessing(resp, auids, placementMap, onTimeoutWorker, needToSendStat); + _responseProcessing(resp, auids, placementMap); }; return '$$PREBID_GLOBAL$$.' + cbName; } - function _sendRequest(auids, placementMap, onTimeoutWorker) { + function _sendRequest(auids, placementMap) { var query = []; var path = reqPath; var needToSendStat = Math.random() < 0.1; + $$PREBID_GLOBAL$$.needToSendTrustxStat = needToSendStat; query.push('u=' + encodeURIComponent(location.href)); query.push('auids=' + encodeURIComponent(auids.join(','))); - query.push('cb=' + _makeHandler(auids, placementMap, onTimeoutWorker, needToSendStat)); + query.push('cb=' + _makeHandler(auids, placementMap)); query.push('pt=' + (window.globalPrebidTrustxPriceType === 'net' ? 'net' : 'gross')); query.push('hbstat=' + (needToSendStat ? '1' : '0')); adloader.loadScript(reqHost + path + query.join('&')); } - function _makeTimeoutWorker() { - var active = true; - var handler = function() { - worker.called = true; - $$PREBID_GLOBAL$$.offEvent('bidTimeout', handler); - }; - $$PREBID_GLOBAL$$.onEvent('bidTimeout', handler); - var worker = { - startTime: new Date().getTime(), - off: function() { - if (active && !this.called) { - active = false; - $$PREBID_GLOBAL$$.offEvent('bidTimeout', handler); - } - }, - called: false - }; - return worker; - } - function _callBids(params) { var auids = []; var placementMap = {}; @@ -85,27 +66,12 @@ var TrustxAdapter = function TrustxAdapter() { } if (auids.length) { - var onTimeoutWorker = _makeTimeoutWorker(); - _sendRequest(auids, placementMap, onTimeoutWorker); + _sendRequest(auids, placementMap); } else if (hasBid) { utils.logError(LOG_ERROR_MESS.emptyUids); } } - function tryToSendStat(startTime, timeout, auids, isTimeouted, error) { - var protocol = location.protocol === 'https:' ? 'https:' : 'http:'; - var stat = { - wrapper: 'prebid', - tt: timeout, - ct: new Date().getTime() - startTime, - st: isTimeouted ? 'to' : error ? 'err' : 'ok', - cr: 10 - }; - stat = encodeURIComponent(JSON.stringify(stat)); - auids = encodeURIComponent(auids.join(',')); - (new Image()).src = protocol + '//sofia.trustx.org/hbstat?auids=' + auids + '&stat=' + stat + '&rnd=' + Math.random(); - } - function _getBidFromResponse(resp) { if (!resp) { utils.logError(LOG_ERROR_MESS.emptySeatbid); @@ -162,7 +128,7 @@ var TrustxAdapter = function TrustxAdapter() { } } - function _responseProcessing(resp, auids, placementMap, onTimeoutWorker, needToSendStat) { + function _responseProcessing(resp, auids, placementMap) { var errorMessage; if (!resp) errorMessage = LOG_ERROR_MESS.emptyResponse; @@ -188,15 +154,6 @@ var TrustxAdapter = function TrustxAdapter() { } } - onTimeoutWorker.off(); - needToSendStat && tryToSendStat( - onTimeoutWorker.startTime, - $$PREBID_GLOBAL$$.cbTimeout + $$PREBID_GLOBAL$$.timeoutBuffer, - auids, - onTimeoutWorker.called, - errorMessage - ); - if (errorMessage) utils.logError(errorMessage); } From ef500123318cb6b8cd3a2d730ee5017f799e65e3 Mon Sep 17 00:00:00 2001 From: pwyrembak Date: Tue, 19 Sep 2017 18:54:52 +0300 Subject: [PATCH 04/30] Post-review fixes of Trustx adapter --- modules/trustxBidAdapter.js | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 07141f92e24..44c00c2c0b3 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -1,14 +1,15 @@ -var utils = require('src/utils.js'); -var bidfactory = require('src/bidfactory.js'); -var bidmanager = require('src/bidmanager.js'); -var adloader = require('src/adloader'); -var adaptermanager = require('src/adaptermanager'); +const utils = require('src/utils.js'); +const bidfactory = require('src/bidfactory.js'); +const bidmanager = require('src/bidmanager.js'); +const adloader = require('src/adloader'); +const adaptermanager = require('src/adaptermanager'); +const CONSTANTS = require('src/constants.json'); var TrustxAdapter = function TrustxAdapter() { - var bidderCode = 'trustx'; - var reqHost = '//sofia.trustx.org'; - var reqPath = '/hb?'; - var LOG_ERROR_MESS = { + const bidderCode = 'trustx'; + const reqHost = '//sofia.trustx.org'; + const reqPath = '/hb?'; + const LOG_ERROR_MESS = { noAuid: 'Bid from response has no auid parameter - ', noAdm: 'Bid from response has no adm parameter - ', noBid: 'Array of bid objects is empty', @@ -33,13 +34,10 @@ var TrustxAdapter = function TrustxAdapter() { function _sendRequest(auids, placementMap) { var query = []; var path = reqPath; - var needToSendStat = Math.random() < 0.1; - $$PREBID_GLOBAL$$.needToSendTrustxStat = needToSendStat; query.push('u=' + encodeURIComponent(location.href)); query.push('auids=' + encodeURIComponent(auids.join(','))); query.push('cb=' + _makeHandler(auids, placementMap)); query.push('pt=' + (window.globalPrebidTrustxPriceType === 'net' ? 'net' : 'gross')); - query.push('hbstat=' + (needToSendStat ? '1' : '0')); adloader.loadScript(reqHost + path + query.join('&')); } @@ -86,9 +84,9 @@ var TrustxAdapter = function TrustxAdapter() { function _forEachPlacement(error, bid, placementCode) { var bidObject; if (error) { - bidObject = bidfactory.createBid(2); + bidObject = bidfactory.createBid(CONSTANTS.STATUS.NO_BID, bid); } else { - bidObject = bidfactory.createBid(1); + bidObject = bidfactory.createBid(CONSTANTS.STATUS.GOOD, bid); bidObject.cpm = bid.price; bidObject.ad = bid.adm; bidObject.width = bid.w; @@ -147,7 +145,7 @@ var TrustxAdapter = function TrustxAdapter() { if (placementMap.hasOwnProperty(auid) && placementMap[auid]) { n = placementMap[auid].length; while (n--) { - bidObj = bidfactory.createBid(2); + bidObj = bidfactory.createBid(CONSTANTS.STATUS.NO_BID); bidObj.bidderCode = bidderCode; bidmanager.addBidResponse(placementMap[auid][n], bidObj); } From 843440dd0f923e2e77c86bac1f5a8e7591f97b1c Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Tue, 10 Oct 2017 17:23:05 +0300 Subject: [PATCH 05/30] Code improvement for trustx adapter: changed default price type from gross to net --- modules/trustxBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 44c00c2c0b3..13f893a841d 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -37,7 +37,7 @@ var TrustxAdapter = function TrustxAdapter() { query.push('u=' + encodeURIComponent(location.href)); query.push('auids=' + encodeURIComponent(auids.join(','))); query.push('cb=' + _makeHandler(auids, placementMap)); - query.push('pt=' + (window.globalPrebidTrustxPriceType === 'net' ? 'net' : 'gross')); + query.push('pt=' + (window.globalPrebidTrustxPriceType === 'gross' ? 'gross' : 'net')); adloader.loadScript(reqHost + path + query.join('&')); } From aa249b58f41829111726bb9ea7d99ef37bd7b959 Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Tue, 17 Oct 2017 22:08:36 +0300 Subject: [PATCH 06/30] Update TrustX adapter to support the 1.0 version --- modules/trustxBidAdapter.js | 284 ++++++------- modules/trustxBidAdapter.md | 39 ++ test/spec/modules/trustxBidAdapter_spec.js | 473 ++++++++++++--------- 3 files changed, 438 insertions(+), 358 deletions(-) create mode 100755 modules/trustxBidAdapter.md diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 13f893a841d..e7d163828c0 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -1,165 +1,147 @@ -const utils = require('src/utils.js'); -const bidfactory = require('src/bidfactory.js'); -const bidmanager = require('src/bidmanager.js'); -const adloader = require('src/adloader'); -const adaptermanager = require('src/adaptermanager'); -const CONSTANTS = require('src/constants.json'); +import * as utils from 'src/utils'; +import {registerBidder} from 'src/adapters/bidderFactory'; +const BIDDER_CODE = 'trustx'; +const ENDPOINT_URL = '//sofia.trustx.org/hb'; +const TIME_TO_LIVE = 360; +const ADAPTER_SYNC_URL = '//sofia.trustx.org/push_sync'; +const LOG_ERROR_MESS = { + noAuid: 'Bid from response has no auid parameter - ', + noAdm: 'Bid from response has no adm parameter - ', + noBid: 'Array of bid objects is empty', + noPlacementCode: 'Can\'t find in requested bids the bid with auid - ', + emptyUids: 'Uids should be not empty', + emptySeatbid: 'Seatbid array from response has empty item', + emptyResponse: 'Response is empty', + hasEmptySeatbidArray: 'Response has empty seatbid array', + hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' +}; +const bidsRequestMap = {}; +export const spec = { + code: BIDDER_CODE, + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function(bid) { + return !!(bid.params.uid && bid.adUnitCode); + }, + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests - an array of bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests) { + const auids = []; + const bidsMap = {}; + const bids = validBidRequests || []; + const bidderRequestId = bids[0] && bids[0].bidderRequestId; + + bids.forEach(bid => { + if (!bidsMap[bid.params.uid]) { + bidsMap[bid.params.uid] = [bid]; + auids.push(bid.params.uid); + } else { + bidsMap[bid.params.uid].push(bid); + } + }); -var TrustxAdapter = function TrustxAdapter() { - const bidderCode = 'trustx'; - const reqHost = '//sofia.trustx.org'; - const reqPath = '/hb?'; - const LOG_ERROR_MESS = { - noAuid: 'Bid from response has no auid parameter - ', - noAdm: 'Bid from response has no adm parameter - ', - noBid: 'Array of bid objects is empty', - noPlacementCode: 'Can\'t find placementCode for bid with auid - ', - havePCodeFor: ', placementCode is available only for the following uids - ', - emptyUids: 'Uids should be not empty', - emptySeatbid: 'Seatbid array from response has empty item', - emptyResponse: 'Response is empty', - hasEmptySeatbidArray: 'Response has empty seatbid array', - hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' - }; + bidsRequestMap[bidderRequestId] = bidsMap; - function _makeHandler(auids, placementMap) { - var cbName = bidderCode + '_callback_wrapper_' + auids.join('_'); - $$PREBID_GLOBAL$$[cbName] = function(resp) { - delete $$PREBID_GLOBAL$$[cbName]; - _responseProcessing(resp, auids, placementMap); + const payload = { + u: utils.getTopWindowUrl(), + pt: window.globalPrebidTrustxPriceType === 'gross' ? 'gross' : 'net', + auids: auids.join(','), + reqid: bidderRequestId, }; - return '$$PREBID_GLOBAL$$.' + cbName; - } - - function _sendRequest(auids, placementMap) { - var query = []; - var path = reqPath; - query.push('u=' + encodeURIComponent(location.href)); - query.push('auids=' + encodeURIComponent(auids.join(','))); - query.push('cb=' + _makeHandler(auids, placementMap)); - query.push('pt=' + (window.globalPrebidTrustxPriceType === 'gross' ? 'gross' : 'net')); - - adloader.loadScript(reqHost + path + query.join('&')); - } - function _callBids(params) { - var auids = []; - var placementMap = {}; - var hasBid; - var bid; - var bids = params.bids || []; - for (var i = 0; i < bids.length; i++) { - bid = bids[i]; - if (bid && bid.bidder === bidderCode && bid.placementCode) { - hasBid = true; - if (bid.params && bid.params.uid) { - if (!placementMap[bid.params.uid]) { - placementMap[bid.params.uid] = [bid.placementCode]; - auids.push(bid.params.uid); - } else { - placementMap[bid.params.uid].push(bid.placementCode); - } - } - } + // const payloadString = JSON.stringify(payload); + return { + method: 'GET', + url: ENDPOINT_URL, + data: payload, + }; + }, + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @param {*} bidRequest + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse, bidRequest) { + const bidResponses = []; + const bidsMap = bidsRequestMap[bidRequest.data.reqid]; + + let errorMessage; + + if (!serverResponse) errorMessage = LOG_ERROR_MESS.emptyResponse; + else if (serverResponse.seatbid && !serverResponse.seatbid.length) { + errorMessage = LOG_ERROR_MESS.hasEmptySeatbidArray; } - if (auids.length) { - _sendRequest(auids, placementMap); - } else if (hasBid) { - utils.logError(LOG_ERROR_MESS.emptyUids); + if (!errorMessage && serverResponse.seatbid) { + serverResponse.seatbid.forEach(respItem => { + _addBidResponse(_getBidFromResponse(respItem), bidsMap, bidResponses); + }); } - } - - function _getBidFromResponse(resp) { - if (!resp) { - utils.logError(LOG_ERROR_MESS.emptySeatbid); - } else if (!resp.bid) { - utils.logError(LOG_ERROR_MESS.hasNoArrayOfBids + JSON.stringify(resp)); - } else if (!resp.bid[0]) { - utils.logError(LOG_ERROR_MESS.noBid); + if (errorMessage) utils.logError(errorMessage); + return bidResponses; + }, + getUserSyncs: function(syncOptions) { + if (syncOptions.pixelEnabled) { + return [{ + type: 'image', + url: ADAPTER_SYNC_URL + }]; } - return resp && resp.bid && resp.bid[0]; } - - function _forEachPlacement(error, bid, placementCode) { - var bidObject; - if (error) { - bidObject = bidfactory.createBid(CONSTANTS.STATUS.NO_BID, bid); - } else { - bidObject = bidfactory.createBid(CONSTANTS.STATUS.GOOD, bid); - bidObject.cpm = bid.price; - bidObject.ad = bid.adm; - bidObject.width = bid.w; - bidObject.height = bid.h; - if (bid.dealid) { - bidObject.dealId = bid.dealid; - } - } - bidObject.bidderCode = bidderCode; - bidmanager.addBidResponse(placementCode, bidObject); +} + +function _getBidFromResponse(respItem) { + if (!respItem) { + utils.logError(LOG_ERROR_MESS.emptySeatbid); + } else if (!respItem.bid) { + utils.logError(LOG_ERROR_MESS.hasNoArrayOfBids + JSON.stringify(respItem)); + } else if (!respItem.bid[0]) { + utils.logError(LOG_ERROR_MESS.noBid); } - - function _addBidResponse(bid, auids, placementMap) { - if (!bid) return; - var errorMessage, placementCodes; - if (!bid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(bid); - else { - placementCodes = placementMap.hasOwnProperty(bid.auid) && placementMap[bid.auid]; - if (!placementCodes) { - errorMessage = LOG_ERROR_MESS.noPlacementCode + bid.auid + LOG_ERROR_MESS.havePCodeFor + auids.join(','); - } - } - - if (!errorMessage) { - if (!bid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(bid); - - var l = placementCodes.length; - while (l--) { - _forEachPlacement(errorMessage, bid, placementCodes[l]); - } - - delete placementMap[bid.auid]; - } - - if (errorMessage) { - utils.logError(errorMessage); + return respItem && respItem.bid && respItem.bid[0]; +} + +function _addBidResponse(serverBid, bidsMap, bidResponses) { + if (!serverBid) return; + let errorMessage; + if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); + if (!serverBid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); + else { + const awaitingBids = bidsMap[serverBid.auid]; + if (awaitingBids) { + awaitingBids.forEach(bid => { + const bidResponse = { + requestId: bid.bidId, // bid.bidderRequestId, + bidderCode: spec.code, + cpm: serverBid.price, + width: serverBid.w, + height: serverBid.h, + creativeId: serverBid.auid, // bid.bidId, + currency: 'USD', + netRevenue: window.globalPrebidTrustxPriceType !== 'gross', + ttl: TIME_TO_LIVE, + ad: serverBid.adm, + dealId: serverBid.dealid + }; + bidResponses.push(bidResponse); + }); + } else { + errorMessage = LOG_ERROR_MESS.noPlacementCode + serverBid.auid; } } - - function _responseProcessing(resp, auids, placementMap) { - var errorMessage; - - if (!resp) errorMessage = LOG_ERROR_MESS.emptyResponse; - else if (resp.seatbid && !resp.seatbid.length) errorMessage = LOG_ERROR_MESS.hasEmptySeatbidArray; - - if (!errorMessage) { - resp = resp.seatbid || []; - var l = resp.length; - while (l--) { - _addBidResponse(_getBidFromResponse(resp[l]), auids, placementMap); - } - } - - var n, bidObj; - for (var auid in placementMap) { - if (placementMap.hasOwnProperty(auid) && placementMap[auid]) { - n = placementMap[auid].length; - while (n--) { - bidObj = bidfactory.createBid(CONSTANTS.STATUS.NO_BID); - bidObj.bidderCode = bidderCode; - bidmanager.addBidResponse(placementMap[auid][n], bidObj); - } - } - } - - if (errorMessage) utils.logError(errorMessage); + if (errorMessage) { + utils.logError(errorMessage); } +} - return { - callBids: _callBids - }; -}; - -adaptermanager.registerBidAdapter(new TrustxAdapter(), 'trustx'); - -module.exports = TrustxAdapter; +registerBidder(spec); diff --git a/modules/trustxBidAdapter.md b/modules/trustxBidAdapter.md new file mode 100755 index 00000000000..7952b8845cf --- /dev/null +++ b/modules/trustxBidAdapter.md @@ -0,0 +1,39 @@ +# Overview + +Module Name: TrustX Bidder Adapter +Module Type: Bidder Adapter +Maintainer: paul@trustx.org + +# Description + +Module that connects to TrustX demand source to fetch bids. + +# Test Parameters +``` + window.globalPrebidTrustxPriceType = 'gross'; // by default is 'net' + var adUnits = [ + { + code: 'test-div', + sizes: [[300, 250]], + bids: [ + { + bidder: "trustx", + params: { + uid: '44' + } + } + ] + },{ + code: 'test-div', + sizes: [[728, 90]], + bids: [ + { + bidder: "trustx", + params: { + uid: 45 + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index 7208ebef343..16760d2e3ef 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -1,234 +1,293 @@ -describe('trustx adapter tests', function () { - var expect = require('chai').expect; - var assert = require('chai').assert; - var urlParse = require('url-parse'); - var querystringify = require('querystringify'); - - var adapter = require('modules/trustxBidAdapter'); - var bidmanager = require('src/bidmanager'); - var adLoader = require('src/adloader'); - var utils = require('src/utils'); - window.$$PREBID_GLOBAL$$ = window.$$PREBID_GLOBAL$$ || {}; - - if (typeof (pbjs) === 'undefined') { - var pbjs = window.$$PREBID_GLOBAL$$; - } - let stubLoadScript; - beforeEach(function () { - stubLoadScript = sinon.stub(adLoader, 'loadScript'); - }); - afterEach(function () { - stubLoadScript.restore(); - }); - var logErrorSpy; - beforeEach(function () { - logErrorSpy = sinon.spy(utils, 'logError'); - }); - afterEach(function () { - logErrorSpy.restore(); +import { expect } from 'chai'; +import { spec } from 'modules/trustxBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +describe('TrustXAdapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); }); - describe('creation of request url', function () { - if (typeof (pbjs._bidsRequested) === 'undefined') { - pbjs._bidsRequested = []; - } - it('should fix parameter name', function () { - var params = { - bidderCode: 'trustx', - bids: [ - { - bidder: 'trustx', - params: { - uid: 5 - }, - placementCode: 'div-1' - }, - { - bidder: 'trustx', - params: { - uid: 6 - }, - placementCode: 'div-1' - }, - { - bidder: 'trustx', - params: {}, - placementCode: 'div-2' - }, - { - bidder: 'trustx', - params: { - uid: 6, - test: true - }, - placementCode: 'div-3' - }, - { - bidder: 'trustx', - placementCode: 'div-4' - } - ] + + describe('isBidRequestValid', () => { + let bid = { + 'bidder': 'trustx', + 'params': { + 'uid': '44' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'uid': 0 }; - adapter().callBids(params); - var bidUrl = stubLoadScript.getCall(0).args[0]; - sinon.assert.calledWith(stubLoadScript, bidUrl); - var parsedBidUrl = urlParse(bidUrl); - var parsedBidUrlQueryString = querystringify.parse(parsedBidUrl.query); - var generatedCallback = '$$PREBID_GLOBAL$$.trustx_callback_wrapper_5_6'; - expect(parsedBidUrl.hostname).to.equal('sofia.trustx.org'); - expect(parsedBidUrl.pathname).to.equal('/hb'); - expect(parsedBidUrlQueryString).to.have.property('auids').and.to.equal('5,6'); - expect(parsedBidUrlQueryString).to.have.property('u').and.to.equal(location.href); - expect(parsedBidUrlQueryString).to.have.property('cb').and.to.equal(generatedCallback); + expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); - describe('validate incoming params', function () { - if (typeof (pbjs._bidsRequested) === 'undefined') { - pbjs._bidsRequested = []; - } - it('has no correct item in config', function () { - var params = { - bidderCode: 'trustx', - bids: [ - { - bidder: 'trustx', - params: {}, - placementCode: 'div-1' - }, - { - bidder: 'trustx', - placementCode: 'div-1' - } - ] - }; - adapter().callBids(params); - sinon.assert.notCalled(stubLoadScript); - expect(logErrorSpy.getCall(0).args[0]).to.equal('Uids should be not empty'); + + describe('buildRequests', () => { + let bidRequests = [ + { + 'bidder': 'trustx', + 'params': { + 'uid': '43' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '43' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '3150ccb55da321', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '45' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '42dbe3a7168a6a', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + it('should attach valid params to the tag', () => { + const request = spec.buildRequests([bidRequests[0]]); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('pt', 'net'); + expect(payload).to.have.property('auids', '43'); + expect(payload).to.have.property('reqid', '22edbae2733bf6'); + }); + + it('auids must not be duplicated', () => { + const request = spec.buildRequests(bidRequests); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('pt', 'net'); + expect(payload).to.have.property('auids', '43,45'); + expect(payload).to.have.property('reqid', '22edbae2733bf6'); + }); + + it('pt parameter must be "gross" if window.globalPrebidTrustxPriceType === "gross"', () => { + window.globalPrebidTrustxPriceType = 'gross'; + const request = spec.buildRequests(bidRequests); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('pt', 'gross'); + expect(payload).to.have.property('auids', '43,45'); + expect(payload).to.have.property('reqid', '22edbae2733bf6'); + delete window.globalPrebidTrustxPriceType; + }); + + it('pt parameter must be "net" or "gross"', () => { + window.globalPrebidTrustxPriceType = 'some'; + const request = spec.buildRequests(bidRequests); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('pt', 'net'); + expect(payload).to.have.property('auids', '43,45'); + expect(payload).to.have.property('reqid', '22edbae2733bf6'); + delete window.globalPrebidTrustxPriceType; }); }); - describe('handling of the callback response', function () { - if (typeof (pbjs._bidsReceived) === 'undefined') { - pbjs._bidsReceived = []; - } - if (typeof (pbjs._bidsRequested) === 'undefined') { - pbjs._bidsRequested = []; - } - if (typeof (pbjs._adsReceived) === 'undefined') { - pbjs._adsReceived = []; - } - var params = { - bidderCode: 'trustx', - bids: [ + + describe('interpretResponse', () => { + const responses = [ + {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 43, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 44, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'price': 0, 'auid': 45, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0, 'adm': '
test content 4
', 'h': 250, 'w': 300}], 'seat': '1'}, + undefined, + {'bid': [], 'seat': '1'}, + {'seat': '1'}, + ]; + + it('should get correct bid response', () => { + const bidRequests = [ + { + 'bidder': 'trustx', + 'params': { + 'uid': '43' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '659423fff799cb', + 'bidderRequestId': '5f2009617a7c0a', + 'auctionId': '1cbd2feafe5e8b', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '659423fff799cb', + 'cpm': 1.15, + 'creativeId': 43, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
test content 1
', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'seatbid': [responses[0]]}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('should get correct multi bid response', () => { + const bidRequests = [ { - bidder: 'trustx', - params: { - uid: 5 + 'bidder': 'trustx', + 'params': { + 'uid': '43' }, - placementCode: '/19968336/header-bid-tag-0' + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '300bfeb0d71a5b', + 'bidderRequestId': '2c2bb1972df9a', + 'auctionId': '1fa09aee5c8c99', }, { - bidder: 'trustx', - params: { - uid: 6 + 'bidder': 'trustx', + 'params': { + 'uid': '44' }, - placementCode: '/19968336/header-bid-tag-1' + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '4dff80cc4ee346', + 'bidderRequestId': '2c2bb1972df9a', + 'auctionId': '1fa09aee5c8c99', }, { - bidder: 'trustx', - params: { - uid: 42 + 'bidder': 'trustx', + 'params': { + 'uid': '43' }, - placementCode: '/19968336/header-bid-tag-2' + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '5703af74d0472a', + 'bidderRequestId': '2c2bb1972df9a', + 'auctionId': '1fa09aee5c8c99', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '300bfeb0d71a5b', + 'cpm': 1.15, + 'creativeId': 43, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
test content 1
', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '5703af74d0472a', + 'cpm': 1.15, + 'creativeId': 43, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
test content 1
', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, }, { - bidder: 'trustx', - params: { - uid: 43 + 'requestId': '4dff80cc4ee346', + 'cpm': 0.5, + 'creativeId': 44, + 'dealId': undefined, + 'width': 728, + 'height': 90, + 'ad': '
test content 2
', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'seatbid': [responses[0], responses[1]]}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('handles wrong and nobid responses', () => { + const bidRequests = [ + { + 'bidder': 'trustx', + 'params': { + 'uid': '45' }, - placementCode: '/19968336/header-bid-tag-3' + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '300bfeb0d7190gf', + 'bidderRequestId': '2c2bb1972d23af', + 'auctionId': '1fa09aee5c84d34', }, { - bidder: 'trustx', - params: { - uid: 44 + 'bidder': 'trustx', + 'params': { + 'uid': '46' }, - placementCode: '/19968336/header-bid-tag-4' + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '300bfeb0d71321', + 'bidderRequestId': '2c2bb1972d23af', + 'auctionId': '1fa09aee5c84d34', }, { - bidder: 'trustx', - params: { - uid: 45 + 'bidder': 'trustx', + 'params': { + 'uid': '50' }, - placementCode: '/19968336/header-bid-tag-5' + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '300bfeb0d7183bb', + 'bidderRequestId': '2c2bb1972d23af', + 'auctionId': '1fa09aee5c84d34', } - ] - }; - it('callback function should exist', function () { - adapter().callBids(params); - expect(pbjs['trustx_callback_wrapper_5_6_42_43_44_45']) - .to.exist.and.to.be.a('function'); - }); - it('bidmanager.addBidResponse should be called with correct arguments', function () { - var stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); - adapter().callBids(params); - var adUnits = []; - var unit = {}; - unit.bids = params.bids; - unit.code = '/19968336/header-bid-tag'; - adUnits.push(unit); - if (typeof (pbjs._bidsRequested) === 'undefined') { - pbjs._bidsRequested = [params]; - } else { - pbjs._bidsRequested.push(params); - } - pbjs.adUnits = adUnits; - var response = { - seatbid: [ - {bid: [{price: 1.15, adm: '
test content 1
', auid: 5, h: 90, w: 728}], seat: '1'}, - {bid: [{price: 0, auid: 6, h: 250, w: 300}], seat: '1'}, - {bid: [{price: 0, adm: '
test content 3
', h: 250, w: 300}], seat: '1'}, - undefined, - {bid: [], seat: '1'}, - {seat: '1'}, - {bid: [{price: 0, adm: '
test content 7
', auid: 46, h: 250, w: 300}], seat: '1'} - ] - }; - pbjs['trustx_callback_wrapper_5_6_42_43_44_45'](response); - var bidPlacementCode1 = stubAddBidResponse.getCall(1).args[0]; - var bidObject1 = stubAddBidResponse.getCall(1).args[1]; - var bidPlacementCode2 = stubAddBidResponse.getCall(0).args[0]; - var bidObject2 = stubAddBidResponse.getCall(0).args[1]; - var bidPlacementCode3 = stubAddBidResponse.getCall(2).args[0]; - var bidObject3 = stubAddBidResponse.getCall(2).args[1]; - var bidPlacementCode4 = stubAddBidResponse.getCall(3).args[0]; - var bidObject4 = stubAddBidResponse.getCall(3).args[1]; - var bidPlacementCode5 = stubAddBidResponse.getCall(4).args[0]; - var bidObject5 = stubAddBidResponse.getCall(4).args[1]; - var bidPlacementCode6 = stubAddBidResponse.getCall(5).args[0]; - var bidObject6 = stubAddBidResponse.getCall(5).args[1]; - expect(logErrorSpy.getCall(5).args[0]).to.equal('Bid from response has no adm parameter - {"price":0,"auid":6,"h":250,"w":300}'); - expect(logErrorSpy.getCall(4).args[0]).to.equal('Bid from response has no auid parameter - {"price":0,"adm":"<' + 'div>test content 3","h":250,"w":300}'); - expect(logErrorSpy.getCall(3).args[0]).to.equal('Seatbid array from response has empty item'); - expect(logErrorSpy.getCall(2).args[0]).to.equal('Array of bid objects is empty'); - expect(logErrorSpy.getCall(1).args[0]).to.equal('Seatbid from response has no array of bid objects - {"seat":"1"}'); - expect(logErrorSpy.getCall(0).args[0]).to.equal('Can\'t find placementCode for bid with auid - 46, placementCode is available only for the following uids - 5,6,42,43,44,45'); - expect(bidPlacementCode1).to.equal('/19968336/header-bid-tag-0'); - expect(bidObject1.cpm).to.equal(1.15); - expect(bidObject1.ad).to.equal('
test content 1
'); - expect(bidObject1.width).to.equal(728); - expect(bidObject1.height).to.equal(90); - expect(bidObject1.getStatusCode()).to.equal(1); - expect(bidObject1.bidderCode).to.equal('trustx'); - expect(bidPlacementCode2).to.equal('/19968336/header-bid-tag-1'); - expect(bidObject2.getStatusCode()).to.equal(2); - expect(bidPlacementCode3).to.equal('/19968336/header-bid-tag-2'); - expect(bidObject3.getStatusCode()).to.equal(2); - expect(bidPlacementCode4).to.equal('/19968336/header-bid-tag-3'); - expect(bidObject4.getStatusCode()).to.equal(2); - expect(bidPlacementCode5).to.equal('/19968336/header-bid-tag-4'); - expect(bidObject5.getStatusCode()).to.equal(2); - expect(bidPlacementCode6).to.equal('/19968336/header-bid-tag-5'); - expect(bidObject6.getStatusCode()).to.equal(2); - stubAddBidResponse.restore(); + ]; + const request = spec.buildRequests(bidRequests); + const result = spec.interpretResponse({'seatbid': responses.slice(2)}, request); + expect(result.length).to.equal(0); }); }); }); From 5f60ac34c2738c4de83d438df408758b2cea9655 Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Fri, 20 Oct 2017 17:45:13 +0300 Subject: [PATCH 07/30] Make requested changes for TrustX adapter --- modules/trustxBidAdapter.js | 24 +++++++++++----------- test/spec/modules/trustxBidAdapter_spec.js | 14 +++++-------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index e7d163828c0..595a27626ee 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -15,7 +15,6 @@ const LOG_ERROR_MESS = { hasEmptySeatbidArray: 'Response has empty seatbid array', hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' }; -const bidsRequestMap = {}; export const spec = { code: BIDDER_CODE, /** @@ -25,7 +24,7 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function(bid) { - return !!(bid.params.uid && bid.adUnitCode); + return !!bid.params.uid; }, /** * Make a server request from the list of BidRequests. @@ -37,9 +36,12 @@ export const spec = { const auids = []; const bidsMap = {}; const bids = validBidRequests || []; - const bidderRequestId = bids[0] && bids[0].bidderRequestId; + let priceType = 'net'; bids.forEach(bid => { + if (bid.params.priceType === 'gross') { + priceType = 'gross'; + } if (!bidsMap[bid.params.uid]) { bidsMap[bid.params.uid] = [bid]; auids.push(bid.params.uid); @@ -48,20 +50,17 @@ export const spec = { } }); - bidsRequestMap[bidderRequestId] = bidsMap; - const payload = { u: utils.getTopWindowUrl(), - pt: window.globalPrebidTrustxPriceType === 'gross' ? 'gross' : 'net', + pt: priceType, auids: auids.join(','), - reqid: bidderRequestId, }; - // const payloadString = JSON.stringify(payload); return { method: 'GET', url: ENDPOINT_URL, data: payload, + bidsMap: bidsMap, }; }, /** @@ -73,7 +72,8 @@ export const spec = { */ interpretResponse: function(serverResponse, bidRequest) { const bidResponses = []; - const bidsMap = bidsRequestMap[bidRequest.data.reqid]; + const bidsMap = bidRequest.bidsMap; + const priceType = bidRequest.data.pt; let errorMessage; @@ -84,7 +84,7 @@ export const spec = { if (!errorMessage && serverResponse.seatbid) { serverResponse.seatbid.forEach(respItem => { - _addBidResponse(_getBidFromResponse(respItem), bidsMap, bidResponses); + _addBidResponse(_getBidFromResponse(respItem), bidsMap, priceType, bidResponses); }); } if (errorMessage) utils.logError(errorMessage); @@ -111,7 +111,7 @@ function _getBidFromResponse(respItem) { return respItem && respItem.bid && respItem.bid[0]; } -function _addBidResponse(serverBid, bidsMap, bidResponses) { +function _addBidResponse(serverBid, bidsMap, priceType, bidResponses) { if (!serverBid) return; let errorMessage; if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); @@ -128,7 +128,7 @@ function _addBidResponse(serverBid, bidsMap, bidResponses) { height: serverBid.h, creativeId: serverBid.auid, // bid.bidId, currency: 'USD', - netRevenue: window.globalPrebidTrustxPriceType !== 'gross', + netRevenue: priceType !== 'gross', ttl: TIME_TO_LIVE, ad: serverBid.adm, dealId: serverBid.dealid diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index 16760d2e3ef..803df1615cb 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -82,7 +82,6 @@ describe('TrustXAdapter', function () { expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43'); - expect(payload).to.have.property('reqid', '22edbae2733bf6'); }); it('auids must not be duplicated', () => { @@ -92,31 +91,28 @@ describe('TrustXAdapter', function () { expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43,45'); - expect(payload).to.have.property('reqid', '22edbae2733bf6'); }); - it('pt parameter must be "gross" if window.globalPrebidTrustxPriceType === "gross"', () => { - window.globalPrebidTrustxPriceType = 'gross'; + it('pt parameter must be "gross" if params.priceType === "gross"', () => { + bidRequests[1].params.priceType = 'gross'; const request = spec.buildRequests(bidRequests); const payload = request.data; expect(payload).to.be.an('object'); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'gross'); expect(payload).to.have.property('auids', '43,45'); - expect(payload).to.have.property('reqid', '22edbae2733bf6'); - delete window.globalPrebidTrustxPriceType; + delete bidRequests[1].params.priceType; }); it('pt parameter must be "net" or "gross"', () => { - window.globalPrebidTrustxPriceType = 'some'; + bidRequests[1].params.priceType = 'some'; const request = spec.buildRequests(bidRequests); const payload = request.data; expect(payload).to.be.an('object'); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43,45'); - expect(payload).to.have.property('reqid', '22edbae2733bf6'); - delete window.globalPrebidTrustxPriceType; + delete bidRequests[1].params.priceType; }); }); From 2628673e1c4702572ae4136d5e57278205a7112f Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Tue, 24 Oct 2017 19:37:17 +0300 Subject: [PATCH 08/30] Updated markdown file for TrustX adapter --- modules/trustxBidAdapter.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/trustxBidAdapter.md b/modules/trustxBidAdapter.md index 7952b8845cf..ca407b0c5e8 100755 --- a/modules/trustxBidAdapter.md +++ b/modules/trustxBidAdapter.md @@ -10,7 +10,6 @@ Module that connects to TrustX demand source to fetch bids. # Test Parameters ``` - window.globalPrebidTrustxPriceType = 'gross'; // by default is 'net' var adUnits = [ { code: 'test-div', @@ -19,7 +18,8 @@ Module that connects to TrustX demand source to fetch bids. { bidder: "trustx", params: { - uid: '44' + uid: '44', + priceType: 'gross' // by default is 'net' } } ] @@ -30,7 +30,8 @@ Module that connects to TrustX demand source to fetch bids. { bidder: "trustx", params: { - uid: 45 + uid: 45, + priceType: 'gross' } } ] From 292b4ddcc864770d953f41ce314913aac5b8574f Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Thu, 26 Oct 2017 16:09:32 +0300 Subject: [PATCH 09/30] Fix TrustX adapter and spec file --- modules/trustxBidAdapter.js | 1 + test/spec/modules/trustxBidAdapter_spec.js | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 595a27626ee..f16b8b96ec8 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -71,6 +71,7 @@ export const spec = { * @return {Bid[]} An array of bids which were nested inside the server. */ interpretResponse: function(serverResponse, bidRequest) { + serverResponse = serverResponse && serverResponse.body const bidResponses = []; const bidsMap = bidRequest.bidsMap; const priceType = bidRequest.data.pt; diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index 803df1615cb..918e03674a9 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -158,7 +158,7 @@ describe('TrustXAdapter', function () { } ]; - const result = spec.interpretResponse({'seatbid': [responses[0]]}, request); + const result = spec.interpretResponse({'body': {'seatbid': [responses[0]]}}, request); expect(result).to.deep.equal(expectedResponse); }); @@ -241,7 +241,7 @@ describe('TrustXAdapter', function () { } ]; - const result = spec.interpretResponse({'seatbid': [responses[0], responses[1]]}, request); + const result = spec.interpretResponse({'body': {'seatbid': [responses[0], responses[1]]}}, request); expect(result).to.deep.equal(expectedResponse); }); @@ -282,7 +282,7 @@ describe('TrustXAdapter', function () { } ]; const request = spec.buildRequests(bidRequests); - const result = spec.interpretResponse({'seatbid': responses.slice(2)}, request); + const result = spec.interpretResponse({'body': {'seatbid': responses.slice(2)}}, request); expect(result.length).to.equal(0); }); }); From 09b581afcc0fc818c50302485ca03f09fbc5ced2 Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Tue, 19 Dec 2017 23:29:53 +0300 Subject: [PATCH 10/30] Update TrustX adapter: r parameter was added to ad request as cache buster --- modules/trustxBidAdapter.js | 3 +++ test/spec/modules/trustxBidAdapter_spec.js | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index f16b8b96ec8..ec1f0247455 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -37,11 +37,13 @@ export const spec = { const bidsMap = {}; const bids = validBidRequests || []; let priceType = 'net'; + let reqId; bids.forEach(bid => { if (bid.params.priceType === 'gross') { priceType = 'gross'; } + reqId = bid.bidderRequestId; if (!bidsMap[bid.params.uid]) { bidsMap[bid.params.uid] = [bid]; auids.push(bid.params.uid); @@ -54,6 +56,7 @@ export const spec = { u: utils.getTopWindowUrl(), pt: priceType, auids: auids.join(','), + r: reqId }; return { diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index 918e03674a9..6149545d59f 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -82,6 +82,7 @@ describe('TrustXAdapter', function () { expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43'); + expect(payload).to.have.property('r', '22edbae2733bf6'); }); it('auids must not be duplicated', () => { @@ -91,6 +92,7 @@ describe('TrustXAdapter', function () { expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43,45'); + expect(payload).to.have.property('r', '22edbae2733bf6'); }); it('pt parameter must be "gross" if params.priceType === "gross"', () => { @@ -101,6 +103,7 @@ describe('TrustXAdapter', function () { expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'gross'); expect(payload).to.have.property('auids', '43,45'); + expect(payload).to.have.property('r', '22edbae2733bf6'); delete bidRequests[1].params.priceType; }); @@ -112,6 +115,7 @@ describe('TrustXAdapter', function () { expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43,45'); + expect(payload).to.have.property('r', '22edbae2733bf6'); delete bidRequests[1].params.priceType; }); }); From 73b724943b6bce613ef268aa86f157883b9e9b9b Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Thu, 17 May 2018 22:53:13 +0300 Subject: [PATCH 11/30] Add support of gdpr to Trustx Bid Adapter --- modules/trustxBidAdapter.js | 12 ++++++++++- test/spec/modules/trustxBidAdapter_spec.js | 24 ++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index ec1f0247455..3688aa3b976 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -30,9 +30,10 @@ export const spec = { * Make a server request from the list of BidRequests. * * @param {BidRequest[]} validBidRequests - an array of bids + * @param {bidderRequest} - bidder request object * @return ServerRequest Info describing the request to the server. */ - buildRequests: function(validBidRequests) { + buildRequests: function(validBidRequests, bidderRequest) { const auids = []; const bidsMap = {}; const bids = validBidRequests || []; @@ -59,6 +60,15 @@ export const spec = { r: reqId }; + if (bidderRequest && bidderRequest.gdprConsent) { + if (bidderRequest.gdprConsent.consentString) { + payload.gdpr_consent = bidderRequest.gdprConsent.consentString; + } + payload.gdpr_applies = + (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') + ? Number(bidderRequest.gdprConsent.gdprApplies) : 1; + } + return { method: 'GET', url: ENDPOINT_URL, diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index 6149545d59f..75405850d7a 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -118,6 +118,30 @@ describe('TrustXAdapter', function () { expect(payload).to.have.property('r', '22edbae2733bf6'); delete bidRequests[1].params.priceType; }); + + it('if gdprConsent is present payload must have gdpr params', () => { + const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: true}}); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', 1); + }); + + it('if gdprApplies is false gdpr_applies must be 0', () => { + const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: false}}); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', 0); + }); + + it('if gdprApplies is undefined gdpr_applies must be 1', () => { + const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA'}}); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', 1); + }); }); describe('interpretResponse', () => { From 4974e55da6732500a8e37a9ad88d79b2cf6c08a8 Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Mon, 6 Aug 2018 14:51:27 +0300 Subject: [PATCH 12/30] Add wtimeout to ad request params for TrustX Bid Adapter --- modules/trustxBidAdapter.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 3688aa3b976..5a55c3d8cf3 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -60,13 +60,18 @@ export const spec = { r: reqId }; - if (bidderRequest && bidderRequest.gdprConsent) { - if (bidderRequest.gdprConsent.consentString) { - payload.gdpr_consent = bidderRequest.gdprConsent.consentString; + if (bidderRequest) { + if (bidderRequest.timeout) { + payload.wtimeout = bidderRequest.timeout; + } + if (bidderRequest.gdprConsent) { + if (bidderRequest.gdprConsent.consentString) { + payload.gdpr_consent = bidderRequest.gdprConsent.consentString; + } + payload.gdpr_applies = + (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') + ? Number(bidderRequest.gdprConsent.gdprApplies) : 1; } - payload.gdpr_applies = - (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') - ? Number(bidderRequest.gdprConsent.gdprApplies) : 1; } return { From 9175298551a2f5baf21c2e287571318af5249235 Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Fri, 10 Aug 2018 17:52:56 +0300 Subject: [PATCH 13/30] TrustX Bid Adapter: remove last ampersand in the ad request --- modules/trustxBidAdapter.js | 4 +-- test/spec/modules/trustxBidAdapter_spec.js | 42 +++++++++++++--------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 5a55c3d8cf3..20e000e3379 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -77,7 +77,7 @@ export const spec = { return { method: 'GET', url: ENDPOINT_URL, - data: payload, + data: utils.parseQueryStringParameters(payload).replace(/\&$/, ''), bidsMap: bidsMap, }; }, @@ -89,7 +89,7 @@ export const spec = { * @return {Bid[]} An array of bids which were nested inside the server. */ interpretResponse: function(serverResponse, bidRequest) { - serverResponse = serverResponse && serverResponse.body + serverResponse = serverResponse && serverResponse.body; const bidResponses = []; const bidsMap = bidRequest.bidsMap; const priceType = bidRequest.data.pt; diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index 75405850d7a..2e099772593 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -39,6 +39,14 @@ describe('TrustXAdapter', function () { }); describe('buildRequests', () => { + function parseRequest(url) { + const res = {}; + url.split('&').forEach((it) => { + const couple = it.split('='); + res[couple[0]] = decodeURIComponent(couple[1]); + }); + return res; + } let bidRequests = [ { 'bidder': 'trustx', @@ -77,8 +85,8 @@ describe('TrustXAdapter', function () { it('should attach valid params to the tag', () => { const request = spec.buildRequests([bidRequests[0]]); - const payload = request.data; - expect(payload).to.be.an('object'); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43'); @@ -87,8 +95,8 @@ describe('TrustXAdapter', function () { it('auids must not be duplicated', () => { const request = spec.buildRequests(bidRequests); - const payload = request.data; - expect(payload).to.be.an('object'); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43,45'); @@ -98,8 +106,8 @@ describe('TrustXAdapter', function () { it('pt parameter must be "gross" if params.priceType === "gross"', () => { bidRequests[1].params.priceType = 'gross'; const request = spec.buildRequests(bidRequests); - const payload = request.data; - expect(payload).to.be.an('object'); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'gross'); expect(payload).to.have.property('auids', '43,45'); @@ -110,8 +118,8 @@ describe('TrustXAdapter', function () { it('pt parameter must be "net" or "gross"', () => { bidRequests[1].params.priceType = 'some'; const request = spec.buildRequests(bidRequests); - const payload = request.data; - expect(payload).to.be.an('object'); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43,45'); @@ -121,26 +129,26 @@ describe('TrustXAdapter', function () { it('if gdprConsent is present payload must have gdpr params', () => { const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: true}}); - const payload = request.data; - expect(payload).to.be.an('object'); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', 1); + expect(payload).to.have.property('gdpr_applies', '1'); }); it('if gdprApplies is false gdpr_applies must be 0', () => { const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: false}}); - const payload = request.data; - expect(payload).to.be.an('object'); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', 0); + expect(payload).to.have.property('gdpr_applies', '0'); }); it('if gdprApplies is undefined gdpr_applies must be 1', () => { const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA'}}); - const payload = request.data; - expect(payload).to.be.an('object'); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', 1); + expect(payload).to.have.property('gdpr_applies', '1'); }); }); From 83b3aaf8c2183cb740872f39711424a93c0c3c11 Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Fri, 15 Feb 2019 14:25:11 +0300 Subject: [PATCH 14/30] Update TrustX Bid Adapter to support identical uids in parameters --- modules/trustxBidAdapter.js | 100 ++++++-- test/spec/modules/trustxBidAdapter_spec.js | 253 +++++++++++++++++++-- 2 files changed, 314 insertions(+), 39 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index b61a870c2a4..6a9ac3e6b4c 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -36,6 +36,8 @@ export const spec = { buildRequests: function(validBidRequests, bidderRequest) { const auids = []; const bidsMap = {}; + const slotsMapByUid = {}; + const sizeMap = {}; const bids = validBidRequests || []; let priceType = 'net'; let reqId; @@ -45,18 +47,41 @@ export const spec = { priceType = 'gross'; } reqId = bid.bidderRequestId; - if (!bidsMap[bid.params.uid]) { - bidsMap[bid.params.uid] = [bid]; - auids.push(bid.params.uid); + const {params: {uid}, adUnitCode} = bid; + auids.push(uid); + const sizesId = utils.parseSizesInput(bid.sizes); + + if (!slotsMapByUid[uid]) { + slotsMapByUid[uid] = {}; + } + const slotsMap = slotsMapByUid[uid]; + if (!slotsMap[adUnitCode]) { + slotsMap[adUnitCode] = {adUnitCode, bids: [bid], parents: []}; } else { - bidsMap[bid.params.uid].push(bid); + slotsMap[adUnitCode].bids.push(bid); } + const slot = slotsMap[adUnitCode]; + + sizesId.forEach((sizeId) => { + sizeMap[sizeId] = true; + if (!bidsMap[uid]) { + bidsMap[uid] = {}; + } + + if (!bidsMap[uid][sizeId]) { + bidsMap[uid][sizeId] = [slot]; + } else { + bidsMap[uid][sizeId].push(slot); + } + slot.parents.push({parent: bidsMap[uid], key: sizeId, uid}); + }); }); const payload = { u: utils.getTopWindowUrl(), pt: priceType, auids: auids.join(','), + sizes: utils.getKeys(sizeMap).join(','), r: reqId }; @@ -91,6 +116,7 @@ export const spec = { interpretResponse: function(serverResponse, bidRequest) { serverResponse = serverResponse && serverResponse.body; const bidResponses = []; + const remainingServerBids = []; const bidsMap = bidRequest.bidsMap; const priceType = bidRequest.data.pt; @@ -103,7 +129,12 @@ export const spec = { if (!errorMessage && serverResponse.seatbid) { serverResponse.seatbid.forEach(respItem => { - _addBidResponse(_getBidFromResponse(respItem), bidsMap, priceType, bidResponses); + _addBidResponse(_getBidFromResponse(respItem), bidsMap, priceType, bidResponses, remainingServerBids); + }); + } + if (remainingServerBids.length) { + remainingServerBids.forEach(serverBid => { + _addBidResponse(serverBid, bidsMap, priceType, bidResponses); }); } if (errorMessage) utils.logError(errorMessage); @@ -130,30 +161,57 @@ function _getBidFromResponse(respItem) { return respItem && respItem.bid && respItem.bid[0]; } -function _addBidResponse(serverBid, bidsMap, priceType, bidResponses) { +function _addBidResponse(serverBid, bidsMap, priceType, bidResponses, remainingServerBids) { if (!serverBid) return; let errorMessage; if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); if (!serverBid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); else { + const ignoreSizes = !remainingServerBids; const awaitingBids = bidsMap[serverBid.auid]; if (awaitingBids) { - awaitingBids.forEach(bid => { - const bidResponse = { - requestId: bid.bidId, // bid.bidderRequestId, - bidderCode: spec.code, - cpm: serverBid.price, - width: serverBid.w, - height: serverBid.h, - creativeId: serverBid.auid, // bid.bidId, - currency: 'USD', - netRevenue: priceType !== 'gross', - ttl: TIME_TO_LIVE, - ad: serverBid.adm, - dealId: serverBid.dealid - }; - bidResponses.push(bidResponse); + let sizeId = `${serverBid.w}x${serverBid.h}`; + + if (!awaitingBids[sizeId]) { + if (ignoreSizes) { + sizeId = utils.getKeys(awaitingBids)[0]; + } else { + remainingServerBids.push(serverBid); + return; + } + } + + const slot = awaitingBids[sizeId][0]; + + const bid = slot.bids.shift(); + bidResponses.push({ + requestId: bid.bidId, // bid.bidderRequestId, + bidderCode: spec.code, + cpm: serverBid.price, + width: serverBid.w, + height: serverBid.h, + creativeId: serverBid.auid, // bid.bidId, + currency: 'USD', + netRevenue: priceType !== 'gross', + ttl: TIME_TO_LIVE, + ad: serverBid.adm, + dealId: serverBid.dealid }); + + if (!slot.bids.length) { + slot.parents.forEach(({parent, key, uid}) => { + const index = parent[key].indexOf(slot); + if (index > -1) { + parent[key].splice(index, 1); + } + if (!parent[key].length) { + delete parent[key]; + if (!utils.getKeys(parent).length) { + delete bidsMap[uid]; + } + } + }); + } } else { errorMessage = LOG_ERROR_MESS.noPlacementCode + serverBid.auid; } diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index 9f2fdca6a99..355fbbfa4bb 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -65,7 +65,7 @@ describe('TrustXAdapter', function () { 'uid': '43' }, 'adUnitCode': 'adunit-code-2', - 'sizes': [[728, 90]], + 'sizes': [[728, 90], [300, 250]], 'bidId': '3150ccb55da321', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', @@ -90,16 +90,18 @@ describe('TrustXAdapter', function () { expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43'); + expect(payload).to.have.property('sizes', '300x250,300x600'); expect(payload).to.have.property('r', '22edbae2733bf6'); }); - it('auids must not be duplicated', function () { + it('sizes must not be duplicated', function () { const request = spec.buildRequests(bidRequests); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '43,45'); + expect(payload).to.have.property('auids', '43,43,45'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); expect(payload).to.have.property('r', '22edbae2733bf6'); }); @@ -110,7 +112,8 @@ describe('TrustXAdapter', function () { const payload = parseRequest(request.data); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'gross'); - expect(payload).to.have.property('auids', '43,45'); + expect(payload).to.have.property('auids', '43,43,45'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); expect(payload).to.have.property('r', '22edbae2733bf6'); delete bidRequests[1].params.priceType; }); @@ -122,7 +125,8 @@ describe('TrustXAdapter', function () { const payload = parseRequest(request.data); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '43,45'); + expect(payload).to.have.property('auids', '43,43,45'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); expect(payload).to.have.property('r', '22edbae2733bf6'); delete bidRequests[1].params.priceType; }); @@ -155,9 +159,10 @@ describe('TrustXAdapter', function () { describe('interpretResponse', function () { const responses = [ {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 43, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 44, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 44, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 43, 'h': 90, 'w': 728}], 'seat': '1'}, {'bid': [{'price': 0, 'auid': 45, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0, 'adm': '
test content 4
', 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0, 'adm': '
test content 5
', 'h': 250, 'w': 300}], 'seat': '1'}, undefined, {'bid': [], 'seat': '1'}, {'seat': '1'}, @@ -250,26 +255,26 @@ describe('TrustXAdapter', function () { 'ttl': 360, }, { - 'requestId': '5703af74d0472a', - 'cpm': 1.15, - 'creativeId': 43, + 'requestId': '4dff80cc4ee346', + 'cpm': 0.5, + 'creativeId': 44, 'dealId': undefined, 'width': 300, - 'height': 250, - 'ad': '
test content 1
', + 'height': 600, + 'ad': '
test content 2
', 'bidderCode': 'trustx', 'currency': 'USD', 'netRevenue': true, 'ttl': 360, }, { - 'requestId': '4dff80cc4ee346', - 'cpm': 0.5, - 'creativeId': 44, + 'requestId': '5703af74d0472a', + 'cpm': 0.15, + 'creativeId': 43, 'dealId': undefined, 'width': 728, 'height': 90, - 'ad': '
test content 2
', + 'ad': '
test content 3
', 'bidderCode': 'trustx', 'currency': 'USD', 'netRevenue': true, @@ -277,7 +282,7 @@ describe('TrustXAdapter', function () { } ]; - const result = spec.interpretResponse({'body': {'seatbid': [responses[0], responses[1]]}}, request); + const result = spec.interpretResponse({'body': {'seatbid': responses.slice(0, 3)}}, request); expect(result).to.deep.equal(expectedResponse); }); @@ -318,8 +323,220 @@ describe('TrustXAdapter', function () { } ]; const request = spec.buildRequests(bidRequests); - const result = spec.interpretResponse({'body': {'seatbid': responses.slice(2)}}, request); + const result = spec.interpretResponse({'body': {'seatbid': responses.slice(3)}}, request); expect(result.length).to.equal(0); }); + + it('complicated case', function () { + const fullResponse = [ + {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 43, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 44, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 43, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
test content 4
', 'auid': 43, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
test content 5
', 'auid': 44, 'h': 600, 'w': 350}], 'seat': '1'}, + ]; + const bidRequests = [ + { + 'bidder': 'trustx', + 'params': { + 'uid': '43' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '2164be6358b9', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '43' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '326bde7fbf69', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '44' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '4e111f1b66e4', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '43' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '26d6f897b516', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '44' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '1751cd90161', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '2164be6358b9', + 'cpm': 1.15, + 'creativeId': 43, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
test content 1
', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '4e111f1b66e4', + 'cpm': 0.5, + 'creativeId': 44, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'ad': '
test content 2
', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '26d6f897b516', + 'cpm': 0.15, + 'creativeId': 43, + 'dealId': undefined, + 'width': 728, + 'height': 90, + 'ad': '
test content 3
', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '326bde7fbf69', + 'cpm': 0.15, + 'creativeId': 43, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'ad': '
test content 4
', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '1751cd90161', + 'cpm': 0.5, + 'creativeId': 44, + 'dealId': undefined, + 'width': 350, + 'height': 600, + 'ad': '
test content 5
', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('dublicate uids and sizes in one slot', function () { + const fullResponse = [ + {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 43, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 43, 'h': 250, 'w': 300}], 'seat': '1'}, + ]; + const bidRequests = [ + { + 'bidder': 'trustx', + 'params': { + 'uid': '43' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '5126e301f4be', + 'bidderRequestId': '171c5405a390', + 'auctionId': '35bcbc0f7e79c', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '43' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '57b2ebe70e16', + 'bidderRequestId': '171c5405a390', + 'auctionId': '35bcbc0f7e79c', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '43' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '225fcd44b18c', + 'bidderRequestId': '171c5405a390', + 'auctionId': '35bcbc0f7e79c', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '5126e301f4be', + 'cpm': 1.15, + 'creativeId': 43, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
test content 1
', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '57b2ebe70e16', + 'cpm': 0.5, + 'creativeId': 43, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
test content 2
', + 'bidderCode': 'trustx', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); + expect(result).to.deep.equal(expectedResponse); + }); }); }); From f77c2ee787dfa3327b2398a785f5e0beaa8b491d Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Wed, 20 Feb 2019 14:50:56 +0300 Subject: [PATCH 15/30] Update TrustX Bid Adapter to ignore bids that sizes do not match the size of the request --- modules/trustxBidAdapter.js | 81 +++++++++------------- test/spec/modules/trustxBidAdapter_spec.js | 13 ---- 2 files changed, 33 insertions(+), 61 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 6a9ac3e6b4c..bd5f63e5302 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -116,7 +116,6 @@ export const spec = { interpretResponse: function(serverResponse, bidRequest) { serverResponse = serverResponse && serverResponse.body; const bidResponses = []; - const remainingServerBids = []; const bidsMap = bidRequest.bidsMap; const priceType = bidRequest.data.pt; @@ -129,12 +128,7 @@ export const spec = { if (!errorMessage && serverResponse.seatbid) { serverResponse.seatbid.forEach(respItem => { - _addBidResponse(_getBidFromResponse(respItem), bidsMap, priceType, bidResponses, remainingServerBids); - }); - } - if (remainingServerBids.length) { - remainingServerBids.forEach(serverBid => { - _addBidResponse(serverBid, bidsMap, priceType, bidResponses); + _addBidResponse(_getBidFromResponse(respItem), bidsMap, priceType, bidResponses); }); } if (errorMessage) utils.logError(errorMessage); @@ -161,56 +155,47 @@ function _getBidFromResponse(respItem) { return respItem && respItem.bid && respItem.bid[0]; } -function _addBidResponse(serverBid, bidsMap, priceType, bidResponses, remainingServerBids) { +function _addBidResponse(serverBid, bidsMap, priceType, bidResponses) { if (!serverBid) return; let errorMessage; if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); if (!serverBid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); else { - const ignoreSizes = !remainingServerBids; const awaitingBids = bidsMap[serverBid.auid]; if (awaitingBids) { - let sizeId = `${serverBid.w}x${serverBid.h}`; - - if (!awaitingBids[sizeId]) { - if (ignoreSizes) { - sizeId = utils.getKeys(awaitingBids)[0]; - } else { - remainingServerBids.push(serverBid); - return; - } - } - - const slot = awaitingBids[sizeId][0]; - - const bid = slot.bids.shift(); - bidResponses.push({ - requestId: bid.bidId, // bid.bidderRequestId, - bidderCode: spec.code, - cpm: serverBid.price, - width: serverBid.w, - height: serverBid.h, - creativeId: serverBid.auid, // bid.bidId, - currency: 'USD', - netRevenue: priceType !== 'gross', - ttl: TIME_TO_LIVE, - ad: serverBid.adm, - dealId: serverBid.dealid - }); + const sizeId = `${serverBid.w}x${serverBid.h}`; + if (awaitingBids[sizeId]) { + const slot = awaitingBids[sizeId][0]; + + const bid = slot.bids.shift(); + bidResponses.push({ + requestId: bid.bidId, // bid.bidderRequestId, + bidderCode: spec.code, + cpm: serverBid.price, + width: serverBid.w, + height: serverBid.h, + creativeId: serverBid.auid, // bid.bidId, + currency: 'USD', + netRevenue: priceType !== 'gross', + ttl: TIME_TO_LIVE, + ad: serverBid.adm, + dealId: serverBid.dealid + }); - if (!slot.bids.length) { - slot.parents.forEach(({parent, key, uid}) => { - const index = parent[key].indexOf(slot); - if (index > -1) { - parent[key].splice(index, 1); - } - if (!parent[key].length) { - delete parent[key]; - if (!utils.getKeys(parent).length) { - delete bidsMap[uid]; + if (!slot.bids.length) { + slot.parents.forEach(({parent, key, uid}) => { + const index = parent[key].indexOf(slot); + if (index > -1) { + parent[key].splice(index, 1); } - } - }); + if (!parent[key].length) { + delete parent[key]; + if (!utils.getKeys(parent).length) { + delete bidsMap[uid]; + } + } + }); + } } } else { errorMessage = LOG_ERROR_MESS.noPlacementCode + serverBid.auid; diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index 355fbbfa4bb..207d3a068ba 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -445,19 +445,6 @@ describe('TrustXAdapter', function () { 'currency': 'USD', 'netRevenue': true, 'ttl': 360, - }, - { - 'requestId': '1751cd90161', - 'cpm': 0.5, - 'creativeId': 44, - 'dealId': undefined, - 'width': 350, - 'height': 600, - 'ad': '
test content 5
', - 'bidderCode': 'trustx', - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 360, } ]; From 549ea7a0b37f72e225d6f07459fb085f8e88ebdd Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Wed, 13 Mar 2019 18:43:42 +0300 Subject: [PATCH 16/30] Update TrustX Bid Adapter to support instream and outstream video --- modules/trustxBidAdapter.js | 60 +++++- modules/trustxBidAdapter.md | 13 ++ test/spec/modules/trustxBidAdapter_spec.js | 207 +++++++++++++++++++++ 3 files changed, 274 insertions(+), 6 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index bd5f63e5302..b0593e5c7d7 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -1,9 +1,14 @@ import * as utils from '../src/utils'; import {registerBidder} from '../src/adapters/bidderFactory'; +import { Renderer } from '../src/Renderer'; +import { VIDEO, BANNER } from '../src/mediaTypes'; + const BIDDER_CODE = 'trustx'; const ENDPOINT_URL = '//sofia.trustx.org/hb'; const TIME_TO_LIVE = 360; const ADAPTER_SYNC_URL = '//sofia.trustx.org/push_sync'; +const RENDERER_URL = '//cdn.adnxs.com/renderer/video/ANOutstreamVideo.js'; + const LOG_ERROR_MESS = { noAuid: 'Bid from response has no auid parameter - ', noAdm: 'Bid from response has no adm parameter - ', @@ -17,6 +22,7 @@ const LOG_ERROR_MESS = { }; export const spec = { code: BIDDER_CODE, + supportedMediaTypes: [ BANNER, VIDEO ], /** * Determines whether or not the given bid request is valid. * @@ -113,7 +119,7 @@ export const spec = { * @param {*} bidRequest * @return {Bid[]} An array of bids which were nested inside the server. */ - interpretResponse: function(serverResponse, bidRequest) { + interpretResponse: function(serverResponse, bidRequest, RendererConst = Renderer) { serverResponse = serverResponse && serverResponse.body; const bidResponses = []; const bidsMap = bidRequest.bidsMap; @@ -128,7 +134,7 @@ export const spec = { if (!errorMessage && serverResponse.seatbid) { serverResponse.seatbid.forEach(respItem => { - _addBidResponse(_getBidFromResponse(respItem), bidsMap, priceType, bidResponses); + _addBidResponse(_getBidFromResponse(respItem), bidsMap, priceType, bidResponses, RendererConst); }); } if (errorMessage) utils.logError(errorMessage); @@ -155,7 +161,7 @@ function _getBidFromResponse(respItem) { return respItem && respItem.bid && respItem.bid[0]; } -function _addBidResponse(serverBid, bidsMap, priceType, bidResponses) { +function _addBidResponse(serverBid, bidsMap, priceType, bidResponses, RendererConst) { if (!serverBid) return; let errorMessage; if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); @@ -168,7 +174,7 @@ function _addBidResponse(serverBid, bidsMap, priceType, bidResponses) { const slot = awaitingBids[sizeId][0]; const bid = slot.bids.shift(); - bidResponses.push({ + const bidResponse = { requestId: bid.bidId, // bid.bidderRequestId, bidderCode: spec.code, cpm: serverBid.price, @@ -178,9 +184,26 @@ function _addBidResponse(serverBid, bidsMap, priceType, bidResponses) { currency: 'USD', netRevenue: priceType !== 'gross', ttl: TIME_TO_LIVE, - ad: serverBid.adm, dealId: serverBid.dealid - }); + }; + if (serverBid.content_type === 'video') { + bidResponse.vastXml = serverBid.adm; + bidResponse.mediaType = VIDEO; + bidResponse.adResponse = { + content: bidResponse.vastXml + }; + if (!bid.renderer && (!bid.mediaTypes || !bid.mediaTypes.video || bid.mediaTypes.video.context === 'outstream')) { + bidResponse.renderer = createRenderer(bidResponse, { + id: bid.bidId, + url: RENDERER_URL + }, RendererConst); + } + } else { + bidResponse.ad = serverBid.adm; + bidResponse.mediaType = BANNER; + } + + bidResponses.push(bidResponse); if (!slot.bids.length) { slot.parents.forEach(({parent, key, uid}) => { @@ -206,4 +229,29 @@ function _addBidResponse(serverBid, bidsMap, priceType, bidResponses) { } } +function outstreamRender (bid) { + bid.renderer.push(() => { + window.ANOutstreamVideo.renderAd({ + targetId: bid.adUnitCode, + adResponse: bid.adResponse + }); + }); +} + +function createRenderer (bid, rendererParams, RendererConst) { + const rendererInst = RendererConst.install({ + id: rendererParams.id, + url: rendererParams.url, + loaded: false + }); + + try { + rendererInst.setRender(outstreamRender); + } catch (err) { + utils.logWarn('Prebid Error calling setRender on renderer', err); + } + + return rendererInst; +} + registerBidder(spec); diff --git a/modules/trustxBidAdapter.md b/modules/trustxBidAdapter.md index ca407b0c5e8..d6b660c6248 100755 --- a/modules/trustxBidAdapter.md +++ b/modules/trustxBidAdapter.md @@ -7,6 +7,7 @@ Maintainer: paul@trustx.org # Description Module that connects to TrustX demand source to fetch bids. +TrustX Bid Adapter supports Banner and Video (instream and outstream). # Test Parameters ``` @@ -35,6 +36,18 @@ Module that connects to TrustX demand source to fetch bids. } } ] + },{ + code: 'test-div', + sizes: [[640, 360]], + mediaTypes: { video: {} }, + bids: [ + { + bidder: "trustx", + params: { + uid: 7697 + } + } + ] } ]; ``` \ No newline at end of file diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index 207d3a068ba..de0d1c8a530 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -194,6 +194,7 @@ describe('TrustXAdapter', function () { 'ad': '
test content 1
', 'bidderCode': 'trustx', 'currency': 'USD', + 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, } @@ -251,6 +252,7 @@ describe('TrustXAdapter', function () { 'ad': '
test content 1
', 'bidderCode': 'trustx', 'currency': 'USD', + 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, }, @@ -264,6 +266,7 @@ describe('TrustXAdapter', function () { 'ad': '
test content 2
', 'bidderCode': 'trustx', 'currency': 'USD', + 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, }, @@ -277,6 +280,7 @@ describe('TrustXAdapter', function () { 'ad': '
test content 3
', 'bidderCode': 'trustx', 'currency': 'USD', + 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, } @@ -404,6 +408,7 @@ describe('TrustXAdapter', function () { 'ad': '
test content 1
', 'bidderCode': 'trustx', 'currency': 'USD', + 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, }, @@ -417,6 +422,7 @@ describe('TrustXAdapter', function () { 'ad': '
test content 2
', 'bidderCode': 'trustx', 'currency': 'USD', + 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, }, @@ -430,6 +436,7 @@ describe('TrustXAdapter', function () { 'ad': '
test content 3
', 'bidderCode': 'trustx', 'currency': 'USD', + 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, }, @@ -443,6 +450,7 @@ describe('TrustXAdapter', function () { 'ad': '
test content 4
', 'bidderCode': 'trustx', 'currency': 'USD', + 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, } @@ -504,6 +512,7 @@ describe('TrustXAdapter', function () { 'ad': '
test content 1
', 'bidderCode': 'trustx', 'currency': 'USD', + 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, }, @@ -517,6 +526,7 @@ describe('TrustXAdapter', function () { 'ad': '
test content 2
', 'bidderCode': 'trustx', 'currency': 'USD', + 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, } @@ -526,4 +536,201 @@ describe('TrustXAdapter', function () { expect(result).to.deep.equal(expectedResponse); }); }); + + it('should get correct video bid response', function () { + const bidRequests = [ + { + 'bidder': 'trustx', + 'params': { + 'uid': '50' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '57dfefb80eca', + 'bidderRequestId': '20394420a762a2', + 'auctionId': '140132d07b031', + 'mediaTypes': { + 'video': { + 'context': 'instream' + } + } + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '51' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'e893c787c22dd', + 'bidderRequestId': '20394420a762a2', + 'auctionId': '140132d07b031', + 'mediaTypes': { + 'video': { + 'context': 'instream' + } + } + } + ]; + const response = [ + {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 50, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, + {'bid': [{'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 51, content_type: 'video'}], 'seat': '2'} + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '57dfefb80eca', + 'cpm': 1.15, + 'creativeId': 50, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'bidderCode': 'trustx', + 'currency': 'USD', + 'mediaType': 'video', + 'netRevenue': true, + 'ttl': 360, + 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'adResponse': { + 'content': '\n<\/Ad>\n<\/VAST>' + } + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': response}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('should have right renderer in the bid response', function () { + const spySetRenderer = sinon.spy(); + const stubRenderer = { + setRender: spySetRenderer + }; + const spyRendererInstall = sinon.spy(function() { return stubRenderer; }); + const stubRendererConst = { + install: spyRendererInstall + }; + const bidRequests = [ + { + 'bidder': 'trustx', + 'params': { + 'uid': '50' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'e6e65553fc8', + 'bidderRequestId': '1380f393215dc7', + 'auctionId': '10b8d2f3c697e3', + 'mediaTypes': { + 'video': { + 'context': 'outstream' + } + } + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '51' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'c8fdcb3f269f', + 'bidderRequestId': '1380f393215dc7', + 'auctionId': '10b8d2f3c697e3' + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '52' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '1de036c37685', + 'bidderRequestId': '1380f393215dc7', + 'auctionId': '10b8d2f3c697e3', + 'renderer': {} + } + ]; + const response = [ + {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 50, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, + {'bid': [{'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 51, content_type: 'video', w: 300, h: 250}], 'seat': '2'}, + {'bid': [{'price': 1.20, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 52, content_type: 'video', w: 300, h: 250}], 'seat': '2'} + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': 'e6e65553fc8', + 'cpm': 1.15, + 'creativeId': 50, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'bidderCode': 'trustx', + 'currency': 'USD', + 'mediaType': 'video', + 'netRevenue': true, + 'ttl': 360, + 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'adResponse': { + 'content': '\n<\/Ad>\n<\/VAST>' + }, + 'renderer': stubRenderer + }, + { + 'requestId': 'c8fdcb3f269f', + 'cpm': 1.00, + 'creativeId': 51, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'bidderCode': 'trustx', + 'currency': 'USD', + 'mediaType': 'video', + 'netRevenue': true, + 'ttl': 360, + 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'adResponse': { + 'content': '\n<\/Ad>\n<\/VAST>' + }, + 'renderer': stubRenderer + }, + { + 'requestId': '1de036c37685', + 'cpm': 1.20, + 'creativeId': 52, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'bidderCode': 'trustx', + 'currency': 'USD', + 'mediaType': 'video', + 'netRevenue': true, + 'ttl': 360, + 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'adResponse': { + 'content': '\n<\/Ad>\n<\/VAST>' + } + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': response}}, request, stubRendererConst); + + expect(spySetRenderer.calledTwice).to.equal(true); + expect(spySetRenderer.getCall(0).args[0]).to.be.a('function'); + expect(spySetRenderer.getCall(1).args[0]).to.be.a('function'); + + expect(spyRendererInstall.calledTwice).to.equal(true); + expect(spyRendererInstall.getCall(0).args[0]).to.deep.equal({ + id: 'e6e65553fc8', + url: '//cdn.adnxs.com/renderer/video/ANOutstreamVideo.js', + loaded: false + }); + expect(spyRendererInstall.getCall(1).args[0]).to.deep.equal({ + id: 'c8fdcb3f269f', + url: '//cdn.adnxs.com/renderer/video/ANOutstreamVideo.js', + loaded: false + }); + + expect(result).to.deep.equal(expectedResponse); + }); }); From 0bce98535e206a2cebfa45a3e92b1a7ef64b83c2 Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Mon, 15 Apr 2019 18:55:10 +0300 Subject: [PATCH 17/30] Added wrapperType and wrapperVersion parameters in ad request for TrustX Bid Adapter --- modules/trustxBidAdapter.js | 4 +++- test/spec/modules/trustxBidAdapter_spec.js | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index b0593e5c7d7..1a501faf74b 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -88,7 +88,9 @@ export const spec = { pt: priceType, auids: auids.join(','), sizes: utils.getKeys(sizeMap).join(','), - r: reqId + r: reqId, + wrapperType: 'Prebid_js', + wrapperVersion: '$prebid.version$' }; if (bidderRequest) { diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index de0d1c8a530..a15ae94cfad 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -92,6 +92,8 @@ describe('TrustXAdapter', function () { expect(payload).to.have.property('auids', '43'); expect(payload).to.have.property('sizes', '300x250,300x600'); expect(payload).to.have.property('r', '22edbae2733bf6'); + expect(payload).to.have.property('wrapperType', 'Prebid_js'); + expect(payload).to.have.property('wrapperVersion', '$prebid.version$'); }); it('sizes must not be duplicated', function () { From c604ba04cf304d3e3a6348c75d2953c82472b467 Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Fri, 5 Jul 2019 12:02:07 +0300 Subject: [PATCH 18/30] Update TrustX Bid Adapter to use refererInfo instead depricated function utils.getTopWindowUrl --- modules/trustxBidAdapter.js | 4 ++- test/spec/modules/trustxBidAdapter_spec.js | 33 ++++++++++++++-------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 1a501faf74b..e9eb175671e 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -84,7 +84,6 @@ export const spec = { }); const payload = { - u: utils.getTopWindowUrl(), pt: priceType, auids: auids.join(','), sizes: utils.getKeys(sizeMap).join(','), @@ -94,6 +93,9 @@ export const spec = { }; if (bidderRequest) { + if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { + payload.u = encodeURIComponent(bidderRequest.refererInfo.referer); + } if (bidderRequest.timeout) { payload.wtimeout = bidderRequest.timeout; } diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index a15ae94cfad..0a4fddcb852 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -47,6 +47,14 @@ describe('TrustXAdapter', function () { }); return res; } + + const bidderRequest = { + refererInfo: { + referer: 'http://example.com' + } + }; + const encodedReferer = encodeURIComponent(bidderRequest.refererInfo.referer); + let bidRequests = [ { 'bidder': 'trustx', @@ -84,10 +92,10 @@ describe('TrustXAdapter', function () { ]; it('should attach valid params to the tag', function () { - const request = spec.buildRequests([bidRequests[0]]); + const request = spec.buildRequests([bidRequests[0]], bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); - expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('u', encodedReferer); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43'); expect(payload).to.have.property('sizes', '300x250,300x600'); @@ -97,10 +105,10 @@ describe('TrustXAdapter', function () { }); it('sizes must not be duplicated', function () { - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); - expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('u', encodedReferer); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43,43,45'); expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); @@ -109,10 +117,10 @@ describe('TrustXAdapter', function () { it('pt parameter must be "gross" if params.priceType === "gross"', function () { bidRequests[1].params.priceType = 'gross'; - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); - expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('u', encodedReferer); expect(payload).to.have.property('pt', 'gross'); expect(payload).to.have.property('auids', '43,43,45'); expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); @@ -122,10 +130,10 @@ describe('TrustXAdapter', function () { it('pt parameter must be "net" or "gross"', function () { bidRequests[1].params.priceType = 'some'; - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); - expect(payload).to.have.property('u').that.is.a('string'); + expect(payload).to.have.property('u', encodedReferer); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43,43,45'); expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); @@ -134,7 +142,8 @@ describe('TrustXAdapter', function () { }); it('if gdprConsent is present payload must have gdpr params', function () { - const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: true}}); + const bidderRequestWithGDPR = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: true}}, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('gdpr_consent', 'AAA'); @@ -142,7 +151,8 @@ describe('TrustXAdapter', function () { }); it('if gdprApplies is false gdpr_applies must be 0', function () { - const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: false}}); + const bidderRequestWithGDPR = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: false}}, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('gdpr_consent', 'AAA'); @@ -150,7 +160,8 @@ describe('TrustXAdapter', function () { }); it('if gdprApplies is undefined gdpr_applies must be 1', function () { - const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA'}}); + const bidderRequestWithGDPR = Object.assign({gdprConsent: {consentString: 'AAA'}}, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('gdpr_consent', 'AAA'); From aedbb7c5b36e431fa70463d6a678cde271c5ca16 Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Tue, 30 Jul 2019 13:37:21 +0300 Subject: [PATCH 19/30] HOTFIX for referrer encodind in TrustX Bid Adapter --- modules/trustxBidAdapter.js | 2 +- test/spec/modules/trustxBidAdapter_spec.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index e9eb175671e..aecb6aba8af 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -94,7 +94,7 @@ export const spec = { if (bidderRequest) { if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { - payload.u = encodeURIComponent(bidderRequest.refererInfo.referer); + payload.u = bidderRequest.refererInfo.referer; } if (bidderRequest.timeout) { payload.wtimeout = bidderRequest.timeout; diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index 0a4fddcb852..b8cf2a2335d 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -50,10 +50,10 @@ describe('TrustXAdapter', function () { const bidderRequest = { refererInfo: { - referer: 'http://example.com' + referer: encodeURIComponent('http://example.com') } }; - const encodedReferer = encodeURIComponent(bidderRequest.refererInfo.referer); + const encodedReferer = bidderRequest.refererInfo.referer; let bidRequests = [ { From 9a6110d33e767c4fc507563f5cc109bc8a8b6b14 Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Tue, 30 Jul 2019 14:17:00 +0300 Subject: [PATCH 20/30] Fix test for TrustX Bid Adapter --- test/spec/modules/trustxBidAdapter_spec.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index b8cf2a2335d..f99831eeca1 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -50,10 +50,10 @@ describe('TrustXAdapter', function () { const bidderRequest = { refererInfo: { - referer: encodeURIComponent('http://example.com') + referer: 'http://example.com' } }; - const encodedReferer = bidderRequest.refererInfo.referer; + const referrer = bidderRequest.refererInfo.referer; let bidRequests = [ { @@ -95,7 +95,7 @@ describe('TrustXAdapter', function () { const request = spec.buildRequests([bidRequests[0]], bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); - expect(payload).to.have.property('u', encodedReferer); + expect(payload).to.have.property('u', referrer); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43'); expect(payload).to.have.property('sizes', '300x250,300x600'); @@ -108,7 +108,7 @@ describe('TrustXAdapter', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); - expect(payload).to.have.property('u', encodedReferer); + expect(payload).to.have.property('u', referrer); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43,43,45'); expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); @@ -120,7 +120,7 @@ describe('TrustXAdapter', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); - expect(payload).to.have.property('u', encodedReferer); + expect(payload).to.have.property('u', referrer); expect(payload).to.have.property('pt', 'gross'); expect(payload).to.have.property('auids', '43,43,45'); expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); @@ -133,7 +133,7 @@ describe('TrustXAdapter', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); - expect(payload).to.have.property('u', encodedReferer); + expect(payload).to.have.property('u', referrer); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '43,43,45'); expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); From 319a7deeac1c400b27ac0d6f2829d930fb561e91 Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Thu, 5 Sep 2019 10:57:55 +0300 Subject: [PATCH 21/30] TrustX Bid Adapter: added keywords passing support --- modules/trustxBidAdapter.js | 24 +++++++++++ modules/trustxBidAdapter.md | 6 ++- test/spec/modules/trustxBidAdapter_spec.js | 49 ++++++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index a1ba632a487..00c86dec0d3 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -46,6 +46,7 @@ export const spec = { const sizeMap = {}; const bids = validBidRequests || []; let priceType = 'net'; + let pageKeywords; let reqId; bids.forEach(bid => { @@ -57,6 +58,15 @@ export const spec = { auids.push(uid); const sizesId = utils.parseSizesInput(bid.sizes); + if (!pageKeywords && !utils.isEmpty(bid.params.keywords)) { + const keywords = utils.transformBidderParamKeywords(bid.params.keywords); + + if (keywords.length > 0) { + keywords.forEach(deleteValues); + } + pageKeywords = keywords; + } + if (!slotsMapByUid[uid]) { slotsMapByUid[uid] = {}; } @@ -92,6 +102,10 @@ export const spec = { wrapperVersion: '$prebid.version$' }; + if (pageKeywords) { + payload.keywords = JSON.stringify(pageKeywords); + } + if (bidderRequest) { if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { payload.u = bidderRequest.refererInfo.referer; @@ -154,6 +168,16 @@ export const spec = { } } +function isPopulatedArray(arr) { + return !!(utils.isArray(arr) && arr.length > 0); +} + +function deleteValues(keyPairObj) { + if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { + delete keyPairObj.value; + } +} + function _getBidFromResponse(respItem) { if (!respItem) { utils.logError(LOG_ERROR_MESS.emptySeatbid); diff --git a/modules/trustxBidAdapter.md b/modules/trustxBidAdapter.md index d6b660c6248..a72f1ba85aa 100644 --- a/modules/trustxBidAdapter.md +++ b/modules/trustxBidAdapter.md @@ -32,7 +32,11 @@ TrustX Bid Adapter supports Banner and Video (instream and outstream). bidder: "trustx", params: { uid: 45, - priceType: 'gross' + priceType: 'gross', + keywords: { + brandsafety: ['disaster'], + topic: ['stress', 'fear'] + } } } ] diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index d7d16348bcf..4256012ba0b 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -167,6 +167,55 @@ describe('TrustXAdapter', function () { expect(payload).to.have.property('gdpr_consent', 'AAA'); expect(payload).to.have.property('gdpr_applies', '1'); }); + + it('should convert keyword params to proper form and attaches to request', function () { + const bidRequestWithKeywords = [].concat(bidRequests); + bidRequestWithKeywords[1] = Object.assign({}, + bidRequests[1], + { + params: { + uid: '43', + keywords: { + single: 'val', + singleArr: ['val'], + singleArrNum: [5], + multiValMixed: ['value1', 2, 'value3'], + singleValNum: 123, + emptyStr: '', + emptyArr: [''], + badValue: {'foo': 'bar'} // should be dropped + } + } + } + ); + + const request = spec.buildRequests(bidRequestWithKeywords, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.keywords).to.be.an('string'); + payload.keywords = JSON.parse(payload.keywords); + + expect(payload.keywords).to.deep.equal([{ + 'key': 'single', + 'value': ['val'] + }, { + 'key': 'singleArr', + 'value': ['val'] + }, { + 'key': 'singleArrNum', + 'value': ['5'] + }, { + 'key': 'multiValMixed', + 'value': ['value1', '2', 'value3'] + }, { + 'key': 'singleValNum', + 'value': ['123'] + }, { + 'key': 'emptyStr' + }, { + 'key': 'emptyArr' + }]); + }); }); describe('interpretResponse', function () { From 32f6aae364fcb808cf0a410003d7a9b89512fb2f Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Wed, 27 Nov 2019 14:32:12 +0300 Subject: [PATCH 22/30] TrustX Bid Adapter: added us_privacy parameter in bid request --- modules/trustxBidAdapter.js | 3 +++ test/spec/modules/trustxBidAdapter_spec.js | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 00c86dec0d3..e2091c61542 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -121,6 +121,9 @@ export const spec = { (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? Number(bidderRequest.gdprConsent.gdprApplies) : 1; } + if (bidderRequest.us_privacy) { + payload.us_privacy = bidderRequest.us_privacy; + } } return { diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index 4256012ba0b..e77fc8731cd 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -168,6 +168,14 @@ describe('TrustXAdapter', function () { expect(payload).to.have.property('gdpr_applies', '1'); }); + it('if usPrivacy is present payload must have us_privacy param', function () { + const bidderRequestWithUSP = Object.assign({us_privacy: '1YN'}, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequestWithUSP); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('us_privacy', '1YN'); + }); + it('should convert keyword params to proper form and attaches to request', function () { const bidRequestWithKeywords = [].concat(bidRequests); bidRequestWithKeywords[1] = Object.assign({}, From 9b8e6838b086c707b4755c2c29008938a36a395a Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Wed, 4 Dec 2019 19:16:01 +0300 Subject: [PATCH 23/30] TrustX Bid Adapter: fix us_privacy parameter in bid request --- modules/trustxBidAdapter.js | 4 ++-- test/spec/modules/trustxBidAdapter_spec.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index e2091c61542..94d541d1fe2 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -121,8 +121,8 @@ export const spec = { (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? Number(bidderRequest.gdprConsent.gdprApplies) : 1; } - if (bidderRequest.us_privacy) { - payload.us_privacy = bidderRequest.us_privacy; + if (bidderRequest.uspConsent) { + payload.us_privacy = bidderRequest.uspConsent; } } diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index e77fc8731cd..aee6311cb85 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -169,11 +169,11 @@ describe('TrustXAdapter', function () { }); it('if usPrivacy is present payload must have us_privacy param', function () { - const bidderRequestWithUSP = Object.assign({us_privacy: '1YN'}, bidderRequest); + const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithUSP); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); - expect(payload).to.have.property('us_privacy', '1YN'); + expect(payload).to.have.property('us_privacy', '1YNN'); }); it('should convert keyword params to proper form and attaches to request', function () { From a25be55403ca7f7ff94c9a0c93dab3df79fb6658 Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Mon, 1 Mar 2021 19:13:45 +0300 Subject: [PATCH 24/30] Fix alias error for TrustX Bid Adapter --- modules/trustxBidAdapter.js | 1 - test/spec/modules/trustxBidAdapter_spec.js | 14 -------------- 2 files changed, 15 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index f412fa21a05..a86ac1f2874 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -207,7 +207,6 @@ function _addBidResponse(serverBid, bidsMap, priceType, bidResponses, RendererCo const bid = slot.bids.shift(); const bidResponse = { requestId: bid.bidId, // bid.bidderRequestId, - bidderCode: spec.code, cpm: serverBid.price, width: serverBid.w, height: serverBid.h, diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index 9e0aad9b36f..e9daaa83b5d 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -262,7 +262,6 @@ describe('TrustXAdapter', function () { 'width': 300, 'height': 250, 'ad': '
test content 1
', - 'bidderCode': 'trustx', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': true, @@ -320,7 +319,6 @@ describe('TrustXAdapter', function () { 'width': 300, 'height': 250, 'ad': '
test content 1
', - 'bidderCode': 'trustx', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': true, @@ -334,7 +332,6 @@ describe('TrustXAdapter', function () { 'width': 300, 'height': 600, 'ad': '
test content 2
', - 'bidderCode': 'trustx', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': true, @@ -348,7 +345,6 @@ describe('TrustXAdapter', function () { 'width': 728, 'height': 90, 'ad': '
test content 3
', - 'bidderCode': 'trustx', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': true, @@ -476,7 +472,6 @@ describe('TrustXAdapter', function () { 'width': 300, 'height': 250, 'ad': '
test content 1
', - 'bidderCode': 'trustx', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': true, @@ -490,7 +485,6 @@ describe('TrustXAdapter', function () { 'width': 300, 'height': 600, 'ad': '
test content 2
', - 'bidderCode': 'trustx', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': true, @@ -504,7 +498,6 @@ describe('TrustXAdapter', function () { 'width': 728, 'height': 90, 'ad': '
test content 3
', - 'bidderCode': 'trustx', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': true, @@ -518,7 +511,6 @@ describe('TrustXAdapter', function () { 'width': 300, 'height': 600, 'ad': '
test content 4
', - 'bidderCode': 'trustx', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': true, @@ -580,7 +572,6 @@ describe('TrustXAdapter', function () { 'width': 300, 'height': 250, 'ad': '
test content 1
', - 'bidderCode': 'trustx', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': true, @@ -594,7 +585,6 @@ describe('TrustXAdapter', function () { 'width': 300, 'height': 250, 'ad': '
test content 2
', - 'bidderCode': 'trustx', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': true, @@ -655,7 +645,6 @@ describe('TrustXAdapter', function () { 'dealId': undefined, 'width': 300, 'height': 600, - 'bidderCode': 'trustx', 'currency': 'USD', 'mediaType': 'video', 'netRevenue': true, @@ -735,7 +724,6 @@ describe('TrustXAdapter', function () { 'dealId': undefined, 'width': 300, 'height': 600, - 'bidderCode': 'trustx', 'currency': 'USD', 'mediaType': 'video', 'netRevenue': true, @@ -753,7 +741,6 @@ describe('TrustXAdapter', function () { 'dealId': undefined, 'width': 300, 'height': 250, - 'bidderCode': 'trustx', 'currency': 'USD', 'mediaType': 'video', 'netRevenue': true, @@ -771,7 +758,6 @@ describe('TrustXAdapter', function () { 'dealId': undefined, 'width': 300, 'height': 250, - 'bidderCode': 'trustx', 'currency': 'USD', 'mediaType': 'video', 'netRevenue': true, From 8fb13fecfea883496ac197f63669ebfeabda2d62 Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Mon, 22 Mar 2021 20:10:33 +0300 Subject: [PATCH 25/30] TrustX Bid Adapter: added new request format --- modules/trustxBidAdapter.js | 556 ++++++++++++++++----- test/spec/modules/trustxBidAdapter_spec.js | 465 ++++++++++++++++- 2 files changed, 867 insertions(+), 154 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index a86ac1f2874..cae18c1f27b 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -2,9 +2,11 @@ import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { Renderer } from '../src/Renderer.js'; import { VIDEO, BANNER } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; const BIDDER_CODE = 'trustx'; const ENDPOINT_URL = 'https://sofia.trustx.org/hb'; +const NEW_ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; const TIME_TO_LIVE = 360; const ADAPTER_SYNC_URL = 'https://sofia.trustx.org/push_sync'; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; @@ -40,98 +42,30 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function(validBidRequests, bidderRequest) { - const auids = []; - const bidsMap = {}; - const slotsMapByUid = {}; - const sizeMap = {}; const bids = validBidRequests || []; - let priceType = 'net'; - let pageKeywords; - let reqId; - + const newFormatBids = []; + const oldFormatBids = []; + const requests = []; bids.forEach(bid => { - if (bid.params.priceType === 'gross') { - priceType = 'gross'; - } - reqId = bid.bidderRequestId; - const {params: {uid}, adUnitCode} = bid; - auids.push(uid); - const sizesId = utils.parseSizesInput(bid.sizes); - - if (!pageKeywords && !utils.isEmpty(bid.params.keywords)) { - const keywords = utils.transformBidderParamKeywords(bid.params.keywords); - - if (keywords.length > 0) { - keywords.forEach(deleteValues); - } - pageKeywords = keywords; - } - - if (!slotsMapByUid[uid]) { - slotsMapByUid[uid] = {}; - } - const slotsMap = slotsMapByUid[uid]; - if (!slotsMap[adUnitCode]) { - slotsMap[adUnitCode] = {adUnitCode, bids: [bid], parents: []}; + if (bid.params.useNewFormat) { + newFormatBids.push(bid); } else { - slotsMap[adUnitCode].bids.push(bid); + oldFormatBids.push(bid); } - const slot = slotsMap[adUnitCode]; - - sizesId.forEach((sizeId) => { - sizeMap[sizeId] = true; - if (!bidsMap[uid]) { - bidsMap[uid] = {}; - } - - if (!bidsMap[uid][sizeId]) { - bidsMap[uid][sizeId] = [slot]; - } else { - bidsMap[uid][sizeId].push(slot); - } - slot.parents.push({parent: bidsMap[uid], key: sizeId, uid}); - }); }); - - const payload = { - pt: priceType, - auids: auids.join(','), - sizes: utils.getKeys(sizeMap).join(','), - r: reqId, - wrapperType: 'Prebid_js', - wrapperVersion: '$prebid.version$' - }; - - if (pageKeywords) { - payload.keywords = JSON.stringify(pageKeywords); - } - - if (bidderRequest) { - if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { - payload.u = bidderRequest.refererInfo.referer; + if (newFormatBids.length) { + const newFormatRequests = newFormatRequest(newFormatBids, bidderRequest); + if (newFormatRequests) { + requests.push(newFormatRequests); } - if (bidderRequest.timeout) { - payload.wtimeout = bidderRequest.timeout; - } - if (bidderRequest.gdprConsent) { - if (bidderRequest.gdprConsent.consentString) { - payload.gdpr_consent = bidderRequest.gdprConsent.consentString; - } - payload.gdpr_applies = - (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') - ? Number(bidderRequest.gdprConsent.gdprApplies) : 1; - } - if (bidderRequest.uspConsent) { - payload.us_privacy = bidderRequest.uspConsent; + } + if (oldFormatBids.length) { + const oldFormatRequests = oldFormatRequest(oldFormatBids, bidderRequest); + if (oldFormatRequests) { + requests.push(oldFormatRequests); } } - - return { - method: 'GET', - url: ENDPOINT_URL, - data: utils.parseQueryStringParameters(payload).replace(/\&$/, ''), - bidsMap: bidsMap, - }; + return requests; }, /** * Unpack the response from the server into a list of bids. @@ -143,8 +77,6 @@ export const spec = { interpretResponse: function(serverResponse, bidRequest, RendererConst = Renderer) { serverResponse = serverResponse && serverResponse.body; const bidResponses = []; - const bidsMap = bidRequest.bidsMap; - const priceType = bidRequest.data.pt; let errorMessage; @@ -155,7 +87,7 @@ export const spec = { if (!errorMessage && serverResponse.seatbid) { serverResponse.seatbid.forEach(respItem => { - _addBidResponse(_getBidFromResponse(respItem), bidsMap, priceType, bidResponses, RendererConst); + _addBidResponse(_getBidFromResponse(respItem), bidRequest, bidResponses, RendererConst); }); } if (errorMessage) utils.logError(errorMessage); @@ -192,66 +124,75 @@ function _getBidFromResponse(respItem) { return respItem && respItem.bid && respItem.bid[0]; } -function _addBidResponse(serverBid, bidsMap, priceType, bidResponses, RendererConst) { +function _addBidResponse(serverBid, bidRequest, bidResponses, RendererConst) { if (!serverBid) return; let errorMessage; if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); if (!serverBid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); else { - const awaitingBids = bidsMap[serverBid.auid]; - if (awaitingBids) { - const sizeId = `${serverBid.w}x${serverBid.h}`; - if (awaitingBids[sizeId]) { - const slot = awaitingBids[sizeId][0]; - - const bid = slot.bids.shift(); - const bidResponse = { - requestId: bid.bidId, // bid.bidderRequestId, - cpm: serverBid.price, - width: serverBid.w, - height: serverBid.h, - creativeId: serverBid.auid, // bid.bidId, - currency: 'USD', - netRevenue: priceType !== 'gross', - ttl: TIME_TO_LIVE, - dealId: serverBid.dealid - }; - if (serverBid.content_type === 'video') { - bidResponse.vastXml = serverBid.adm; - bidResponse.mediaType = VIDEO; - bidResponse.adResponse = { - content: bidResponse.vastXml - }; - if (!bid.renderer && (!bid.mediaTypes || !bid.mediaTypes.video || bid.mediaTypes.video.context === 'outstream')) { - bidResponse.renderer = createRenderer(bidResponse, { - id: bid.bidId, - url: RENDERER_URL - }, RendererConst); - } - } else { - bidResponse.ad = serverBid.adm; - bidResponse.mediaType = BANNER; + const { bidsMap, priceType, newFormat } = bidRequest; + let bid; + let slot; + if (newFormat) { + bid = bidsMap[serverBid.impid]; + } else { + const awaitingBids = bidsMap[serverBid.auid]; + if (awaitingBids) { + const sizeId = `${serverBid.w}x${serverBid.h}`; + if (awaitingBids[sizeId]) { + slot = awaitingBids[sizeId][0]; + bid = slot.bids.shift(); } + } else { + errorMessage = LOG_ERROR_MESS.noPlacementCode + serverBid.auid; + } + } - bidResponses.push(bidResponse); - - if (!slot.bids.length) { - slot.parents.forEach(({parent, key, uid}) => { - const index = parent[key].indexOf(slot); - if (index > -1) { - parent[key].splice(index, 1); - } - if (!parent[key].length) { - delete parent[key]; - if (!utils.getKeys(parent).length) { - delete bidsMap[uid]; - } - } - }); + if (!errorMessage && bid) { + const bidResponse = { + requestId: bid.bidId, // bid.bidderRequestId, + cpm: serverBid.price, + width: serverBid.w, + height: serverBid.h, + creativeId: serverBid.auid, // bid.bidId, + currency: 'USD', + netRevenue: newFormat ? false : priceType !== 'gross', + ttl: TIME_TO_LIVE, + dealId: serverBid.dealid + }; + if (serverBid.content_type === 'video') { + bidResponse.vastXml = serverBid.adm; + bidResponse.mediaType = VIDEO; + bidResponse.adResponse = { + content: bidResponse.vastXml + }; + if (!bid.renderer && (!bid.mediaTypes || !bid.mediaTypes.video || bid.mediaTypes.video.context === 'outstream')) { + bidResponse.renderer = createRenderer(bidResponse, { + id: bid.bidId, + url: RENDERER_URL + }, RendererConst); } + } else { + bidResponse.ad = serverBid.adm; + bidResponse.mediaType = BANNER; } - } else { - errorMessage = LOG_ERROR_MESS.noPlacementCode + serverBid.auid; + + bidResponses.push(bidResponse); + } + + if (slot && !slot.bids.length) { + slot.parents.forEach(({parent, key, uid}) => { + const index = parent[key].indexOf(slot); + if (index > -1) { + parent[key].splice(index, 1); + } + if (!parent[key].length) { + delete parent[key]; + if (!utils.getKeys(parent).length) { + delete bidsMap[uid]; + } + } + }); } } if (errorMessage) { @@ -284,4 +225,343 @@ function createRenderer (bid, rendererParams, RendererConst) { return rendererInst; } +function createVideoRequest(bid, mediaType) { + const {playerSize, mimes, durationRangeSec, protocols} = mediaType; + const size = (playerSize || bid.sizes || [])[0]; + if (!size) return; + + let result = utils.parseGPTSingleSizeArrayToRtbSize(size); + + if (mimes) { + result.mimes = mimes; + } + + if (durationRangeSec && durationRangeSec.length === 2) { + result.minduration = durationRangeSec[0]; + result.maxduration = durationRangeSec[1]; + } + + if (protocols && protocols.length) { + result.protocols = protocols; + } + + return result; +} + +function createBannerRequest(bid, mediaType) { + const sizes = mediaType.sizes || bid.sizes; + if (!sizes || !sizes.length) return; + + let format = sizes.map((size) => utils.parseGPTSingleSizeArrayToRtbSize(size)); + let result = utils.parseGPTSingleSizeArrayToRtbSize(sizes[0]); + + if (format.length) { + result.format = format + } + return result; +} + +/** + * Gets bidfloor + * @param {Object} mediaTypes + * @param {Object} bid + * @returns {Number} floor + */ +function _getFloor (mediaTypes, bid) { + const curMediaType = mediaTypes.video ? 'video' : 'banner'; + let floor = bid.params.bidFloor || 0; + + if (typeof bid.getFloor === 'function') { + const floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: curMediaType, + size: bid.sizes.map(([w, h]) => ({w, h})) + }); + + if (typeof floorInfo === 'object' && + floorInfo.currency === 'USD' && + !isNaN(parseFloat(floorInfo.floor))) { + floor = Math.max(floor, parseFloat(floorInfo.floor)); + } + } + + return floor; +} + +function newFormatRequest(validBidRequests, bidderRequest) { + if (!validBidRequests.length) { + return null; + } + let pageKeywords = null; + let jwpseg = null; + let content = null; + let schain = null; + let userId = null; + let userIdAsEids = null; + let user = null; + let userExt = null; + let {bidderRequestId, auctionId, gdprConsent, uspConsent, timeout, refererInfo} = bidderRequest || {}; + + const referer = refererInfo ? encodeURIComponent(refererInfo.referer) : ''; + const imp = []; + const bidsMap = {}; + + validBidRequests.forEach((bid) => { + if (!bidderRequestId) { + bidderRequestId = bid.bidderRequestId; + } + if (!auctionId) { + auctionId = bid.auctionId; + } + if (!schain) { + schain = bid.schain; + } + if (!userId) { + userId = bid.userId; + } + if (!userIdAsEids) { + userIdAsEids = bid.userIdAsEids; + } + const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, rtd} = bid; + bidsMap[bidId] = bid; + if (!pageKeywords && !utils.isEmpty(keywords)) { + pageKeywords = utils.transformBidderParamKeywords(keywords); + } + const bidFloor = _getFloor(mediaTypes || {}, bid); + const jwTargeting = rtd && rtd.jwplayer && rtd.jwplayer.targeting; + if (jwTargeting) { + if (!jwpseg && jwTargeting.segments) { + jwpseg = jwTargeting.segments; + } + if (!content && jwTargeting.content) { + content = jwTargeting.content; + } + } + let impObj = { + id: bidId, + tagid: uid.toString(), + ext: { + divid: adUnitCode + } + }; + + if (bidFloor) { + impObj.bidfloor = bidFloor; + } + + if (!mediaTypes || mediaTypes[BANNER]) { + const banner = createBannerRequest(bid, mediaTypes ? mediaTypes[BANNER] : {}); + if (banner) { + impObj.banner = banner; + } + } + if (mediaTypes && mediaTypes[VIDEO]) { + const video = createVideoRequest(bid, mediaTypes[VIDEO]); + if (video) { + impObj.video = video; + } + } + + if (impObj.banner || impObj.video) { + imp.push(impObj); + } + }); + + const source = { + tid: auctionId, + ext: { + wrapper: 'Prebid_js', + wrapper_version: '$prebid.version$' + } + }; + + if (schain) { + source.ext.schain = schain; + } + + const bidderTimeout = config.getConfig('bidderTimeout') || timeout; + const tmax = timeout ? Math.min(bidderTimeout, timeout) : bidderTimeout; + + let request = { + id: bidderRequestId, + site: { + page: referer + }, + tmax, + source, + imp + }; + + if (content) { + request.site.content = content; + } + + if (jwpseg && jwpseg.length) { + user = { + data: [{ + name: 'iow_labs_pub_data', + segment: jwpseg.map((seg) => { + return {name: 'jwpseg', value: seg}; + }) + }] + }; + } + + if (gdprConsent && gdprConsent.consentString) { + userExt = {consent: gdprConsent.consentString}; + } + + if (userIdAsEids && userIdAsEids.length) { + userExt = userExt || {}; + userExt.eids = [...userIdAsEids]; + } + + if (userExt && Object.keys(userExt).length) { + user = user || {}; + user.ext = userExt; + } + + if (user) { + request.user = user; + } + + const configKeywords = utils.transformBidderParamKeywords({ + 'user': utils.deepAccess(config.getConfig('ortb2.user'), 'keywords') || null, + 'context': utils.deepAccess(config.getConfig('ortb2.site'), 'keywords') || null + }); + + if (configKeywords.length) { + pageKeywords = (pageKeywords || []).concat(configKeywords); + } + + if (pageKeywords && pageKeywords.length > 0) { + pageKeywords.forEach(deleteValues); + } + + if (pageKeywords) { + request.ext = { + keywords: pageKeywords + }; + } + + if (gdprConsent && gdprConsent.gdprApplies) { + request.regs = { + ext: { + gdpr: gdprConsent.gdprApplies ? 1 : 0 + } + } + } + + if (uspConsent) { + if (!request.regs) { + request.regs = {ext: {}}; + } + request.regs.ext.us_privacy = uspConsent; + } + + return { + method: 'POST', + url: NEW_ENDPOINT_URL, + data: JSON.stringify(request), + newFormat: true, + bidsMap + }; +} + +function oldFormatRequest(validBidRequests, bidderRequest) { + const auids = []; + const bidsMap = {}; + const slotsMapByUid = {}; + const sizeMap = {}; + const bids = validBidRequests || []; + let priceType = 'net'; + let pageKeywords; + let reqId; + + bids.forEach(bid => { + if (bid.params.priceType === 'gross') { + priceType = 'gross'; + } + reqId = bid.bidderRequestId; + const {params: {uid}, adUnitCode} = bid; + auids.push(uid); + const sizesId = utils.parseSizesInput(bid.sizes); + + if (!pageKeywords && !utils.isEmpty(bid.params.keywords)) { + const keywords = utils.transformBidderParamKeywords(bid.params.keywords); + + if (keywords.length > 0) { + keywords.forEach(deleteValues); + } + pageKeywords = keywords; + } + + if (!slotsMapByUid[uid]) { + slotsMapByUid[uid] = {}; + } + const slotsMap = slotsMapByUid[uid]; + if (!slotsMap[adUnitCode]) { + slotsMap[adUnitCode] = {adUnitCode, bids: [bid], parents: []}; + } else { + slotsMap[adUnitCode].bids.push(bid); + } + const slot = slotsMap[adUnitCode]; + + sizesId.forEach((sizeId) => { + sizeMap[sizeId] = true; + if (!bidsMap[uid]) { + bidsMap[uid] = {}; + } + + if (!bidsMap[uid][sizeId]) { + bidsMap[uid][sizeId] = [slot]; + } else { + bidsMap[uid][sizeId].push(slot); + } + slot.parents.push({parent: bidsMap[uid], key: sizeId, uid}); + }); + }); + + const payload = { + pt: priceType, + auids: auids.join(','), + sizes: utils.getKeys(sizeMap).join(','), + r: reqId, + wrapperType: 'Prebid_js', + wrapperVersion: '$prebid.version$' + }; + + if (pageKeywords) { + payload.keywords = JSON.stringify(pageKeywords); + } + + if (bidderRequest) { + if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { + payload.u = bidderRequest.refererInfo.referer; + } + if (bidderRequest.timeout) { + payload.wtimeout = bidderRequest.timeout; + } + if (bidderRequest.gdprConsent) { + if (bidderRequest.gdprConsent.consentString) { + payload.gdpr_consent = bidderRequest.gdprConsent.consentString; + } + payload.gdpr_applies = + (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') + ? Number(bidderRequest.gdprConsent.gdprApplies) : 1; + } + if (bidderRequest.uspConsent) { + payload.us_privacy = bidderRequest.uspConsent; + } + } + + return { + method: 'GET', + url: ENDPOINT_URL, + data: utils.parseQueryStringParameters(payload).replace(/\&$/, ''), + bidsMap, + priceType + }; +} + registerBidder(spec); diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index e9daaa83b5d..c0155f87ab6 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { spec } from 'modules/trustxBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from 'src/config.js'; describe('TrustXAdapter', function () { const adapter = newBidder(spec); @@ -92,7 +93,7 @@ describe('TrustXAdapter', function () { ]; it('should attach valid params to the tag', function () { - const request = spec.buildRequests([bidRequests[0]], bidderRequest); + const request = spec.buildRequests([bidRequests[0]], bidderRequest)[0]; expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('u', referrer); @@ -105,7 +106,7 @@ describe('TrustXAdapter', function () { }); it('sizes must not be duplicated', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('u', referrer); @@ -117,7 +118,7 @@ describe('TrustXAdapter', function () { it('pt parameter must be "gross" if params.priceType === "gross"', function () { bidRequests[1].params.priceType = 'gross'; - const request = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('u', referrer); @@ -130,7 +131,7 @@ describe('TrustXAdapter', function () { it('pt parameter must be "net" or "gross"', function () { bidRequests[1].params.priceType = 'some'; - const request = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('u', referrer); @@ -143,7 +144,7 @@ describe('TrustXAdapter', function () { it('if gdprConsent is present payload must have gdpr params', function () { const bidderRequestWithGDPR = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: true}}, bidderRequest); - const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); + const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR)[0]; expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('gdpr_consent', 'AAA'); @@ -152,7 +153,7 @@ describe('TrustXAdapter', function () { it('if gdprApplies is false gdpr_applies must be 0', function () { const bidderRequestWithGDPR = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: false}}, bidderRequest); - const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); + const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR)[0]; expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('gdpr_consent', 'AAA'); @@ -161,7 +162,7 @@ describe('TrustXAdapter', function () { it('if gdprApplies is undefined gdpr_applies must be 1', function () { const bidderRequestWithGDPR = Object.assign({gdprConsent: {consentString: 'AAA'}}, bidderRequest); - const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); + const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR)[0]; expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('gdpr_consent', 'AAA'); @@ -170,7 +171,7 @@ describe('TrustXAdapter', function () { it('if usPrivacy is present payload must have us_privacy param', function () { const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); - const request = spec.buildRequests(bidRequests, bidderRequestWithUSP); + const request = spec.buildRequests(bidRequests, bidderRequestWithUSP)[0]; expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('us_privacy', '1YNN'); @@ -197,7 +198,7 @@ describe('TrustXAdapter', function () { } ); - const request = spec.buildRequests(bidRequestWithKeywords, bidderRequest); + const request = spec.buildRequests(bidRequestWithKeywords, bidderRequest)[0]; expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload.keywords).to.be.an('string'); @@ -226,6 +227,438 @@ describe('TrustXAdapter', function () { }); }); + describe('buildRequests with new format', function () { + function parseRequest(data) { + return JSON.parse(data); + } + const bidderRequest = { + refererInfo: {referer: 'https://example.com'}, + bidderRequestId: '22edbae2733bf6', + auctionId: '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + timeout: 3000 + }; + const referrer = encodeURIComponent(bidderRequest.refererInfo.referer); + + let bidRequests = [ + { + 'bidder': 'trustx', + 'params': { + 'uid': '43', + 'bidFloor': 1.25, + 'useNewFormat': true + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [300, 600]] + } + }, + 'bidId': '42dbe3a7168a6a', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '44', + 'useNewFormat': true + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '45', + 'useNewFormat': true + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'mediaTypes': { + 'video': { + 'playerSize': [[400, 600]], + 'mimes': ['video/mp4', 'video/webm', 'application/javascript', 'video/ogg'] + } + }, + 'bidId': '3150ccb55da321', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + }, + { + 'bidder': 'trustx', + 'params': { + 'uid': '41', + 'useNewFormat': true + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'mediaTypes': { + 'video': { + 'playerSize': [[400, 600]], + 'protocols': [1, 2, 3] + }, + 'banner': { + 'sizes': [[728, 90]] + } + }, + 'bidId': '3150ccb55da321', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + } + ]; + + it('should attach valid params to the tag', function () { + const request = spec.buildRequests([bidRequests[0]], bidderRequest)[0]; + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.deep.equal({ + 'id': bidderRequest.bidderRequestId, + 'site': { + 'page': referrer + }, + 'tmax': bidderRequest.timeout, + 'source': { + 'tid': bidderRequest.auctionId, + 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} + }, + 'imp': [{ + 'id': bidRequests[0].bidId, + 'tagid': bidRequests[0].params.uid, + 'ext': {'divid': bidRequests[0].adUnitCode}, + 'bidfloor': bidRequests[0].params.bidFloor, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + } + }] + }); + }); + + it('make possible to process request without mediaTypes', function () { + const request = spec.buildRequests([bidRequests[0], bidRequests[1]], bidderRequest)[0]; + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.deep.equal({ + 'id': bidderRequest.bidderRequestId, + 'site': { + 'page': referrer + }, + 'tmax': bidderRequest.timeout, + 'source': { + 'tid': bidderRequest.auctionId, + 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} + }, + 'imp': [{ + 'id': bidRequests[0].bidId, + 'tagid': bidRequests[0].params.uid, + 'ext': {'divid': bidRequests[0].adUnitCode}, + 'bidfloor': bidRequests[0].params.bidFloor, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + } + }, { + 'id': bidRequests[1].bidId, + 'tagid': bidRequests[1].params.uid, + 'ext': {'divid': bidRequests[1].adUnitCode}, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + } + }] + }); + }); + + it('should attach valid params to the video tag', function () { + const request = spec.buildRequests(bidRequests.slice(0, 3), bidderRequest)[0]; + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.deep.equal({ + 'id': bidderRequest.bidderRequestId, + 'site': { + 'page': referrer + }, + 'tmax': bidderRequest.timeout, + 'source': { + 'tid': bidderRequest.auctionId, + 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} + }, + 'imp': [{ + 'id': bidRequests[0].bidId, + 'tagid': bidRequests[0].params.uid, + 'ext': {'divid': bidRequests[0].adUnitCode}, + 'bidfloor': bidRequests[0].params.bidFloor, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + } + }, { + 'id': bidRequests[1].bidId, + 'tagid': bidRequests[1].params.uid, + 'ext': {'divid': bidRequests[1].adUnitCode}, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + } + }, { + 'id': bidRequests[2].bidId, + 'tagid': bidRequests[2].params.uid, + 'ext': {'divid': bidRequests[2].adUnitCode}, + 'video': { + 'w': 400, + 'h': 600, + 'mimes': ['video/mp4', 'video/webm', 'application/javascript', 'video/ogg'] + } + }] + }); + }); + + it('should support mixed mediaTypes', function () { + const request = spec.buildRequests(bidRequests, bidderRequest)[0]; + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.deep.equal({ + 'id': bidderRequest.bidderRequestId, + 'site': { + 'page': referrer + }, + 'tmax': bidderRequest.timeout, + 'source': { + 'tid': bidderRequest.auctionId, + 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} + }, + 'imp': [{ + 'id': bidRequests[0].bidId, + 'tagid': bidRequests[0].params.uid, + 'ext': {'divid': bidRequests[0].adUnitCode}, + 'bidfloor': bidRequests[0].params.bidFloor, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + } + }, { + 'id': bidRequests[1].bidId, + 'tagid': bidRequests[1].params.uid, + 'ext': {'divid': bidRequests[1].adUnitCode}, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + } + }, { + 'id': bidRequests[2].bidId, + 'tagid': bidRequests[2].params.uid, + 'ext': {'divid': bidRequests[2].adUnitCode}, + 'video': { + 'w': 400, + 'h': 600, + 'mimes': ['video/mp4', 'video/webm', 'application/javascript', 'video/ogg'], + } + }, { + 'id': bidRequests[3].bidId, + 'tagid': bidRequests[3].params.uid, + 'ext': {'divid': bidRequests[3].adUnitCode}, + 'banner': { + 'w': 728, + 'h': 90, + 'format': [{'w': 728, 'h': 90}] + }, + 'video': { + 'w': 400, + 'h': 600, + 'protocols': [1, 2, 3] + } + }] + }); + }); + + it('if gdprConsent is present payload must have gdpr params', function () { + const gdprBidderRequest = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: true}}, bidderRequest); + const request = spec.buildRequests(bidRequests, gdprBidderRequest)[0]; + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('user'); + expect(payload.user).to.have.property('ext'); + expect(payload.user.ext).to.have.property('consent', 'AAA'); + expect(payload).to.have.property('regs'); + expect(payload.regs).to.have.property('ext'); + expect(payload.regs.ext).to.have.property('gdpr', 1); + }); + + it('if usPrivacy is present payload must have us_privacy param', function () { + const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequestWithUSP)[0]; + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('regs'); + expect(payload.regs).to.have.property('ext'); + expect(payload.regs.ext).to.have.property('us_privacy', '1YNN'); + }); + + it('if userId is present payload must have user.ext param with right keys', function () { + const eids = [ + { + source: 'pubcid.org', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] + }, + { + source: 'adserver.org', + uids: [{ + id: 'some-random-id-value', + atype: 1, + ext: { + rtiPartner: 'TDID' + } + }] + } + ]; + const bidRequestsWithUserIds = bidRequests.map((bid) => { + return Object.assign({ + userIdAsEids: eids + }, bid); + }); + const request = spec.buildRequests(bidRequestsWithUserIds, bidderRequest)[0]; + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('user'); + expect(payload.user).to.have.property('ext'); + expect(payload.user.ext.eids).to.deep.equal(eids); + }); + + it('if schain is present payload must have source.ext.schain param', function () { + const schain = { + complete: 1, + nodes: [ + { + asi: 'indirectseller.com', + sid: '00001', + hp: 1 + } + ] + }; + const bidRequestsWithSChain = bidRequests.map((bid) => { + return Object.assign({ + schain: schain + }, bid); + }); + const request = spec.buildRequests(bidRequestsWithSChain, bidderRequest)[0]; + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('source'); + expect(payload.source).to.have.property('ext'); + expect(payload.source.ext).to.have.property('schain'); + expect(payload.source.ext.schain).to.deep.equal(schain); + }); + + it('if content and segment is present in jwTargeting, payload must have right params', function () { + const jsContent = {id: 'test_jw_content_id'}; + const jsSegments = ['test_seg_1', 'test_seg_2']; + const bidRequestsWithJwTargeting = bidRequests.map((bid) => { + return Object.assign({ + rtd: { + jwplayer: { + targeting: { + segments: jsSegments, + content: jsContent + } + } + } + }, bid); + }); + const request = spec.buildRequests(bidRequestsWithJwTargeting, bidderRequest)[0]; + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('user'); + expect(payload.user.data).to.deep.equal([{ + name: 'iow_labs_pub_data', + segment: [ + {name: 'jwpseg', value: jsSegments[0]}, + {name: 'jwpseg', value: jsSegments[1]} + ] + }]); + expect(payload).to.have.property('site'); + expect(payload.site.content).to.deep.equal(jsContent); + }); + + it('should contain the keyword values if it present in ortb2.(site/user)', function () { + const getConfigStub = sinon.stub(config, 'getConfig').callsFake( + arg => arg === 'ortb2.user' ? {'keywords': 'foo'} : (arg === 'ortb2.site' ? {'keywords': 'bar'} : null)); + const request = spec.buildRequests([bidRequests[0]], bidderRequest)[0]; + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.ext.keywords).to.deep.equal([{'key': 'user', 'value': ['foo']}, {'key': 'context', 'value': ['bar']}]); + getConfigStub.restore(); + }); + + it('shold be right tmax when timeout in config is less then timeout in bidderRequest', function() { + const getConfigStub = sinon.stub(config, 'getConfig').callsFake( + arg => arg === 'bidderTimeout' ? 2000 : null); + const request = spec.buildRequests([bidRequests[0]], bidderRequest)[0]; + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.tmax).to.equal(2000); + getConfigStub.restore(); + }); + it('shold be right tmax when timeout in bidderRequest is less then timeout in config', function() { + const getConfigStub = sinon.stub(config, 'getConfig').callsFake( + arg => arg === 'bidderTimeout' ? 5000 : null); + const request = spec.buildRequests([bidRequests[0]], bidderRequest)[0]; + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.tmax).to.equal(3000); + getConfigStub.restore(); + }); + + describe('floorModule', function () { + const floorTestData = { + 'currency': 'USD', + 'floor': 1.50 + }; + const bidRequest = Object.assign({ + getFloor: (_) => { + return floorTestData; + } + }, bidRequests[1]); + it('should return the value from getFloor if present', function () { + const request = spec.buildRequests([bidRequest], bidderRequest)[0]; + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.imp[0].bidfloor).to.equal(floorTestData.floor); + }); + it('should return the getFloor.floor value if it is greater than bidfloor', function () { + const bidfloor = 0.80; + const bidRequestsWithFloor = { ...bidRequest }; + bidRequestsWithFloor.params = Object.assign({bidFloor: bidfloor}, bidRequestsWithFloor.params); + const request = spec.buildRequests([bidRequestsWithFloor], bidderRequest)[0]; + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.imp[0].bidfloor).to.equal(floorTestData.floor); + }); + it('should return the bidfloor value if it is greater than getFloor.floor', function () { + const bidfloor = 1.80; + const bidRequestsWithFloor = { ...bidRequest }; + bidRequestsWithFloor.params = Object.assign({bidFloor: bidfloor}, bidRequestsWithFloor.params); + const request = spec.buildRequests([bidRequestsWithFloor], bidderRequest)[0]; + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.imp[0].bidfloor).to.equal(bidfloor); + }); + }); + }); + describe('interpretResponse', function () { const responses = [ {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 43, 'h': 250, 'w': 300}], 'seat': '1'}, @@ -252,7 +685,7 @@ describe('TrustXAdapter', function () { 'auctionId': '1cbd2feafe5e8b', } ]; - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests)[0]; const expectedResponse = [ { 'requestId': '659423fff799cb', @@ -309,7 +742,7 @@ describe('TrustXAdapter', function () { 'auctionId': '1fa09aee5c8c99', } ]; - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests)[0]; const expectedResponse = [ { 'requestId': '300bfeb0d71a5b', @@ -392,7 +825,7 @@ describe('TrustXAdapter', function () { 'auctionId': '1fa09aee5c84d34', } ]; - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests)[0]; const result = spec.interpretResponse({'body': {'seatbid': responses.slice(3)}}, request); expect(result.length).to.equal(0); }); @@ -462,7 +895,7 @@ describe('TrustXAdapter', function () { 'auctionId': '32a1f276cb87cb8', } ]; - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests)[0]; const expectedResponse = [ { 'requestId': '2164be6358b9', @@ -562,7 +995,7 @@ describe('TrustXAdapter', function () { 'auctionId': '35bcbc0f7e79c', } ]; - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests)[0]; const expectedResponse = [ { 'requestId': '5126e301f4be', @@ -636,7 +1069,7 @@ describe('TrustXAdapter', function () { {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 50, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, {'bid': [{'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 51, content_type: 'video'}], 'seat': '2'} ]; - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests)[0]; const expectedResponse = [ { 'requestId': '57dfefb80eca', @@ -715,7 +1148,7 @@ describe('TrustXAdapter', function () { {'bid': [{'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 51, content_type: 'video', w: 300, h: 250}], 'seat': '2'}, {'bid': [{'price': 1.20, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 52, content_type: 'video', w: 300, h: 250}], 'seat': '2'} ]; - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests)[0]; const expectedResponse = [ { 'requestId': 'e6e65553fc8', From e809a080c4b034268b579d8859b846978f913a3c Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Fri, 9 Apr 2021 10:38:37 +0300 Subject: [PATCH 26/30] TrustX Bid adapter: fix new format endpoint --- modules/trustxBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index cae18c1f27b..3116400edf7 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -6,7 +6,7 @@ import { config } from '../src/config.js'; const BIDDER_CODE = 'trustx'; const ENDPOINT_URL = 'https://sofia.trustx.org/hb'; -const NEW_ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; +const NEW_ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson?sp=trustx'; const TIME_TO_LIVE = 360; const ADAPTER_SYNC_URL = 'https://sofia.trustx.org/push_sync'; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; From 306dc26b28296684be500512624f6529eb7eb6cf Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Fri, 9 Apr 2021 10:55:20 +0300 Subject: [PATCH 27/30] TrustX Bid Adapter: update md file to support useNewFormat parameter --- modules/trustxBidAdapter.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/modules/trustxBidAdapter.md b/modules/trustxBidAdapter.md index a72f1ba85aa..e891df8f161 100644 --- a/modules/trustxBidAdapter.md +++ b/modules/trustxBidAdapter.md @@ -52,6 +52,18 @@ TrustX Bid Adapter supports Banner and Video (instream and outstream). } } ] + },{ + code: 'test-div', + sizes: [[300, 250]], + bids: [ + { + bidder: "trustx", + params: { + uid: '58851', + useNewFormat: true + } + } + ] } ]; -``` \ No newline at end of file +``` From df8c19e530adcab4b8e79a38f2ca8a59e88cb2f4 Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Mon, 17 May 2021 19:15:54 +0300 Subject: [PATCH 28/30] TrustX Bid Adapter: added additional sync url --- modules/trustxBidAdapter.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 3116400edf7..1cf19bd7280 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -7,6 +7,7 @@ import { config } from '../src/config.js'; const BIDDER_CODE = 'trustx'; const ENDPOINT_URL = 'https://sofia.trustx.org/hb'; const NEW_ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson?sp=trustx'; +const ADDITIONAL_SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; const ADAPTER_SYNC_URL = 'https://sofia.trustx.org/push_sync'; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; @@ -93,11 +94,14 @@ export const spec = { if (errorMessage) utils.logError(errorMessage); return bidResponses; }, - getUserSyncs: function(syncOptions) { + getUserSyncs: function(syncOptions, serverResponses) { if (syncOptions.pixelEnabled) { return [{ type: 'image', url: ADAPTER_SYNC_URL + }, { + type: 'image', + url: ADDITIONAL_SYNC_URL }]; } } From c3829ddc57e5bf4390826dde381cdcbbac53ba8d Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Tue, 18 May 2021 17:06:19 +0300 Subject: [PATCH 29/30] TrustX Bid Adapter: added check for enabled syncs number + added gdpr data to sync urls --- modules/trustxBidAdapter.js | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 1cf19bd7280..9f8de30a0d4 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -94,15 +94,32 @@ export const spec = { if (errorMessage) utils.logError(errorMessage); return bidResponses; }, - getUserSyncs: function(syncOptions, serverResponses) { + getUserSyncs: function(syncOptions, responses, gdprConsent, uspConsent) { if (syncOptions.pixelEnabled) { - return [{ - type: 'image', - url: ADAPTER_SYNC_URL - }, { + const syncsPerBidder = config.getConfig('userSync.syncsPerBidder'); + let params = []; + if (gdprConsent && typeof gdprConsent.consentString === 'string') { + if (typeof gdprConsent.gdprApplies === 'boolean') { + params.push(`gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`); + } else { + params.push(`gdpr_consent=${gdprConsent.consentString}`); + } + } + if (uspConsent) { + params.push(`us_privacy=${uspConsent}`); + } + const stringParams = params.join('&'); + const syncs = [{ type: 'image', - url: ADDITIONAL_SYNC_URL + url: ADAPTER_SYNC_URL + (stringParams ? `?${stringParams}` : '') }]; + if (syncsPerBidder > 1) { + syncs.push({ + type: 'image', + url: ADDITIONAL_SYNC_URL + (stringParams ? `&${stringParams}` : '') + }); + } + return syncs; } } } From 5b312b1193474ae7ecd6782e89871bdbd6b10af6 Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Mon, 31 May 2021 18:36:09 +0300 Subject: [PATCH 30/30] TrustX Bid Adapter: added support of meta.advertiserDomains --- modules/trustxBidAdapter.js | 5 ++- test/spec/modules/trustxBidAdapter_spec.js | 44 +++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 9f8de30a0d4..247a1f6d048 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -179,7 +179,10 @@ function _addBidResponse(serverBid, bidRequest, bidResponses, RendererConst) { currency: 'USD', netRevenue: newFormat ? false : priceType !== 'gross', ttl: TIME_TO_LIVE, - dealId: serverBid.dealid + dealId: serverBid.dealid, + meta: { + advertiserDomains: serverBid.adomain ? serverBid.adomain : [] + }, }; if (serverBid.content_type === 'video') { bidResponse.vastXml = serverBid.adm; diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index c0155f87ab6..f53116f60a4 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -661,7 +661,7 @@ describe('TrustXAdapter', function () { describe('interpretResponse', function () { const responses = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 43, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 43, 'h': 250, 'w': 300, 'adomain': ['somedomain.com']}], 'seat': '1'}, {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 44, 'h': 600, 'w': 300}], 'seat': '1'}, {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 43, 'h': 90, 'w': 728}], 'seat': '1'}, {'bid': [{'price': 0, 'auid': 45, 'h': 250, 'w': 300}], 'seat': '1'}, @@ -699,6 +699,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': ['somedomain.com'] + }, } ]; @@ -756,6 +759,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': ['somedomain.com'] + }, }, { 'requestId': '4dff80cc4ee346', @@ -769,6 +775,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '5703af74d0472a', @@ -782,6 +791,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, } ]; @@ -909,6 +921,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '4e111f1b66e4', @@ -922,6 +937,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '26d6f897b516', @@ -935,6 +953,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '326bde7fbf69', @@ -948,6 +969,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, } ]; @@ -1009,6 +1033,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '57b2ebe70e16', @@ -1022,6 +1049,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, } ]; @@ -1082,6 +1112,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'video', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, 'vastXml': '\n<\/Ad>\n<\/VAST>', 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' @@ -1161,6 +1194,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'video', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, 'vastXml': '\n<\/Ad>\n<\/VAST>', 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' @@ -1178,6 +1214,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'video', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, 'vastXml': '\n<\/Ad>\n<\/VAST>', 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' @@ -1195,6 +1234,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'video', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, 'vastXml': '\n<\/Ad>\n<\/VAST>', 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>'