Skip to content

Commit 79291f7

Browse files
dhartonsa1omon
authored and
sa1omon
committed
Prebid 3 adbutler (prebid#4612)
* Adding AdButler Adapter * Prevent AdButler TypeError Only attempt to build a bid response if we have the information of which bid to respond to. * Refactor AdButler Testing Now stubbing adLoader instead of spying. Additional changes to ensure all tests still passed. * Prevent AdButler TypeErrors Prevent AdButler TypeErrors and pass bid request object into the bid response. * Add optional domain parameter. Add optional domain parameter to AdButler adapter. * Update AdButler adapter to 3.0 spec
1 parent a81cb71 commit 79291f7

File tree

3 files changed

+352
-0
lines changed

3 files changed

+352
-0
lines changed

modules/adbutlerBidAdapter.js

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
'use strict';
2+
3+
import * as utils from '../src/utils';
4+
import {config} from '../src/config';
5+
import {registerBidder} from '../src/adapters/bidderFactory';
6+
7+
const BIDDER_CODE = 'adbutler';
8+
9+
export const spec = {
10+
code: BIDDER_CODE,
11+
pageID: Math.floor(Math.random() * 10e6),
12+
aliases: ['divreach'],
13+
14+
isBidRequestValid: function (bid) {
15+
return !!(bid.params.accountID && bid.params.zoneID);
16+
},
17+
18+
buildRequests: function (validBidRequests) {
19+
let i;
20+
let zoneID;
21+
let bidRequest;
22+
let accountID;
23+
let keyword;
24+
let domain;
25+
let requestURI;
26+
let serverRequests = [];
27+
let zoneCounters = {};
28+
29+
for (i = 0; i < validBidRequests.length; i++) {
30+
bidRequest = validBidRequests[i];
31+
zoneID = utils.getBidIdParameter('zoneID', bidRequest.params);
32+
accountID = utils.getBidIdParameter('accountID', bidRequest.params);
33+
keyword = utils.getBidIdParameter('keyword', bidRequest.params);
34+
domain = utils.getBidIdParameter('domain', bidRequest.params);
35+
36+
if (!(zoneID in zoneCounters)) {
37+
zoneCounters[zoneID] = 0;
38+
}
39+
40+
if (typeof domain === 'undefined' || domain.length === 0) {
41+
domain = 'servedbyadbutler.com';
42+
}
43+
44+
requestURI = 'https://' + domain + '/adserve/;type=hbr;';
45+
requestURI += 'ID=' + encodeURIComponent(accountID) + ';';
46+
requestURI += 'setID=' + encodeURIComponent(zoneID) + ';';
47+
requestURI += 'pid=' + encodeURIComponent(spec.pageID) + ';';
48+
requestURI += 'place=' + encodeURIComponent(zoneCounters[zoneID]) + ';';
49+
50+
// append the keyword for targeting if one was passed in
51+
if (keyword !== '') {
52+
requestURI += 'kw=' + encodeURIComponent(keyword) + ';';
53+
}
54+
55+
zoneCounters[zoneID]++;
56+
serverRequests.push({
57+
method: 'GET',
58+
url: requestURI,
59+
data: {},
60+
bidRequest: bidRequest
61+
});
62+
}
63+
return serverRequests;
64+
},
65+
66+
interpretResponse: function (serverResponse, bidRequest) {
67+
const bidObj = bidRequest.bidRequest;
68+
let bidResponses = [];
69+
let bidResponse = {};
70+
let isCorrectSize = false;
71+
let isCorrectCPM = true;
72+
let CPM;
73+
let minCPM;
74+
let maxCPM;
75+
let width;
76+
let height;
77+
78+
serverResponse = serverResponse.body;
79+
if (serverResponse && serverResponse.status === 'SUCCESS' && bidObj) {
80+
CPM = serverResponse.cpm;
81+
minCPM = utils.getBidIdParameter('minCPM', bidObj.params);
82+
maxCPM = utils.getBidIdParameter('maxCPM', bidObj.params);
83+
width = parseInt(serverResponse.width);
84+
height = parseInt(serverResponse.height);
85+
86+
// Ensure response CPM is within the given bounds
87+
if (minCPM !== '' && CPM < parseFloat(minCPM)) {
88+
isCorrectCPM = false;
89+
}
90+
if (maxCPM !== '' && CPM > parseFloat(maxCPM)) {
91+
isCorrectCPM = false;
92+
}
93+
94+
// Ensure that response ad matches one of the placement sizes.
95+
utils._each(utils.deepAccess(bidObj, 'mediaTypes.banner.sizes', []), function (size) {
96+
if (width === size[0] && height === size[1]) {
97+
isCorrectSize = true;
98+
}
99+
});
100+
if (isCorrectCPM && isCorrectSize) {
101+
bidResponse.requestId = bidObj.bidId;
102+
bidResponse.bidderCode = bidObj.bidder;
103+
bidResponse.creativeId = serverResponse.placement_id;
104+
bidResponse.cpm = CPM;
105+
bidResponse.width = width;
106+
bidResponse.height = height;
107+
bidResponse.ad = serverResponse.ad_code;
108+
bidResponse.ad += spec.addTrackingPixels(serverResponse.tracking_pixels);
109+
bidResponse.currency = 'USD';
110+
bidResponse.netRevenue = true;
111+
bidResponse.ttl = config.getConfig('_bidderTimeout');
112+
bidResponse.referrer = utils.deepAccess(bidObj, 'refererInfo.referer');
113+
bidResponses.push(bidResponse);
114+
}
115+
}
116+
return bidResponses;
117+
},
118+
119+
addTrackingPixels: function (trackingPixels) {
120+
let trackingPixelMarkup = '';
121+
utils._each(trackingPixels, function (pixelURL) {
122+
let trackingPixel = '<img height="0" width="0" border="0" style="display:none;" src="';
123+
trackingPixel += pixelURL;
124+
trackingPixel += '">';
125+
126+
trackingPixelMarkup += trackingPixel;
127+
});
128+
return trackingPixelMarkup;
129+
}
130+
};
131+
registerBidder(spec);

test/spec/adapters/adbutler_spec.js

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
import {expect} from 'chai';
2+
import {spec} from 'modules/adbutlerBidAdapter';
3+
4+
describe('AdButler adapter', function () {
5+
let bidRequests;
6+
7+
beforeEach(function () {
8+
bidRequests = [
9+
{
10+
bidder: 'adbutler',
11+
params: {
12+
accountID: '167283',
13+
zoneID: '210093',
14+
keyword: 'red',
15+
minCPM: '1.00',
16+
maxCPM: '5.00'
17+
},
18+
placementCode: '/19968336/header-bid-tag-1',
19+
mediaTypes: {
20+
banner: {
21+
sizes: [[300, 250], [300, 600]],
22+
},
23+
},
24+
bidId: '23acc48ad47af5',
25+
auctionId: '0fb4905b-9456-4152-86be-c6f6d259ba99',
26+
bidderRequestId: '1c56ad30b9b8ca8',
27+
transactionId: '92489f71-1bf2-49a0-adf9-000cea934729'
28+
}
29+
];
30+
});
31+
32+
describe('implementation', function () {
33+
describe('for requests', function () {
34+
it('should accept valid bid', function () {
35+
let validBid = {
36+
bidder: 'adbutler',
37+
params: {
38+
accountID: '167283',
39+
zoneID: '210093'
40+
}
41+
},
42+
isValid = spec.isBidRequestValid(validBid);
43+
44+
expect(isValid).to.equal(true);
45+
});
46+
47+
it('should reject invalid bid', function () {
48+
let invalidBid = {
49+
bidder: 'adbutler',
50+
params: {
51+
accountID: '167283',
52+
}
53+
},
54+
isValid = spec.isBidRequestValid(invalidBid);
55+
56+
expect(isValid).to.equal(false);
57+
});
58+
59+
it('should use custom domain string', function () {
60+
let bidRequests = [
61+
{
62+
bidId: '3c9408cdbf2f68',
63+
sizes: [[300, 250]],
64+
bidder: 'adbutler',
65+
params: {
66+
accountID: '107878',
67+
zoneID: '86133',
68+
domain: 'servedbyadbutler.com.dan.test'
69+
},
70+
auctionId: '10b327aa396609',
71+
placementCode: '/123456/header-bid-tag-1'
72+
}
73+
],
74+
requests = spec.buildRequests(bidRequests),
75+
requestURL = requests[0].url;
76+
77+
expect(requestURL).to.have.string('.dan.test');
78+
});
79+
80+
it('should set default domain', function () {
81+
let requests = spec.buildRequests(bidRequests),
82+
request = requests[0];
83+
84+
let [domain] = request.url.split('/adserve/');
85+
86+
expect(domain).to.equal('https://servedbyadbutler.com');
87+
});
88+
89+
it('should set the keyword parameter', function () {
90+
let requests = spec.buildRequests(bidRequests),
91+
requestURL = requests[0].url;
92+
93+
expect(requestURL).to.have.string(';kw=red;');
94+
});
95+
96+
it('should increment the count for the same zone', function () {
97+
let bidRequests = [
98+
{
99+
sizes: [[300, 250]],
100+
bidder: 'adbutler',
101+
params: {
102+
accountID: '107878',
103+
zoneID: '86133',
104+
}
105+
}, {
106+
sizes: [[300, 250]],
107+
bidder: 'adbutler',
108+
params: {
109+
accountID: '107878',
110+
zoneID: '86133',
111+
}
112+
},
113+
],
114+
requests = spec.buildRequests(bidRequests),
115+
firstRequest = requests[0].url,
116+
secondRequest = requests[1].url;
117+
118+
expect(firstRequest).to.have.string(';place=0;');
119+
expect(secondRequest).to.have.string(';place=1;');
120+
});
121+
});
122+
123+
describe('bid responses', function () {
124+
it('should return complete bid response', function () {
125+
let serverResponse = {
126+
body: {
127+
status: 'SUCCESS',
128+
account_id: 167283,
129+
zone_id: 210093,
130+
cpm: 1.5,
131+
width: 300,
132+
height: 250,
133+
place: 0,
134+
ad_code: '<img src="http://image.source.com/img" alt="" title="" border="0" width="300" height="250">',
135+
tracking_pixels: [
136+
'http://tracking.pixel.com/params=info'
137+
]
138+
}
139+
},
140+
bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]});
141+
142+
expect(bids).to.be.lengthOf(1);
143+
144+
expect(bids[0].bidderCode).to.equal('adbutler');
145+
expect(bids[0].cpm).to.equal(1.5);
146+
expect(bids[0].width).to.equal(300);
147+
expect(bids[0].height).to.equal(250);
148+
expect(bids[0].currency).to.equal('USD');
149+
expect(bids[0].netRevenue).to.equal(true);
150+
expect(bids[0].ad).to.have.length.above(1);
151+
expect(bids[0].ad).to.have.string('http://tracking.pixel.com/params=info');
152+
});
153+
154+
it('should return empty bid response', function () {
155+
let serverResponse = {
156+
body: {
157+
status: 'NO_ELIGIBLE_ADS',
158+
zone_id: 210083,
159+
width: 300,
160+
height: 250,
161+
place: 0
162+
}
163+
},
164+
bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]});
165+
166+
expect(bids).to.be.lengthOf(0);
167+
});
168+
169+
it('should return empty bid response on incorrect size', function () {
170+
let serverResponse = {
171+
body: {
172+
status: 'SUCCESS',
173+
account_id: 167283,
174+
zone_id: 210083,
175+
cpm: 1.5,
176+
width: 728,
177+
height: 90,
178+
place: 0
179+
}
180+
},
181+
bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]});
182+
183+
expect(bids).to.be.lengthOf(0);
184+
});
185+
186+
it('should return empty bid response with CPM too low', function () {
187+
let serverResponse = {
188+
body: {
189+
status: 'SUCCESS',
190+
account_id: 167283,
191+
zone_id: 210093,
192+
cpm: 0.75,
193+
width: 300,
194+
height: 250,
195+
place: 0
196+
}
197+
},
198+
bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]});
199+
200+
expect(bids).to.be.lengthOf(0);
201+
});
202+
203+
it('should return empty bid response with CPM too high', function () {
204+
let serverResponse = {
205+
body: {
206+
status: 'SUCCESS',
207+
account_id: 167283,
208+
zone_id: 210093,
209+
cpm: 7,
210+
width: 300,
211+
height: 250,
212+
place: 0
213+
}
214+
},
215+
bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]});
216+
217+
expect(bids).to.be.lengthOf(0);
218+
});
219+
});
220+
});
221+
});

0 commit comments

Comments
 (0)