diff --git a/integrationExamples/gpt/pbjs_example_gpt.html b/integrationExamples/gpt/pbjs_example_gpt.html index 07e5bb8236f..1493634c1c7 100644 --- a/integrationExamples/gpt/pbjs_example_gpt.html +++ b/integrationExamples/gpt/pbjs_example_gpt.html @@ -269,6 +269,12 @@ placement_id: 0 } }, + { + bidder: 'pollux', + params: { + zone: '1806' // REQUIRED Zone Id (1806 is a test zone) + } + }, { bidder: 'adkernelAdn', params: { @@ -395,6 +401,12 @@ params: { placement_id: 0 } + }, + { + bidder: 'pollux', + params: { + zone: '276' // REQUIRED Zone Id (276 is a test zone) + } } ] } diff --git a/integrationExamples/gpt/pollux_zone_728x90.html b/integrationExamples/gpt/pollux_example.html similarity index 51% rename from integrationExamples/gpt/pollux_zone_728x90.html rename to integrationExamples/gpt/pollux_example.html index ecede9b5db2..56eedbf2a9c 100644 --- a/integrationExamples/gpt/pollux_zone_728x90.html +++ b/integrationExamples/gpt/pollux_example.html @@ -7,31 +7,40 @@ var PREBID_TIMEOUT = 3000; var adUnits = [{ - code: 'div-gpt-ad-1460505661639-0', - sizes: [[728, 90]], - bids: [ - { - bidder: 'pollux', - params: { - zone: '276' - } - }, - { - bidder: 'pollux', - params: { - zone: '1806' - } - } - ] - }]; - + code: 'div-gpt-ad-1460505661639-0', + sizes: [[728, 90], [300, 250]], + bids: [{ + bidder: 'pollux', + params: { + zone: '1806,276' + } + }, { + bidder: 'pollux', + params: { + zone: '276' + } + } + ] + }, + { + code: 'div-gpt-ad-1460505661631-0', + sizes: [[300, 250]], + bids: [{ + bidder: 'pollux', + params: { + zone: '1806,276,855' + } + } + ] + } + ]; var pbjs = pbjs || {}; pbjs.que = pbjs.que || []; - + @@ -79,7 +97,8 @@ + +
+
+ +
+ +
\ No newline at end of file diff --git a/modules/polluxBidAdapter.js b/modules/polluxBidAdapter.js index 54c2122ec36..463de07341c 100644 --- a/modules/polluxBidAdapter.js +++ b/modules/polluxBidAdapter.js @@ -1,97 +1,120 @@ -import bidfactory from 'src/bidfactory'; -import bidmanager from 'src/bidmanager'; import * as utils from 'src/utils'; -import adloader from 'src/adloader'; -import adaptermanager from 'src/adaptermanager'; -import { STATUS } from 'src/constants'; +import { registerBidder } from 'src/adapters/bidderFactory'; -// Prebid adapter for Pollux header bidding client -function PolluxBidAdapter() { - function _callBids(params) { - var bidderUrl = (window.location.protocol) + '//adn.plxnt.com/prebid'; - var bids = params.bids || []; - for (var i = 0; i < bids.length; i++) { - var request_obj = {}; - var bid = bids[i]; - // check params - if (bid.params.zone) { - var domain = utils.getParameterByName('domain'); - var tracker2 = utils.getParameterByName('tracker2'); - if (domain) { - request_obj.domain = domain; - } else { - request_obj.domain = window.location.host; - } - if (tracker2) { - request_obj.tracker2 = tracker2; - } - request_obj.zone = bid.params.zone; - } else { - utils.logError('required param "zone" is missing', 'polluxHandler'); - continue; - } - var parsedSizes = utils.parseSizesInput(bid.sizes); - var parsedSizesLength = parsedSizes.length; - if (parsedSizesLength > 0) { - // first value should be "size" - request_obj.size = parsedSizes[0]; - if (parsedSizesLength > 1) { - // any subsequent values should be "promo_sizes" - var promo_sizes = []; - for (var j = 1; j < parsedSizesLength; j++) { - promo_sizes.push(parsedSizes[j]); - } - request_obj.promo_sizes = promo_sizes.join(','); - } - } - // detect urls - request_obj.callback_id = bid.bidId; - // set a different url bidder - if (bid.bidderUrl) { - bidderUrl = bid.bidderUrl; +const BIDDER_CODE = 'pollux'; +const PLX_ENDPOINT_URL = '//adn.plxnt.com/prebid/v1'; +const PLX_CURRENCY = 'EUR'; +const PLX_TTL = 3600; +const PLX_NETREVENUE = true; + +export const spec = { + code: BIDDER_CODE, + aliases: ['plx'], + /** + * 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) { + if (!bid.hasOwnProperty('params') || !bid.params.hasOwnProperty('zone')) { + utils.logError('required param "zone" is missing for == ' + BIDDER_CODE + ' =='); + return false; + } + return true; + }, + /** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests) { + if (!Array.isArray(validBidRequests) || !validBidRequests.length) { + return []; + } + const payload = []; + let custom_url = null; + for (let i = 0; i < validBidRequests.length; i++) { + const bid = validBidRequests[i]; + const request = { + bidId: bid.bidId, + zones: bid.params.zone, + sizes: bid.sizes + }; + if (bid.bidderUrl && !custom_url) { + custom_url = bid.bidderUrl; } - var prebidUrl = bidderUrl + '?' + utils.parseQueryStringParameters(request_obj); - utils.logMessage('Pollux request built: ' + prebidUrl); - adloader.loadScript(prebidUrl, null, true); + payload.push(request); } - } - - // expose the callback to global object - function _polluxHandler (response) { - // pollux handler - var bidObject = {}; - var callback_id = response.callback_id; - var placementCode = ''; - var bidObj = utils.getBidRequest(callback_id); - if (bidObj) { - placementCode = bidObj.placementCode; + const payloadString = JSON.stringify(payload); + // build url parameters + const domain = utils.getParameterByName('domain'); + const tracker2 = utils.getParameterByName('tracker2'); + const url_params = {}; + if (domain) { + url_params.domain = domain; + } else { + url_params.domain = utils.getTopWindowUrl(); + } + if (tracker2) { + url_params.tracker2 = tracker2; + } + // build url + let bidder_url = custom_url || PLX_ENDPOINT_URL; + if (url_params) { + bidder_url = bidder_url + '?' + utils.parseQueryStringParameters(url_params); + } + utils.logMessage('== ' + BIDDER_CODE + ' == request built: ' + bidder_url); + return { + method: 'POST', + url: bidder_url, + data: payloadString + }; + }, + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse, bidRequest) { + let bidResponses = []; + if (!serverResponse || (typeof serverResponse === 'object' && !serverResponse.hasOwnProperty('body'))) { + utils.logMessage('No prebid response from == ' + BIDDER_CODE + ' == for bid requests:'); + utils.logMessage(bidRequest); + return bidResponses; } - if (bidObj && response.cpm > 0 && !!response.ad) { - bidObject = bidfactory.createBid(STATUS.GOOD, bidObj); - bidObject.bidderCode = bidObj.bidder; - bidObject.mediaType = response.mediaType; - bidObject.cpm = parseFloat(response.cpm); - if (response.ad_type === 'url') { - bidObject.adUrl = response.ad; + serverResponse = serverResponse.body; + if (!Array.isArray(serverResponse) || !serverResponse.length) { + utils.logMessage('No prebid response from == ' + BIDDER_CODE + ' == for bid requests:'); + utils.logMessage(bidRequest); + return bidResponses; + } + // loop through serverResponses + for (let b in serverResponse) { + let bid = serverResponse[b]; + const bidResponse = { + requestId: bid.bidId, // not request id, it's bid's id + cpm: parseFloat(bid.cpm), + width: parseInt(bid.width), + height: parseInt(bid.height), + ttl: PLX_TTL, + creativeId: bid.creativeId, + netRevenue: PLX_NETREVENUE, + currency: PLX_CURRENCY + }; + if (bid.ad_type === 'url') { + bidResponse.adUrl = bid.ad; } else { - bidObject.ad = response.ad; + bidResponse.ad = bid.ad; } - bidObject.width = response.width; - bidObject.height = response.height; - } else { - bidObject = bidfactory.createBid(STATUS.NO_BID, bidObj); - bidObject.bidderCode = 'pollux'; - utils.logMessage('No prebid response from polluxHandler for placement code ' + placementCode); + if (bid.referrer) { + bidResponse.referrer = bid.referrer; + } + bidResponses.push(bidResponse); } - bidmanager.addBidResponse(placementCode, bidObject); - }; - $$PREBID_GLOBAL$$.polluxHandler = _polluxHandler; - // Export the `callBids` function, so that Prebid.js can execute - // this function when the page asks to send out bid requests. - return { - callBids: _callBids, - polluxHandler: _polluxHandler - }; + return bidResponses; + } }; -adaptermanager.registerBidAdapter(new PolluxBidAdapter(), 'pollux'); -module.exports = PolluxBidAdapter; +registerBidder(spec); diff --git a/modules/polluxBidAdapter.md b/modules/polluxBidAdapter.md new file mode 100644 index 00000000000..79bf84e79b9 --- /dev/null +++ b/modules/polluxBidAdapter.md @@ -0,0 +1,33 @@ +# Overview + +**Module Name**: Pollux Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: tech@polluxnetwork.com + +# Description + +Module that connects to Pollux Network LLC demand source to fetch bids. +All bids will present CPM in EUR (Euro). + +# Test Parameters +``` + var adUnits = [{ + code: '34f724kh32', + sizes: [[300, 250]], // a single size + bids: [{ + bidder: 'pollux', + params: { + zone: '1806' // a single zone + } + }] + },{ + code: '34f789r783', + sizes: [[300, 250], [728, 90]], // multiple sizes + bids: [{ + bidder: 'pollux', + params: { + zone: '1806,276' // multiple zones, max 5 + } + }] + }]; +``` diff --git a/test/spec/modules/polluxBidAdapter_spec.js b/test/spec/modules/polluxBidAdapter_spec.js index 1bcfe28124d..ea550fecd71 100644 --- a/test/spec/modules/polluxBidAdapter_spec.js +++ b/test/spec/modules/polluxBidAdapter_spec.js @@ -1,172 +1,207 @@ -describe('Pollux Bid Adapter tests', function () { - var expect = require('chai').expect; - var urlParse = require('url-parse'); - var querystringify = require('querystringify'); - var Adapter = require('modules/polluxBidAdapter'); - var adLoader = require('src/adloader'); - var bidmanager = require('src/bidmanager'); - var utils = require('src/utils'); - - var stubLoadScript; - var stubAddBidResponse; - var polluxAdapter; - - // mock golbal _bidsRequested var - var bidsRequested = []; - utils.getBidRequest = function (id) { - return bidsRequested.map(bidSet => bidSet.bids.find(bid => bid.bidId === id)).find(bid => bid); - }; - - beforeEach(function () { - polluxAdapter = new Adapter(); - bidsRequested = []; - stubLoadScript = sinon.stub(adLoader, 'loadScript'); - stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); +import {expect} from 'chai'; +import {spec} from 'modules/polluxBidAdapter'; +import {utils} from 'src/utils'; +import {newBidder} from 'src/adapters/bidderFactory'; + +describe('POLLUX Bid Adapter tests', function () { + // ad units setup + const setup_single_bid = [{ + placementCode: 'div-gpt-ad-1460505661587-0', + bidId: '789s6354sfg856', + bidderUrl: '//adn.polluxnetwork.com/prebid/v1', + sizes: [[728, 90], [300, 250]], + params: {zone: '1806,276'} + }]; + const setup_multi_bid = [{ + placementCode: 'div-gpt-ad-1460505661639-0', + bidId: '21fe992ca48d55', + sizes: [[300, 250]], + params: {zone: '1806'} + }, { + placementCode: 'div-gpt-ad-1460505661812-0', + bidId: '23kljh54390534', + sizes: [[728, 90]], + params: {zone: '276'} + }]; + + it('TEST: verify buildRequests no valid bid requests', () => { + let request = spec.buildRequests(false); + expect(request).to.not.equal(null); + expect(request).to.not.have.property('method'); + expect(request).to.not.have.property('url'); + expect(request).to.not.have.property('data'); + request = spec.buildRequests([]); + expect(request).to.not.equal(null); + expect(request).to.not.have.property('method'); + expect(request).to.not.have.property('url'); + expect(request).to.not.have.property('data'); + request = spec.buildRequests({}); + expect(request).to.not.equal(null); + expect(request).to.not.have.property('method'); + expect(request).to.not.have.property('url'); + expect(request).to.not.have.property('data'); + request = spec.buildRequests(null); + expect(request).to.not.equal(null); + expect(request).to.not.have.property('method'); + expect(request).to.not.have.property('url'); + expect(request).to.not.have.property('data'); }); - afterEach(function () { - stubLoadScript.restore(); - stubAddBidResponse.restore(); + it('TEST: verify buildRequests single bid', () => { + const request = spec.buildRequests(setup_single_bid); + expect(request.method).to.equal('POST'); + const requested_bids = JSON.parse(request.data); + // bids request + expect(requested_bids).to.not.equal(null); + expect(requested_bids).to.have.lengthOf(1); + // bid objects + expect(requested_bids[0]).to.not.equal(null); + expect(requested_bids[0]).to.have.property('bidId'); + expect(requested_bids[0]).to.have.property('sizes'); + expect(requested_bids[0]).to.have.property('zones'); + // bid 0 + expect(requested_bids[0].bidId).to.equal('789s6354sfg856'); + expect(requested_bids[0].sizes).to.not.equal(null); + expect(requested_bids[0].sizes).to.have.lengthOf(2); + expect(requested_bids[0].sizes[0][0]).to.equal(728); + expect(requested_bids[0].sizes[0][1]).to.equal(90); + expect(requested_bids[0].sizes[1][0]).to.equal(300); + expect(requested_bids[0].sizes[1][1]).to.equal(250); + expect(requested_bids[0].zones).to.equal('1806,276'); }); - describe('creation of bid url', function () { - it('bid request for single placement', function () { - var params = { - bidderCode: 'pollux', - bids: [{ - placementCode: 'div-gpt-ad-1460505661639-0', - bidId: '21fe992ca48d55', - bidder: 'pollux', - sizes: [[300, 250]], - params: { zone: '1806' } - }] - }; - - polluxAdapter.callBids(params); - - var bidUrl = stubLoadScript.getCall(0).args[0]; - - sinon.assert.calledOnce(stubLoadScript); - - var parsedBidUrl = urlParse(bidUrl); - var parsedBidUrlQueryString = querystringify.parse(parsedBidUrl.query); - - expect(parsedBidUrlQueryString).to.have.property('zone').and.to.equal('1806'); - expect(parsedBidUrlQueryString).to.have.property('domain').and.to.have.length.above(1); - }); + it('TEST: verify buildRequests multi bid', () => { + const request = spec.buildRequests(setup_multi_bid); + expect(request.method).to.equal('POST'); + const requested_bids = JSON.parse(request.data); + // bids request + expect(requested_bids).to.not.equal(null); + expect(requested_bids).to.have.lengthOf(2); + // bid objects + expect(requested_bids[0]).to.not.equal(null); + expect(requested_bids[0]).to.have.property('bidId'); + expect(requested_bids[0]).to.have.property('sizes'); + expect(requested_bids[0]).to.have.property('zones'); + expect(requested_bids[1]).to.not.equal(null); + expect(requested_bids[1]).to.have.property('bidId'); + expect(requested_bids[1]).to.have.property('sizes'); + expect(requested_bids[1]).to.have.property('zones'); + // bid 0 + expect(requested_bids[0].bidId).to.equal('21fe992ca48d55'); + expect(requested_bids[0].sizes).to.not.equal(null); + expect(requested_bids[0].sizes).to.have.lengthOf(1); + expect(requested_bids[0].sizes[0][0]).to.equal(300); + expect(requested_bids[0].sizes[0][1]).to.equal(250); + expect(requested_bids[0].zones).to.equal('1806'); + // bid 1 + expect(requested_bids[1].bidId).to.equal('23kljh54390534'); + expect(requested_bids[1].sizes).to.not.equal(null); + expect(requested_bids[1].sizes).to.have.lengthOf(1); + expect(requested_bids[1].sizes[0][0]).to.equal(728); + expect(requested_bids[1].sizes[0][1]).to.equal(90); + expect(requested_bids[1].zones).to.equal('276'); }); - describe('handling bid response', function () { - it('should return complete bid response adUrl', function() { - var params = { - bidderCode: 'pollux', - bids: [{ - placementCode: 'div-gpt-ad-1460505661639-0', - sizes: [[300, 250]], - bidId: '21fe992ca48d55', - bidder: 'pollux', - params: { zone: '1806' } - }] - }; - - var response = { - cpm: 0.5, - width: 300, - height: 250, - callback_id: '21fe992ca48d55', - ad: 'some.ad.url', - ad_type: 'url', - zone: 1806 - }; - - polluxAdapter.callBids(params); - bidsRequested.push(params); - polluxAdapter.polluxHandler(response); - - sinon.assert.calledOnce(stubAddBidResponse); - - var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; - var bidObject1 = stubAddBidResponse.getCall(0).args[1]; - - expect(bidPlacementCode1).to.equal('div-gpt-ad-1460505661639-0'); - expect(bidObject1.bidderCode).to.equal('pollux'); - expect(bidObject1.cpm).to.equal(0.5); - expect(bidObject1.width).to.equal(300); - expect(bidObject1.height).to.equal(250); - expect(bidObject1.adUrl).to.have.length.above(1); - }); - - it('should return complete bid response ad (html)', function() { - var params = { - bidderCode: 'pollux', - bids: [{ - placementCode: 'div-gpt-ad-1460505661639-0', - sizes: [[300, 250]], - bidId: '21fe992ca48d55', - bidder: 'pollux', - params: { zone: '1806' } - }] - }; - - var response = { - cpm: 0.5, - width: 300, - height: 250, - callback_id: '21fe992ca48d55', - ad: '', - ad_type: 'html', - zone: 1806 - }; - - polluxAdapter.callBids(params); - bidsRequested.push(params); - polluxAdapter.polluxHandler(response); - - sinon.assert.calledOnce(stubAddBidResponse); - - var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; - var bidObject1 = stubAddBidResponse.getCall(0).args[1]; - - expect(bidPlacementCode1).to.equal('div-gpt-ad-1460505661639-0'); - expect(bidObject1.bidderCode).to.equal('pollux'); - expect(bidObject1.cpm).to.equal(0.5); - expect(bidObject1.width).to.equal(300); - expect(bidObject1.height).to.equal(250); - expect(bidObject1.ad).to.have.length.above(1); - }); + it('TEST: verify interpretResponse empty', () => { + let bids = spec.interpretResponse(false, {}); + expect(bids).to.not.equal(null); + expect(bids).to.have.lengthOf(0); + bids = spec.interpretResponse([], {}); + expect(bids).to.not.equal(null); + expect(bids).to.have.lengthOf(0); + bids = spec.interpretResponse({}, {}); + expect(bids).to.not.equal(null); + expect(bids).to.have.lengthOf(0); + bids = spec.interpretResponse(null, {}); + expect(bids).to.not.equal(null); + expect(bids).to.have.lengthOf(0); + }); - it('should return no bid response', function() { - var params = { - bidderCode: 'pollux', - bids: [{ - placementCode: 'div-gpt-ad-1460505661639-0', - sizes: [[300, 250]], - bidId: '21fe992ca48d55', - bidder: 'pollux', - params: { zone: '276' } - }] - }; + it('TEST: verify interpretResponse ad_type url', () => { + const serverResponse = { + body: [ + { + bidId: '789s6354sfg856', + cpm: '2.15', + width: '728', + height: '90', + ad: 'http://adn.polluxnetwork.com/zone/276?_plx_prebid=1&_plx_campaign=1125', + ad_type: 'url', + creativeId: '1125', + referrer: 'http://www.example.com' + } + ] + }; + const bids = spec.interpretResponse(serverResponse, {}); + expect(bids).to.have.lengthOf(1); + expect(bids[0].requestId).to.equal('789s6354sfg856'); + expect(bids[0].cpm).to.equal(2.15); + expect(bids[0].width).to.equal(728); + expect(bids[0].height).to.equal(90); + expect(bids[0].ttl).to.equal(3600); + expect(bids[0].creativeId).to.equal('1125'); + expect(bids[0].netRevenue).to.equal(true); + expect(bids[0].currency).to.equal('EUR'); + expect(bids[0].referrer).to.equal('http://www.example.com'); + expect(bids[0].adUrl).to.equal('http://adn.polluxnetwork.com/zone/276?_plx_prebid=1&_plx_campaign=1125'); + expect(bids[0]).to.not.have.property('ad'); + }); - var response = { - cpm: null, - width: null, - height: null, - callback_id: null, - ad: null, - zone: null - }; + it('TEST: verify interpretResponse ad_type html', () => { + const serverResponse = { + body: [ + { + bidId: '789s6354sfg856', + cpm: '2.15', + width: '728', + height: '90', + ad: '

I am an ad

', + ad_type: 'html', + creativeId: '1125' + } + ] + }; + const bids = spec.interpretResponse(serverResponse, {}); + expect(bids).to.have.lengthOf(1); + expect(bids[0].requestId).to.equal('789s6354sfg856'); + expect(bids[0].cpm).to.equal(2.15); + expect(bids[0].width).to.equal(728); + expect(bids[0].height).to.equal(90); + expect(bids[0].ttl).to.equal(3600); + expect(bids[0].creativeId).to.equal('1125'); + expect(bids[0].netRevenue).to.equal(true); + expect(bids[0].currency).to.equal('EUR'); + expect(bids[0]).to.not.have.property('referrer'); + expect(bids[0]).to.not.have.property('adUrl'); + expect(bids[0].ad).to.equal('

I am an ad

'); + }); - polluxAdapter.callBids(params); - bidsRequested.push(params); - polluxAdapter.polluxHandler(response); + it('TEST: verify url and query params', () => { + const URL = require('url-parse'); + const querystringify = require('querystringify'); + const request = spec.buildRequests(setup_single_bid); + const parsedUrl = new URL('https:' + request.url); + expect(parsedUrl.origin).to.equal('https://adn.polluxnetwork.com'); + expect(parsedUrl.pathname).to.equal('/prebid/v1'); + expect(parsedUrl).to.have.property('query'); + const parsedQuery = querystringify.parse(parsedUrl.query); + expect(parsedQuery).to.have.property('domain').and.to.have.length.above(1); + }); - sinon.assert.calledOnce(stubAddBidResponse); + it('TEST: verify isBidRequestValid', () => { + expect(spec.isBidRequestValid({})).to.equal(false); + expect(spec.isBidRequestValid({params: {}})).to.equal(false); + expect(spec.isBidRequestValid(setup_single_bid[0])).to.equal(true); + expect(spec.isBidRequestValid(setup_multi_bid[0])).to.equal(true); + expect(spec.isBidRequestValid(setup_multi_bid[1])).to.equal(true); + }); - var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; - var bidObject1 = stubAddBidResponse.getCall(0).args[1]; + it('TEST: verify bidder code', () => { + expect(spec.code).to.equal('pollux'); + }); - expect(bidPlacementCode1).to.equal(''); - expect(bidObject1.bidderCode).to.equal('pollux'); - }); + it('TEST: verify bidder aliases', () => { + expect(spec.aliases).to.have.lengthOf(1); + expect(spec.aliases[0]).to.equal('plx'); }); });