Skip to content

Commit

Permalink
Yieldmo bid adapter (#1415)
Browse files Browse the repository at this point in the history
* Create yieldmoBidAdapter file

* Copy over TL adapter and change name spacing

* Update bid adapter naming to Yieldmo

* Init tests for yieldmoBidAdapter

* Add yeildmo config to pbjs_example_gpt

* feature/yieldmoBidAdapter: added request build

* Update ym response to have placement and impression level info

* Remove environment param

* Update ym tests for single bid request structure

* Update url to use _ instead of camelcase

* Update YMCB to handle multiple placements in one response

* Clean up and remove TODO from adapter

* Update response tests to work with single response structure

* Remove trailing space

* Cleanup

* Adjusted code according to CR comments

* Update bid adapter with fallback for incorrect bid response

* Update yeildmo prebid endpoint

* Add check to make sure response is an Array

* Remove environment tracking as we are no going to use this

* Update test to pass with new Yieldmo endpoint

* Update return obj and remove node debug comments

* Remove changes to hello_world and yarn.lock files

* Remove unneeded comments
  • Loading branch information
xlozinguez authored and jaiminpanchal27 committed Sep 8, 2017
1 parent f57f337 commit 598817f
Show file tree
Hide file tree
Showing 3 changed files with 360 additions and 0 deletions.
4 changes: 4 additions & 0 deletions integrationExamples/gpt/pbjs_example_gpt.html
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,10 @@
floor: 1.50 // Dynamic bid floor - optional
}
},
{
bidder: 'yieldmo',
params: {}
},
{
bidder: 'adequant',
params: {
Expand Down
147 changes: 147 additions & 0 deletions modules/yieldmoBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
var utils = require('src/utils.js');
var adloader = require('src/adloader.js');
var bidmanager = require('src/bidmanager.js');
var bidfactory = require('src/bidfactory.js');
var adaptermanager = require('src/adaptermanager');

/**
* Adapter for requesting bids from Yieldmo.
*
* @returns {{callBids: _callBids}}
* @constructor
*/

var YieldmoAdapter = function YieldmoAdapter() {
function _callBids(params) {
var bids = params.bids;
adloader.loadScript(buildYieldmoCall(bids));
}

function buildYieldmoCall(bids) {
// build our base tag, based on if we are http or https
var ymURI = '//bid.yieldmo.com/exchange/prebid?';
var ymCall = document.location.protocol + ymURI;

// Placement specific information
ymCall = _appendPlacementInformation(ymCall, bids);

// General impression params
ymCall = _appendImpressionInformation(ymCall);

// remove the trailing "&"
if (ymCall.lastIndexOf('&') === ymCall.length - 1) {
ymCall = ymCall.substring(0, ymCall.length - 1);
}

utils.logMessage('ymCall request built: ' + ymCall);

return ymCall;
}

function _appendPlacementInformation(url, bids) {
var placements = [];
var placement;
var bid;

for (var i = 0; i < bids.length; i++) {
bid = bids[i];

placement = {};
placement.callback_id = bid.bidId;
placement.placement_id = bid.placementCode;
placement.sizes = bid.sizes;

placements.push(placement);
}

url = utils.tryAppendQueryString(url, 'p', JSON.stringify(placements));
return url;
}

function _appendImpressionInformation(url) {
var page_url = document.location; // page url
var pr = document.referrer || ''; // page's referrer
var dnt = (navigator.doNotTrack || false).toString(); // true if user enabled dnt (false by default)
var _s = document.location.protocol === 'https:' ? 1 : 0; // 1 if page is secure
var description = _getPageDescription();
var title = document.title || ''; // Value of the title from the publisher's page.
var bust = new Date().getTime().toString(); // cache buster
var scrd = window.devicePixelRatio || 0; // screen pixel density

url = utils.tryAppendQueryString(url, 'callback', '$$PREBID_GLOBAL$$.YMCB');
url = utils.tryAppendQueryString(url, 'page_url', page_url);
url = utils.tryAppendQueryString(url, 'pr', pr);
url = utils.tryAppendQueryString(url, 'bust', bust);
url = utils.tryAppendQueryString(url, '_s', _s);
url = utils.tryAppendQueryString(url, 'scrd', scrd);
url = utils.tryAppendQueryString(url, 'dnt', dnt);
url = utils.tryAppendQueryString(url, 'description', description);
url = utils.tryAppendQueryString(url, 'title', title);

return url;
}

function _getPageDescription() {
if (document.querySelector('meta[name="description"]')) {
return document.querySelector('meta[name="description"]').getAttribute('content'); // Value of the description metadata from the publisher's page.
} else {
return '';
}
}

// expose the callback to the global object:
$$PREBID_GLOBAL$$.YMCB = function(ymResponses) {
if (ymResponses && ymResponses.constructor === Array && ymResponses.length > 0) {
for (var i = 0; i < ymResponses.length; i++) {
_registerPlacementBid(ymResponses[i]);
}
} else {
// If an incorrect response is returned, register error bids for all placements
// to prevent Prebid waiting till timeout for response
_registerNoResponseBids();

utils.logMessage('No prebid response for placement %%PLACEMENT%%');
}
};

function _registerPlacementBid(response) {
var bidObj = utils.getBidRequest(response.callback_id);
var placementCode = bidObj && bidObj.placementCode;
var bid = [];

if (response && response.cpm && response.cpm !== 0) {
bid = bidfactory.createBid(1, bidObj);
bid.bidderCode = 'yieldmo';
bid.cpm = response.cpm;
bid.ad = response.ad;
bid.width = response.width;
bid.height = response.height;
bidmanager.addBidResponse(placementCode, bid);
} else {
// no response data
if (bidObj) { utils.logMessage('No prebid response from yieldmo for placementCode: ' + bidObj.placementCode); }
bid = bidfactory.createBid(2, bidObj);
bid.bidderCode = 'yieldmo';
bidmanager.addBidResponse(placementCode, bid);
}
}

function _registerNoResponseBids() {
var yieldmoBidRequests = $$PREBID_GLOBAL$$._bidsRequested.find(bid => bid.bidderCode === 'yieldmo');

utils._each(yieldmoBidRequests.bids, function (currentBid) {
var bid = [];
bid = bidfactory.createBid(2, currentBid);
bid.bidderCode = 'yieldmo';
bidmanager.addBidResponse(currentBid.placementCode, bid);
});
}

return Object.assign(this, {
callBids: _callBids
});
};

adaptermanager.registerBidAdapter(new YieldmoAdapter(), 'yieldmo');

module.exports = YieldmoAdapter;
209 changes: 209 additions & 0 deletions test/spec/modules/yieldmoBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import {expect} from 'chai';
import Adapter from '../../../modules/yieldmoBidAdapter';
import bidManager from '../../../src/bidmanager';
import adLoader from '../../../src/adloader';
import {parse as parseURL} from '../../../src/url';

describe('Yieldmo adapter', () => {
let bidsRequestedOriginal;
let adapter;
let sandbox;

const bidderRequest = {
bidderCode: 'yieldmo',
bids: [
{
bidId: 'bidId1',
bidder: 'yieldmo',
placementCode: 'foo',
sizes: [[728, 90]]
},
{
bidId: 'bidId2',
bidder: 'yieldmo',
placementCode: 'bar',
sizes: [[300, 600], [300, 250]]
}
]
};

beforeEach(() => {
bidsRequestedOriginal = $$PREBID_GLOBAL$$._bidsRequested;
$$PREBID_GLOBAL$$._bidsRequested = [];

adapter = new Adapter();
sandbox = sinon.sandbox.create();
});

afterEach(() => {
sandbox.restore();

$$PREBID_GLOBAL$$._bidsRequested = bidsRequestedOriginal;
});

describe('callBids', () => {
let bidRequestURL;

beforeEach(() => {
sandbox.stub(adLoader, 'loadScript');
adapter.callBids(bidderRequest);

bidRequestURL = adLoader.loadScript.firstCall.args[0];
});

it('should load a script with passed bid params', () => {
let route = 'http://bid.yieldmo.com/exchange/prebid?';
let requestParams = parseURL(bidRequestURL).search;
let parsedPlacementParams = JSON.parse(decodeURIComponent(requestParams.p));

sinon.assert.calledOnce(adLoader.loadScript);
expect(bidRequestURL).to.contain(route);

// placement 1
expect(parsedPlacementParams[0]).to.have.property('callback_id', 'bidId1');
expect(parsedPlacementParams[0]).to.have.property('placement_id', 'foo');
expect(parsedPlacementParams[0].sizes[0][0]).to.equal(728);
expect(parsedPlacementParams[0].sizes[0][1]).to.equal(90);

// placement 2
expect(parsedPlacementParams[1]).to.have.property('callback_id', 'bidId2');
expect(parsedPlacementParams[1]).to.have.property('placement_id', 'bar');
expect(parsedPlacementParams[1].sizes[0][0]).to.equal(300);
expect(parsedPlacementParams[1].sizes[0][1]).to.equal(600);
expect(parsedPlacementParams[1].sizes[1][0]).to.equal(300);
expect(parsedPlacementParams[1].sizes[1][1]).to.equal(250);

// impression information
expect(requestParams).to.have.property('callback', '$$PREBID_GLOBAL$$.YMCB');
expect(requestParams).to.have.property('page_url');
});
});

describe('YMCB', () => {
it('should exist and be a function', () => {
expect($$PREBID_GLOBAL$$.YMCB).to.exist.and.to.be.a('function');
});
});

describe('add bids to the manager', () => {
let firstBid;
let secondBid;

beforeEach(() => {
sandbox.stub(bidManager, 'addBidResponse');

$$PREBID_GLOBAL$$._bidsRequested.push(bidderRequest);

// respond
let bidderReponse = [{
'cpm': 3.45455,
'width': 300,
'height': 250,
'callback_id': 'bidId1',
'ad': '<html><head></head><body>HELLO YIELDMO AD</body></html>'
}, {
'cpm': 4.35455,
'width': 400,
'height': 350,
'callback_id': 'bidId2',
'ad': '<html><head></head><body>HELLO YIELDMO AD</body></html>'
}];

$$PREBID_GLOBAL$$.YMCB(bidderReponse);

firstBid = bidManager.addBidResponse.firstCall.args[1];
secondBid = bidManager.addBidResponse.secondCall.args[1];
});

it('should add a bid object for each bid', () => {
sinon.assert.calledTwice(bidManager.addBidResponse);
});

it('should pass the correct placement code as first param', () => {
let firstPlacementCode = bidManager.addBidResponse.firstCall.args[0];
let secondPlacementCode = bidManager.addBidResponse.secondCall.args[0];

expect(firstPlacementCode).to.eql('foo');
expect(secondPlacementCode).to.eql('bar');
});

it('should have a good statusCode', () => {
expect(firstBid.getStatusCode()).to.eql(1);
expect(secondBid.getStatusCode()).to.eql(1);
});

it('should add the CPM to the bid object', () => {
expect(firstBid).to.have.property('cpm', 3.45455);
expect(secondBid).to.have.property('cpm', 4.35455);
});

it('should add the bidder code to the bid object', () => {
expect(firstBid).to.have.property('bidderCode', 'yieldmo');
expect(secondBid).to.have.property('bidderCode', 'yieldmo');
});

it('should include the ad on the bid object', () => {
expect(firstBid).to.have.property('ad');
expect(secondBid).to.have.property('ad');
});

it('should include the size on the bid object', () => {
expect(firstBid).to.have.property('width', 300);
expect(firstBid).to.have.property('height', 250);
expect(secondBid).to.have.property('width', 400);
expect(secondBid).to.have.property('height', 350);
});
});

describe('add empty bids if no bid returned', () => {
let firstBid;
let secondBid;

beforeEach(() => {
sandbox.stub(bidManager, 'addBidResponse');

$$PREBID_GLOBAL$$._bidsRequested.push(bidderRequest);

// respond
let bidderReponse = [{
'status': 'no_bid',
'callback_id': 'bidId1'
}, {
'status': 'no_bid',
'callback_id': 'bidId2'
}];

$$PREBID_GLOBAL$$.YMCB(bidderReponse);

firstBid = bidManager.addBidResponse.firstCall.args[1];
secondBid = bidManager.addBidResponse.secondCall.args[1];
});

it('should add a bid object for each bid', () => {
sinon.assert.calledTwice(bidManager.addBidResponse);
});

it('should include the bid request bidId as the adId', () => {
expect(firstBid).to.have.property('adId', 'bidId1');
expect(secondBid).to.have.property('adId', 'bidId2');
});

it('should have an error statusCode', () => {
expect(firstBid.getStatusCode()).to.eql(2);
expect(secondBid.getStatusCode()).to.eql(2);
});

it('should pass the correct placement code as first param', () => {
let firstPlacementCode = bidManager.addBidResponse.firstCall.args[0];
let secondPlacementCode = bidManager.addBidResponse.secondCall.args[0];

expect(firstPlacementCode).to.eql('foo');
expect(secondPlacementCode).to.eql('bar');
});

it('should add the bidder code to the bid object', () => {
expect(firstBid).to.have.property('bidderCode', 'yieldmo');
expect(secondBid).to.have.property('bidderCode', 'yieldmo');
});
});
});

0 comments on commit 598817f

Please sign in to comment.