diff --git a/modules/adbutlerBidAdapter.js b/modules/adbutlerBidAdapter.js new file mode 100644 index 00000000000..a08574c2044 --- /dev/null +++ b/modules/adbutlerBidAdapter.js @@ -0,0 +1,131 @@ +'use strict'; + +import * as utils from '../src/utils'; +import {config} from '../src/config'; +import {registerBidder} from '../src/adapters/bidderFactory'; + +const BIDDER_CODE = 'adbutler'; + +export const spec = { + code: BIDDER_CODE, + pageID: Math.floor(Math.random() * 10e6), + aliases: ['divreach'], + + isBidRequestValid: function (bid) { + return !!(bid.params.accountID && bid.params.zoneID); + }, + + buildRequests: function (validBidRequests) { + let i; + let zoneID; + let bidRequest; + let accountID; + let keyword; + let domain; + let requestURI; + let serverRequests = []; + let zoneCounters = {}; + + for (i = 0; i < validBidRequests.length; i++) { + bidRequest = validBidRequests[i]; + zoneID = utils.getBidIdParameter('zoneID', bidRequest.params); + accountID = utils.getBidIdParameter('accountID', bidRequest.params); + keyword = utils.getBidIdParameter('keyword', bidRequest.params); + domain = utils.getBidIdParameter('domain', bidRequest.params); + + if (!(zoneID in zoneCounters)) { + zoneCounters[zoneID] = 0; + } + + if (typeof domain === 'undefined' || domain.length === 0) { + domain = 'servedbyadbutler.com'; + } + + requestURI = 'https://' + domain + '/adserve/;type=hbr;'; + requestURI += 'ID=' + encodeURIComponent(accountID) + ';'; + requestURI += 'setID=' + encodeURIComponent(zoneID) + ';'; + requestURI += 'pid=' + encodeURIComponent(spec.pageID) + ';'; + requestURI += 'place=' + encodeURIComponent(zoneCounters[zoneID]) + ';'; + + // append the keyword for targeting if one was passed in + if (keyword !== '') { + requestURI += 'kw=' + encodeURIComponent(keyword) + ';'; + } + + zoneCounters[zoneID]++; + serverRequests.push({ + method: 'GET', + url: requestURI, + data: {}, + bidRequest: bidRequest + }); + } + return serverRequests; + }, + + interpretResponse: function (serverResponse, bidRequest) { + const bidObj = bidRequest.bidRequest; + let bidResponses = []; + let bidResponse = {}; + let isCorrectSize = false; + let isCorrectCPM = true; + let CPM; + let minCPM; + let maxCPM; + let width; + let height; + + serverResponse = serverResponse.body; + if (serverResponse && serverResponse.status === 'SUCCESS' && bidObj) { + CPM = serverResponse.cpm; + minCPM = utils.getBidIdParameter('minCPM', bidObj.params); + maxCPM = utils.getBidIdParameter('maxCPM', bidObj.params); + width = parseInt(serverResponse.width); + height = parseInt(serverResponse.height); + + // Ensure response CPM is within the given bounds + if (minCPM !== '' && CPM < parseFloat(minCPM)) { + isCorrectCPM = false; + } + if (maxCPM !== '' && CPM > parseFloat(maxCPM)) { + isCorrectCPM = false; + } + + // Ensure that response ad matches one of the placement sizes. + utils._each(utils.deepAccess(bidObj, 'mediaTypes.banner.sizes', []), function (size) { + if (width === size[0] && height === size[1]) { + isCorrectSize = true; + } + }); + if (isCorrectCPM && isCorrectSize) { + bidResponse.requestId = bidObj.bidId; + bidResponse.bidderCode = bidObj.bidder; + bidResponse.creativeId = serverResponse.placement_id; + bidResponse.cpm = CPM; + bidResponse.width = width; + bidResponse.height = height; + bidResponse.ad = serverResponse.ad_code; + bidResponse.ad += spec.addTrackingPixels(serverResponse.tracking_pixels); + bidResponse.currency = 'USD'; + bidResponse.netRevenue = true; + bidResponse.ttl = config.getConfig('_bidderTimeout'); + bidResponse.referrer = utils.deepAccess(bidObj, 'refererInfo.referer'); + bidResponses.push(bidResponse); + } + } + return bidResponses; + }, + + addTrackingPixels: function (trackingPixels) { + let trackingPixelMarkup = ''; + utils._each(trackingPixels, function (pixelURL) { + let trackingPixel = ''; + + trackingPixelMarkup += trackingPixel; + }); + return trackingPixelMarkup; + } +}; +registerBidder(spec); diff --git a/test/spec/adapters/adbutler_spec.js b/test/spec/adapters/adbutler_spec.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/spec/modules/adbutlerBidAdapter_spec.js b/test/spec/modules/adbutlerBidAdapter_spec.js new file mode 100644 index 00000000000..4f62d6206b4 --- /dev/null +++ b/test/spec/modules/adbutlerBidAdapter_spec.js @@ -0,0 +1,221 @@ +import {expect} from 'chai'; +import {spec} from 'modules/adbutlerBidAdapter'; + +describe('AdButler adapter', function () { + let bidRequests; + + beforeEach(function () { + bidRequests = [ + { + bidder: 'adbutler', + params: { + accountID: '167283', + zoneID: '210093', + keyword: 'red', + minCPM: '1.00', + maxCPM: '5.00' + }, + placementCode: '/19968336/header-bid-tag-1', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + }, + }, + bidId: '23acc48ad47af5', + auctionId: '0fb4905b-9456-4152-86be-c6f6d259ba99', + bidderRequestId: '1c56ad30b9b8ca8', + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' + } + ]; + }); + + describe('implementation', function () { + describe('for requests', function () { + it('should accept valid bid', function () { + let validBid = { + bidder: 'adbutler', + params: { + accountID: '167283', + zoneID: '210093' + } + }, + isValid = spec.isBidRequestValid(validBid); + + expect(isValid).to.equal(true); + }); + + it('should reject invalid bid', function () { + let invalidBid = { + bidder: 'adbutler', + params: { + accountID: '167283', + } + }, + isValid = spec.isBidRequestValid(invalidBid); + + expect(isValid).to.equal(false); + }); + + it('should use custom domain string', function () { + let bidRequests = [ + { + bidId: '3c9408cdbf2f68', + sizes: [[300, 250]], + bidder: 'adbutler', + params: { + accountID: '107878', + zoneID: '86133', + domain: 'servedbyadbutler.com.dan.test' + }, + auctionId: '10b327aa396609', + placementCode: '/123456/header-bid-tag-1' + } + ], + requests = spec.buildRequests(bidRequests), + requestURL = requests[0].url; + + expect(requestURL).to.have.string('.dan.test'); + }); + + it('should set default domain', function () { + let requests = spec.buildRequests(bidRequests), + request = requests[0]; + + let [domain] = request.url.split('/adserve/'); + + expect(domain).to.equal('https://servedbyadbutler.com'); + }); + + it('should set the keyword parameter', function () { + let requests = spec.buildRequests(bidRequests), + requestURL = requests[0].url; + + expect(requestURL).to.have.string(';kw=red;'); + }); + + it('should increment the count for the same zone', function () { + let bidRequests = [ + { + sizes: [[300, 250]], + bidder: 'adbutler', + params: { + accountID: '107878', + zoneID: '86133', + } + }, { + sizes: [[300, 250]], + bidder: 'adbutler', + params: { + accountID: '107878', + zoneID: '86133', + } + }, + ], + requests = spec.buildRequests(bidRequests), + firstRequest = requests[0].url, + secondRequest = requests[1].url; + + expect(firstRequest).to.have.string(';place=0;'); + expect(secondRequest).to.have.string(';place=1;'); + }); + }); + + describe('bid responses', function () { + it('should return complete bid response', function () { + let serverResponse = { + body: { + status: 'SUCCESS', + account_id: 167283, + zone_id: 210093, + cpm: 1.5, + width: 300, + height: 250, + place: 0, + ad_code: '', + tracking_pixels: [ + 'http://tracking.pixel.com/params=info' + ] + } + }, + bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + + expect(bids).to.be.lengthOf(1); + + expect(bids[0].bidderCode).to.equal('adbutler'); + expect(bids[0].cpm).to.equal(1.5); + expect(bids[0].width).to.equal(300); + expect(bids[0].height).to.equal(250); + expect(bids[0].currency).to.equal('USD'); + expect(bids[0].netRevenue).to.equal(true); + expect(bids[0].ad).to.have.length.above(1); + expect(bids[0].ad).to.have.string('http://tracking.pixel.com/params=info'); + }); + + it('should return empty bid response', function () { + let serverResponse = { + body: { + status: 'NO_ELIGIBLE_ADS', + zone_id: 210083, + width: 300, + height: 250, + place: 0 + } + }, + bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + + expect(bids).to.be.lengthOf(0); + }); + + it('should return empty bid response on incorrect size', function () { + let serverResponse = { + body: { + status: 'SUCCESS', + account_id: 167283, + zone_id: 210083, + cpm: 1.5, + width: 728, + height: 90, + place: 0 + } + }, + bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + + expect(bids).to.be.lengthOf(0); + }); + + it('should return empty bid response with CPM too low', function () { + let serverResponse = { + body: { + status: 'SUCCESS', + account_id: 167283, + zone_id: 210093, + cpm: 0.75, + width: 300, + height: 250, + place: 0 + } + }, + bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + + expect(bids).to.be.lengthOf(0); + }); + + it('should return empty bid response with CPM too high', function () { + let serverResponse = { + body: { + status: 'SUCCESS', + account_id: 167283, + zone_id: 210093, + cpm: 7, + width: 300, + height: 250, + place: 0 + } + }, + bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + + expect(bids).to.be.lengthOf(0); + }); + }); + }); +});