From 031fa560069330891e49edb04d513a3a127f1c8a Mon Sep 17 00:00:00 2001 From: Paul Wyrembak Date: Tue, 15 Aug 2017 15:43:08 +0300 Subject: [PATCH 01/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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; }); });