diff --git a/modules/ebdrBidAdapter.js b/modules/ebdrBidAdapter.js
new file mode 100644
index 00000000000..63a308f1f62
--- /dev/null
+++ b/modules/ebdrBidAdapter.js
@@ -0,0 +1,133 @@
+import * as utils from 'src/utils';
+import { VIDEO, BANNER } from 'src/mediaTypes';
+import { registerBidder } from 'src/adapters/bidderFactory';
+const BIDDER_CODE = 'ebdr';
+export const spec = {
+ code: BIDDER_CODE,
+ supportedMediaTypes: [ BANNER, VIDEO ],
+ isBidRequestValid: function(bid) {
+ return !!(bid && bid.params && bid.params.zoneid);
+ },
+ buildRequests: function(bids) {
+ const rtbServerDomain = 'dsp.bnmla.com';
+ let domain = window.location.host;
+ let page = window.location.pathname + location.search + location.hash;
+ let ebdrImps = [];
+ const ebdrReq = {};
+ let ebdrParams = {};
+ let zoneid = '';
+ let requestId = '';
+ bids.forEach(bid => {
+ utils.logInfo('Log bid', bid);
+ let bidFloor = utils.getBidIdParameter('bidfloor', bid.params);
+ let whArr = getWidthAndHeight(bid);
+ let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video) ? VIDEO : BANNER;
+ zoneid = utils.getBidIdParameter('zoneid', bid.params);
+ requestId = bid.bidderRequestId;
+ ebdrImps.push({
+ id: bid.bidId,
+ [_mediaTypes]: {
+ w: whArr[0],
+ h: whArr[1]
+ },
+ bidfloor: bidFloor
+ })
+ ebdrReq[bid.bidId] = {mediaTypes: _mediaTypes,
+ w: whArr[0],
+ h: whArr[1]
+ };
+ 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);
+ });
+ let ebdrBidReq = {
+ id: requestId,
+ imp: ebdrImps,
+ site: {
+ domain: domain,
+ page: page
+ },
+ device: {
+ geo: {
+ lat: ebdrParams.latitude,
+ log: ebdrParams.longitude
+ },
+ ifa: ebdrParams.ifa
+ }
+ };
+ return {
+ method: 'GET',
+ url: '//' + rtbServerDomain + '/hb?' + '&zoneid=' + zoneid + '&br=' + encodeURIComponent(JSON.stringify(ebdrBidReq)),
+ bids: ebdrReq
+ };
+ },
+ interpretResponse: function(serverResponse, bidRequest) {
+ utils.logInfo('Log serverResponse', serverResponse);
+ utils.logInfo('Log bidRequest', bidRequest);
+ let ebdrResponseImps = [];
+ const ebdrResponseObj = serverResponse.body;
+ if (!ebdrResponseObj || !ebdrResponseObj.seatbid || ebdrResponseObj.seatbid.length === 0 || !ebdrResponseObj.seatbid[0].bid || ebdrResponseObj.seatbid[0].bid.length === 0) {
+ return [];
+ }
+ ebdrResponseObj.seatbid[0].bid.forEach(ebdrBid => {
+ let responseCPM;
+ responseCPM = parseFloat(ebdrBid.price);
+ let adm;
+ let type;
+ let _mediaTypes;
+ let vastURL;
+ if (bidRequest.bids[ebdrBid.id].mediaTypes == BANNER) {
+ adm = decodeURIComponent(ebdrBid.adm)
+ type = 'ad';
+ _mediaTypes = BANNER;
+ } else {
+ adm = ebdrBid.adm
+ type = 'vastXml'
+ _mediaTypes = VIDEO;
+ if (ebdrBid.nurl) {
+ vastURL = ebdrBid.nurl;
+ }
+ }
+ let response = {
+ requestId: ebdrBid.id,
+ [type]: adm,
+ mediaType: _mediaTypes,
+ creativeId: ebdrBid.crid,
+ cpm: responseCPM,
+ width: ebdrBid.w,
+ height: ebdrBid.h,
+ currency: 'USD',
+ netRevenue: true,
+ ttl: 3600 }
+ if (vastURL) {
+ response.vastUrl = vastURL;
+ }
+ ebdrResponseImps.push(response);
+ });
+ return ebdrResponseImps;
+ }
+}
+function getWidthAndHeight(bid) {
+ let adW = null;
+ let adH = null;
+ // Handing old bidder only has size object
+ if (bid.sizes && bid.sizes.length) {
+ let 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];
+ }
+ }
+ let _mediaTypes = bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER;
+ if (bid.mediaTypes && bid.mediaTypes[_mediaTypes]) {
+ if (_mediaTypes == BANNER && bid.mediaTypes[_mediaTypes].sizes && bid.mediaTypes[_mediaTypes].sizes[0] && bid.mediaTypes[_mediaTypes].sizes[0].length === 2) {
+ adW = bid.mediaTypes[_mediaTypes].sizes[0][0];
+ adH = bid.mediaTypes[_mediaTypes].sizes[0][1];
+ } else if (_mediaTypes == VIDEO && bid.mediaTypes[_mediaTypes].playerSize && bid.mediaTypes[_mediaTypes].playerSize.length === 2) {
+ adW = bid.mediaTypes[_mediaTypes].playerSize[0];
+ adH = bid.mediaTypes[_mediaTypes].playerSize[1];
+ }
+ }
+ return [adW, adH];
+}
+registerBidder(spec);
diff --git a/modules/ebdrBidAdapter.md b/modules/ebdrBidAdapter.md
new file mode 100644
index 00000000000..64483797b25
--- /dev/null
+++ b/modules/ebdrBidAdapter.md
@@ -0,0 +1,53 @@
+# Overview
+
+```
+Module Name: EngageBDR Bid Adapter
+Module Type: Bidder Adapter
+Maintainer: tech@engagebdr.com
+```
+
+# Description
+
+Adapter that connects to EngageBDR's demand sources.
+
+# Test Parameters
+```
+ var adUnits = [{
+ code: 'div-gpt-ad-1460505748561-0',
+ mediaTypes: {
+ banner: {
+ 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: 'test-video',
+ mediaTypes: {
+ video: {
+ context: 'instream',
+ playerSize: [300, 250]
+ }
+ },
+ bids: [{
+ bidder: 'ebdr',
+ params: {
+ zoneid: '99998',
+ bidfloor: '1.00',
+ IDFA:'xxx-xxx',
+ ADID:'xxx-xxx',
+ latitude:'34.089811',
+ longitude:'-118.392805'
+ }
+ }]
+ }];
+```
diff --git a/test/spec/modules/ebdrBidAdapter_spec.js b/test/spec/modules/ebdrBidAdapter_spec.js
new file mode 100644
index 00000000000..c5328f9ebb9
--- /dev/null
+++ b/test/spec/modules/ebdrBidAdapter_spec.js
@@ -0,0 +1,209 @@
+import { expect } from 'chai';
+import { spec } from 'modules/ebdrBidAdapter';
+import { VIDEO, BANNER } from 'src/mediaTypes';
+import * as utils from 'src/utils';
+
+describe('ebdrBidAdapter', () => {
+ let bidRequests;
+
+ beforeEach(() => {
+ bidRequests = [
+ {
+ code: 'div-gpt-ad-1460505748561-0',
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250], [300, 600]],
+ }
+ },
+ bidder: 'ebdr',
+ params: {
+ zoneid: '99999',
+ bidfloor: '1.00',
+ IDFA: 'xxx-xxx',
+ ADID: 'xxx-xxx',
+ latitude: '34.089811',
+ longitude: '-118.392805'
+ },
+ bidId: '2c5e8a1a84522d',
+ bidderRequestId: '1d0c4017f02458',
+ auctionId: '9adc85ed-43ee-4a78-816b-52b7e578f314'
+ }, {
+ adUnitCode: 'div-gpt-ad-1460505748561-1',
+ mediaTypes: {
+ video: {
+ context: 'instream',
+ playerSize: [300, 250]
+ }
+ },
+ bidder: 'ebdr',
+ params: {
+ zoneid: '99998',
+ bidfloor: '1.00',
+ IDFA: 'xxx-xxx',
+ ADID: 'xxx-xxx',
+ latitude: '34.089811',
+ longitude: '-118.392805'
+ },
+ bidId: '23a01e95856577',
+ bidderRequestId: '1d0c4017f02458',
+ auctionId: '9adc85ed-43ee-4a78-816b-52b7e578f314'
+ }
+ ];
+ });
+
+ describe('spec.isBidRequestValid', () => {
+ it('should return true when the required params are passed', () => {
+ const bidRequest = bidRequests[0];
+ expect(spec.isBidRequestValid(bidRequest)).to.equal(true);
+ });
+
+ it('should return true when the only required param is missing', () => {
+ const bidRequest = bidRequests[0];
+ bidRequest.params = {
+ zoneid: '99998',
+ bidfloor: '1.00',
+ };
+ expect(spec.isBidRequestValid(bidRequest)).to.equal(true);
+ });
+
+ it('should return true when the "bidfloor" param is missing', () => {
+ const bidRequest = bidRequests[0];
+ bidRequest.params = {
+ zoneid: '99998',
+ };
+ expect(spec.isBidRequestValid(bidRequest)).to.equal(true);
+ });
+
+ it('should return false when no bid params are passed', () => {
+ const bidRequest = bidRequests[0];
+ bidRequest.params = {};
+ expect(spec.isBidRequestValid(bidRequest)).to.equal(false);
+ });
+
+ it('should return false when a bid request is not passed', () => {
+ expect(spec.isBidRequestValid()).to.equal(false);
+ expect(spec.isBidRequestValid({})).to.equal(false);
+ });
+ });
+
+ describe('spec.buildRequests', () => {
+ describe('for banner bids', () => {
+ it('must handle an empty bid size', () => {
+ bidRequests[0].mediaTypes = { banner: {} };
+ const requests = spec.buildRequests(bidRequests);
+ const bidRequest = {};
+ bidRequest['2c5e8a1a84522d'] = { mediaTypes: BANNER, w: null, h: null };
+ expect(requests.bids['2c5e8a1a84522d']).to.deep.equals(bidRequest['2c5e8a1a84522d']);
+ });
+ it('should create a single GET', () => {
+ bidRequests[0].mediaTypes = { banner: {} };
+ bidRequests[1].mediaTypes = { banner: {} };
+ const requests = spec.buildRequests(bidRequests);
+ expect(requests.method).to.equal('GET');
+ });
+ it('must parse bid size from a nested array', () => {
+ const width = 640;
+ const height = 480;
+ const bidRequest = bidRequests[0];
+ bidRequest.mediaTypes = { banner: {sizes: [[ width, height ]]} };
+ const requests = spec.buildRequests([ bidRequest ]);
+ const data = {};
+ data['2c5e8a1a84522d'] = { mediaTypes: BANNER, w: width, h: height };
+ expect(requests.bids['2c5e8a1a84522d']).to.deep.equal(data['2c5e8a1a84522d']);
+ });
+ });
+ describe('for video bids', () => {
+ it('must handle an empty bid size', () => {
+ bidRequests[1].mediaTypes = { video: {} };
+ const requests = spec.buildRequests(bidRequests);
+ const bidRequest = {};
+ bidRequest['23a01e95856577'] = { mediaTypes: VIDEO, w: null, h: null };
+ expect(requests.bids['23a01e95856577']).to.deep.equals(bidRequest['23a01e95856577']);
+ });
+
+ it('should create a GET request for each bid', () => {
+ const bidRequest = bidRequests[1];
+ const requests = spec.buildRequests([ bidRequest ]);
+ expect(requests.method).to.equal('GET');
+ });
+ });
+ });
+
+ describe('spec.interpretResponse', () => {
+ describe('for video bids', () => {
+ it('should return no bids if the response is not valid', () => {
+ const bidRequest = bidRequests[0];
+ bidRequest.mediaTypes = { video: {} };
+ const bidResponse = spec.interpretResponse({ body: null }, { bidRequest });
+ expect(bidResponse.length).to.equal(0);
+ });
+
+ it('should return a valid video bid response', () => {
+ const ebdrReq = {bids: {}};
+ bidRequests.forEach(bid => {
+ let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER);
+ ebdrReq.bids[bid.bidId] = {mediaTypes: _mediaTypes,
+ w: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][0] : bid.mediaTypes[_mediaTypes].playerSize[0],
+ h: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][1] : bid.mediaTypes[_mediaTypes].playerSize[1]
+ };
+ });
+ const serverResponse = {id: '1d0c4017f02458', seatbid: [{bid: [{id: '23a01e95856577', impid: '23a01e95856577', price: 0.81, adid: 'abcde-12345', nurl: 'https://cdn0.bnmla.com/vtest.xml', adm: '\n