Skip to content

Commit

Permalink
EngageBDR legacy bid adapter (#2329)
Browse files Browse the repository at this point in the history
* ebdrBidAdatper

* Delete engage_test.html

* fix Superfluous trailing arguments issue

* add Legacy ebdr adapter test case

* add Legacy ebdr adapter test case
  • Loading branch information
jlzhangdev authored and jsnellbaker committed Apr 11, 2018
1 parent 410eacc commit aa32519
Show file tree
Hide file tree
Showing 3 changed files with 361 additions and 0 deletions.
183 changes: 183 additions & 0 deletions modules/ebdrBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
var CONSTANTS = require('src/constants.json');
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 defaultPlacementForBadBid = null;

/**
* Adapter for requesting bids from Ebdr
*/
var EbdrAdapter = function EbdrAdapter() {
var rtbServerDomain = 'dsp.bnmla.com';
var zoneid;
function _callBids(params) {
var ebdrBids = params.bids || [];
_requestBids(_getTags(ebdrBids));
}

// filter bids to de-dupe them?
function _getTags(bids) {
var key;
// var map = {};
var PubZoneIds = [];

for (key in bids) {
PubZoneIds.push(bids[key]);
zoneid = utils.getBidIdParameter('zoneid', bids[key].params);
}

return PubZoneIds;
}

function getWidthAndHeight(bid) {
var adW = null;
var adH = null;

var sizeArrayLength = bid.sizes.length;
if (sizeArrayLength === 2 && typeof bid.sizes[0] === 'number' && typeof bid.sizes[1] === 'number') {
adW = bid.sizes[0];
adH = bid.sizes[1];
} else {
adW = bid.sizes[0][0];
adH = bid.sizes[0][1];
}

return [adW, adH];
}

function _requestBids(bidReqs) {
// build bid request object
var domain = window.location.host;
var page = window.location.pathname + location.search + location.hash;

var ebdrImps = [];
var ebdrParams = {};
// assign the first adUnit (placement) for bad bids;
defaultPlacementForBadBid = bidReqs[0].placementCode;
var bidderequestId = bidReqs[0].bidId;
// build impression array for ebdr
utils._each(bidReqs, function(bid) {
var bidFloor = utils.getBidIdParameter('bidfloor', bid.params);

var whArr = getWidthAndHeight(bid);
ebdrParams['latitude'] = utils.getBidIdParameter('latitude', bid.params);
ebdrParams['longitude'] = utils.getBidIdParameter('longitude', bid.params);
ebdrParams['ifa'] = (utils.getBidIdParameter('IDFA', bid.params).length > utils.getBidIdParameter('ADID', bid.params).length) ? utils.getBidIdParameter('IDFA', bid.params) : utils.getBidIdParameter('ADID', bid.params);
var imp = {
id: bid.bidId,
banner: {
w: whArr[0],
h: whArr[1]
},
// tagid: tagId,
bidfloor: bidFloor
};

ebdrImps.push(imp);
if (bid.params.ebdrDomain != undefined) {
rtbServerDomain = bid.params.ebdrDomain;
}
});

// build bid request with impressions
var ebdrBidReq = {
id: bidderequestId,
imp: ebdrImps,
site: {
domain: domain,
page: page
},
device: {
geo: {
lat: ebdrParams.latitude,
log: ebdrParams.longitude
},
ifa: ebdrParams.ifa
}
};

var scriptUrl = window.location.protocol + '//' + rtbServerDomain + '/hb?callback=window.$$PREBID_GLOBAL$$.ebdrResponse' +
'&zoneid=' + zoneid + '&br=' + encodeURIComponent(JSON.stringify(ebdrBidReq));

adloader.loadScript(scriptUrl);
}

function noBidResponse(bidReqs) {
// no response data
if (!bidReqs.zoneId) {
// no id with which to create an dummy bid
return;
}

var bid = bidfactory.createBid(2);
bid.bidderCode = 'ebdr';
bidmanager.addBidResponse(defaultPlacementForBadBid, bid);
}

// expose the callback to the global object:
$$PREBID_GLOBAL$$.ebdrResponse = function(ebdrResponseObj) {
var bid = {};
var key;
// valid object?
if (!ebdrResponseObj || !ebdrResponseObj.id) {
return noBidResponse(ebdrResponseObj);
}

if (!ebdrResponseObj.seatbid || ebdrResponseObj.seatbid.length === 0 || !ebdrResponseObj.seatbid[0].bid || ebdrResponseObj.seatbid[0].bid.length === 0) {
return noBidResponse(ebdrResponseObj);
}

for (key in ebdrResponseObj.seatbid[0].bid) {
var ebdrBid = ebdrResponseObj.seatbid[0].bid[key];

var responseCPM;
var placementCode = '';
var id = ebdrBid.impid;
// try to fetch the bid request we sent Ebdr
var bidObj = $$PREBID_GLOBAL$$._bidsRequested.find(bidSet => bidSet.bidderCode === 'ebdr').bids.find(bid => bid.bidId === id);
if (!bidObj) {
return noBidResponse(ebdrBid);
}

placementCode = bidObj.placementCode;
bidObj.status = CONSTANTS.STATUS.GOOD;

// place ad response on bidmanager._adResponsesByBidderId
responseCPM = parseFloat(ebdrBid.price);

if (responseCPM === 0) {
noBidResponse(ebdrBid);
}

ebdrBid.placementCode = placementCode;
ebdrBid.size = bidObj.sizes;
var responseAd = ebdrBid.adm;

// store bid response
// bid status is good (indicating 1)
bid = bidfactory.createBid(1);
bid.creative_id = ebdrBid.Id;
bid.bidderCode = 'ebdr';
bid.cpm = responseCPM;

// The bid is a mock bid, the true bidding process happens after the publisher tag is called
bid.ad = decodeURIComponent(responseAd);

var whArr = getWidthAndHeight(bidObj);
bid.width = whArr[0];
bid.height = whArr[1];

bidmanager.addBidResponse(placementCode, bid);
}
}; // ebdrResponse
return {
callBids: _callBids
};
};

adaptermanager.registerBidAdapter(new EbdrAdapter(), 'ebdr');

module.exports = EbdrAdapter;
43 changes: 43 additions & 0 deletions modules/ebdrBidAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Overview

**Module Name**: Ebdr Bidder Adapter
**Module Type**: Bidder Adapter
**Maintainer**: tech@engagebdr.com

# Description

Module that connects to Ebdr demand source to fetch bids.

# Test Parameters
```
var adUnits = [{
code: 'div-gpt-ad-1460505748561-0',
sizes: [[300, 250], [300,600]],
bids: [{
bidder: 'ebdr',
params: {
zoneid: '99999',
bidfloor: '1.00',
IDFA:'xxx-xxx',
ADID:'xxx-xxx',
latitude:'34.089811',
longitude:'-118.392805'
}
}]
},{
code: 'div-gpt-ad-1460505748561-1',
sizes: [[728, 90], [970, 90]],
bids: [{
bidder: 'ebdr',
params: {
zoneid: '99999',
bidfloor: '1.00',
IDFA:'xxx-xxx',
ADID:'xxx-xxx',
latitude:'34.089811',
longitude:'-118.392805'
}
}]
}
];
```
135 changes: 135 additions & 0 deletions test/spec/modules/ebdrBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
var chai = require('chai');
var Adapter = require('modules/ebdrBidAdapter')();
var adLoader = require('src/adloader');
var bidmanager = require('src/bidmanager.js');
var CONSTANTS = require('src/constants.json');

describe('ebdrBidAdapter', function () {
var sandbox;
var bidsRequestedOriginal;
const validBid_1 = {
bidderCode: 'ebdr',
bids: [
{
bidder: 'ebdr',
bidId: 'bid_id',
// bidderRequestId: 'ebdr1',
params: {
zoneid: '99999',
bidfloor: '1.00',
IDFA: 'xxx-xxx',
ADID: 'xxx-xxx',
latitude: '34.089811',
longitude: '-118.392805'
},
placementCode: 'div-gpt-ad-1460505748561-0',
sizes: [[300, 250], [800, 600]]
}
]
};
var invalidBid = {
bidderCode: 'ebdr',
bids: [
{
bidder: 'ebdr',
bidId: 'bid_id',
params: {},
placementCode: 'div-gpt-ad-1460505748561-0',
sizes: [[300, 250]]
}
]
};

var responseWithAd = {'id': 'bid_id', 'seatbid': [{'bid': [{'id': 'bid_id', 'impid': 'bid_id', 'price': 9.81, 'adid': 'abcde-12345', 'nurl': 'http://rex.bnmla.com/pixel?not=1&sid=LRX8nxQ4gX&z=16&rx=1.3392&d=3&dt=3&s=16&px=1.007999', 'adm': '<div><img src="https://cdn0.bnmla.com/0b1c6e85e9376e3092df8c9fc8ab9095.gif" width=300 height=250 /></div>', 'adomain': ['advertiserdomain.com'], 'iurl': 'http://rex.bnmla.com/pixel?not=1&sid=LRX8nxQ4gX&z=16&rx=1.3392&d=3&dt=3&s=16&px=1.007999', 'cid': 'campaign1', 'crid': 'abcde-12345', 'w': 300, 'h': 250}], 'seat': '3d1ec0495ea735'}], 'bidid': '3d1ec0495ea735', 'cur': 'USD'};
var responseWithoutAd = JSON.stringify({
'cpm': 0,
'url': 'http://p.ato.mx/placement?id=1234',
'width': 300,
'height': 250,
'code': 'ad-unit-1'
});

var responseEmpty = '';
var validJsonParams = {
id: '1234',
prebid: 'ad-unit-1',
size: '300x250'
};

beforeEach(() => {
bidsRequestedOriginal = $$PREBID_GLOBAL$$._bidsRequested;
$$PREBID_GLOBAL$$._bidsRequested = [];
sandbox = sinon.sandbox.create();
});

afterEach(() => {
sandbox.restore();
$$PREBID_GLOBAL$$._bidsRequested = bidsRequestedOriginal;
});

describe('bid request', function() {
let loadScript;
beforeEach(function () {
loadScript = sinon.stub(adLoader, 'loadScript');
});
afterEach(function () {
loadScript.restore();
});
it('bid request with valid data', function () {
Adapter.callBids(validBid_1);
sinon.assert.calledOnce(loadScript);
let url = loadScript.firstCall.args[0];
let callback = loadScript.firstCall.args[1];
expect(url).to.equal('http://dsp.bnmla.com/hb?callback=window.pbjs.ebdrResponse&zoneid=99999&br=%7B%22id%22%3A%22bid_id%22%2C%22imp%22%3A%5B%7B%22id%22%3A%22bid_id%22%2C%22banner%22%3A%7B%22w%22%3A300%2C%22h%22%3A250%7D%2C%22bidfloor%22%3A%221.00%22%7D%5D%2C%22site%22%3A%7B%22domain%22%3A%22localhost%3A9876%22%2C%22page%22%3A%22%2Fcontext.html%22%7D%2C%22device%22%3A%7B%22geo%22%3A%7B%22lat%22%3A%2234.089811%22%2C%22log%22%3A%22-118.392805%22%7D%2C%22ifa%22%3A%22xxx-xxx%22%7D%7D');
});
it('bid request with invalid data', function () {
Adapter.callBids(invalidBid);
sinon.assert.calledOnce(loadScript);
let url = loadScript.firstCall.args[0];
let callback = loadScript.firstCall.args[1];
expect(url).to.equal('http://dsp.bnmla.com/hb?callback=window.pbjs.ebdrResponse&zoneid=&br=%7B%22id%22%3A%22bid_id%22%2C%22imp%22%3A%5B%7B%22id%22%3A%22bid_id%22%2C%22banner%22%3A%7B%22w%22%3A300%2C%22h%22%3A250%7D%2C%22bidfloor%22%3A%22%22%7D%5D%2C%22site%22%3A%7B%22domain%22%3A%22localhost%3A9876%22%2C%22page%22%3A%22%2Fcontext.html%22%7D%2C%22device%22%3A%7B%22geo%22%3A%7B%22lat%22%3A%22%22%2C%22log%22%3A%22%22%7D%2C%22ifa%22%3A%22%22%7D%7D');
});
});
describe('ebdrResponse', () => {
it('should exist and be a function', () => {
expect($$PREBID_GLOBAL$$.ebdrResponse).to.be.a('function');
});
});

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

beforeEach(() => {
sandbox.stub(bidmanager, 'addBidResponse');
$$PREBID_GLOBAL$$._bidsRequested.push(validBid_1);
$$PREBID_GLOBAL$$.ebdrResponse(responseWithAd);
firstBid = bidmanager.addBidResponse.firstCall.args[1];
});
it('should add a bid object for each bid', () => {
sinon.assert.calledOnce(bidmanager.addBidResponse);
});

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

expect(firstPlacementCode).to.eql('div-gpt-ad-1460505748561-0');
});

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

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

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

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

0 comments on commit aa32519

Please sign in to comment.