Skip to content

Commit a6d9bac

Browse files
abijrjuanbonoraz-adroll
authored and
kei-igaw
committed
NextRoll Bidder Adapter (prebid#4829)
* First implementation of the AdRoll adapter (#1) * Fix request and bid id (#5) * Send Zone ID (#6) * Add age check before fastbid eval (#7) * Add age check before fastbid eval * Fix linting * Add date check (#8) * Add date exists check * Remove logging statement * Fix bidRequest validation (#9) * Fix deprecated function usage (#10) * [SENG-2757] remove custom function from adapter (#11) * remove loadExternalScript function * add adroll to the adloader whitelist * Handle nextroll id (#12) * Handle nextroll id * Remove double nesting in user obj * Revert change to publisherTagAvailable * Rename adroll -> nextroll (#14) * Rename fastbid -> pubtag functions and variables (#15) * Improve coverage of tests * Add docs * Add docs * Improve sizes and add sellerid * Add maintainer email * Fix CI problem * Fix IE tests * Replace second instance of find * Fix types used in the doc Match prebid/prebid.github.io#1796 * Remove unused fields in spec * Add ccpa support * Remove external script usage * Remove IP field * Remove pubtag key * Rename imports; Remove getUserSync function; Remove unused code; Use url.parse function Co-authored-by: Juan Bono <[email protected]> Co-authored-by: Ricardo Azpeitia Pimentel <[email protected]>
1 parent 0188117 commit a6d9bac

File tree

3 files changed

+448
-0
lines changed

3 files changed

+448
-0
lines changed

modules/nextrollBidAdapter.js

+215
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
import * as utils from '../src/utils.js';
2+
import { registerBidder } from '../src/adapters/bidderFactory.js';
3+
import { BANNER } from '../src/mediaTypes.js';
4+
import { parse as parseUrl } from '../src/url.js';
5+
6+
import find from 'core-js/library/fn/array/find.js';
7+
8+
const BIDDER_CODE = 'nextroll';
9+
const BIDDER_ENDPOINT = 'https://d.adroll.com/bid/prebid/';
10+
const ADAPTER_VERSION = 4;
11+
12+
export const spec = {
13+
code: BIDDER_CODE,
14+
supportedMediaTypes: [BANNER],
15+
16+
/**
17+
* Determines whether or not the given bid request is valid.
18+
*
19+
* @param {BidRequest} bid The bid params to validate.
20+
* @return boolean True if this is a valid bid, and false otherwise.
21+
*/
22+
isBidRequestValid: function (bidRequest) {
23+
return bidRequest !== undefined && !!bidRequest.params && !!bidRequest.bidId;
24+
},
25+
26+
/**
27+
* Make a server request from the list of BidRequests.
28+
*
29+
* @param {validBidRequests[]} - an array of bids
30+
* @return ServerRequest Info describing the request to the server.
31+
*/
32+
buildRequests: function (validBidRequests, bidderRequest) {
33+
let topLocation = parseUrl(utils.deepAccess(bidderRequest, 'refererInfo.referer'));
34+
let consent = hasCCPAConsent(bidderRequest);
35+
return validBidRequests.map((bidRequest, index) => {
36+
return {
37+
method: 'POST',
38+
options: {
39+
withCredentials: consent,
40+
},
41+
url: BIDDER_ENDPOINT,
42+
data: {
43+
id: bidRequest.bidId,
44+
imp: {
45+
id: bidRequest.bidId,
46+
bidfloor: utils.getBidIdParameter('bidfloor', bidRequest.params),
47+
banner: {
48+
format: _getSizes(bidRequest)
49+
},
50+
ext: {
51+
zone: {
52+
id: utils.getBidIdParameter('zoneId', bidRequest.params)
53+
},
54+
nextroll: {
55+
adapter_version: ADAPTER_VERSION
56+
}
57+
}
58+
},
59+
60+
user: _getUser(validBidRequests),
61+
site: _getSite(bidRequest, topLocation),
62+
seller: _getSeller(bidRequest),
63+
device: _getDevice(bidRequest),
64+
}
65+
}
66+
})
67+
},
68+
69+
/**
70+
* Unpack the response from the server into a list of bids.
71+
*
72+
* @param {ServerResponse} serverResponse A successful response from the server.
73+
* @return {Bid[]} An array of bids which were nested inside the server.
74+
*/
75+
interpretResponse: function (serverResponse, bidRequest) {
76+
if (!serverResponse.body) {
77+
return [];
78+
} else {
79+
let response = serverResponse.body
80+
let bids = response.seatbid.reduce((acc, seatbid) => acc.concat(seatbid.bid), []);
81+
return bids.map((bid) => _buildResponse(response, bid));
82+
}
83+
}
84+
}
85+
86+
function _getUser(requests) {
87+
let id = utils.deepAccess(requests, '0.userId.nextroll');
88+
if (id === undefined) {
89+
return
90+
}
91+
92+
return {
93+
ext: {
94+
eid: [{
95+
'source': 'nextroll',
96+
id
97+
}]
98+
}
99+
}
100+
}
101+
102+
function _buildResponse(bidResponse, bid) {
103+
const adm = utils.replaceAuctionPrice(bid.adm, bid.price);
104+
return {
105+
requestId: bidResponse.id,
106+
cpm: bid.price,
107+
width: bid.w,
108+
height: bid.h,
109+
creativeId: bid.crid,
110+
dealId: bidResponse.dealId,
111+
currency: 'USD',
112+
netRevenue: true,
113+
ttl: 300,
114+
ad: adm
115+
}
116+
}
117+
118+
function _getSite(bidRequest, topLocation) {
119+
return {
120+
page: topLocation.href,
121+
domain: topLocation.hostname,
122+
publisher: {
123+
id: utils.getBidIdParameter('publisherId', bidRequest.params)
124+
}
125+
}
126+
}
127+
128+
function _getSeller(bidRequest) {
129+
return {
130+
id: utils.getBidIdParameter('sellerId', bidRequest.params)
131+
}
132+
}
133+
134+
function _getSizes(bidRequest) {
135+
return bidRequest.sizes.filter(_isValidSize).map(size => {
136+
return {
137+
w: size[0],
138+
h: size[1]
139+
}
140+
})
141+
}
142+
143+
function _isValidSize(size) {
144+
const isNumber = x => typeof x === 'number';
145+
return (size.length === 2 && isNumber(size[0]) && isNumber(size[1]));
146+
}
147+
148+
function _getDevice(_bidRequest) {
149+
return {
150+
ua: navigator.userAgent,
151+
language: navigator['language'],
152+
os: _getOs(navigator.userAgent.toLowerCase()),
153+
osv: _getOsVersion(navigator.userAgent)
154+
}
155+
}
156+
157+
function _getOs(userAgent) {
158+
const osTable = {
159+
'android': /android/i,
160+
'ios': /iphone|ipad/i,
161+
'mac': /mac/i,
162+
'linux': /linux/i,
163+
'windows': /windows/i
164+
};
165+
166+
return find(Object.keys(osTable), os => {
167+
if (userAgent.match(osTable[os])) {
168+
return os;
169+
}
170+
}) || 'etc';
171+
}
172+
173+
function _getOsVersion(userAgent) {
174+
let clientStrings = [
175+
{ s: 'Android', r: /Android/ },
176+
{ s: 'iOS', r: /(iPhone|iPad|iPod)/ },
177+
{ s: 'Mac OS X', r: /Mac OS X/ },
178+
{ s: 'Mac OS', r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ },
179+
{ s: 'Linux', r: /(Linux|X11)/ },
180+
{ s: 'Windows 10', r: /(Windows 10.0|Windows NT 10.0)/ },
181+
{ s: 'Windows 8.1', r: /(Windows 8.1|Windows NT 6.3)/ },
182+
{ s: 'Windows 8', r: /(Windows 8|Windows NT 6.2)/ },
183+
{ s: 'Windows 7', r: /(Windows 7|Windows NT 6.1)/ },
184+
{ s: 'Windows Vista', r: /Windows NT 6.0/ },
185+
{ s: 'Windows Server 2003', r: /Windows NT 5.2/ },
186+
{ s: 'Windows XP', r: /(Windows NT 5.1|Windows XP)/ },
187+
{ s: 'UNIX', r: /UNIX/ },
188+
{ s: 'Search Bot', r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/ }
189+
];
190+
let cs = find(clientStrings, cs => cs.r.test(userAgent));
191+
return cs ? cs.s : 'unknown';
192+
}
193+
194+
export function hasCCPAConsent(bidderRequest) {
195+
if (typeof bidderRequest.uspConsent !== 'string') {
196+
return true;
197+
}
198+
const usps = bidderRequest.uspConsent;
199+
const version = usps[0];
200+
201+
// If we don't support the consent string, assume no-consent.
202+
if (version !== '1' || usps.length < 3) {
203+
return false;
204+
}
205+
206+
const notice = usps[1];
207+
const optOut = usps[2];
208+
209+
if (notice === 'N' || optOut === 'Y') {
210+
return false;
211+
}
212+
return true;
213+
}
214+
215+
registerBidder(spec);

modules/nextrollBidAdapter.md

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Overview
2+
3+
```
4+
Module Name: NextRoll Bid Adapter
5+
Module Type: Bidder Adapter
6+
Maintainer: [email protected]
7+
```
8+
9+
# Description
10+
11+
Module that connects to NextRoll's bidders.
12+
The NextRoll bid adapter supports Banner format only.
13+
14+
# Test Parameters
15+
``` javascript
16+
var adUnits = [
17+
{
18+
code: 'div-1',
19+
mediatypes: {
20+
banner: {sizes: [[300, 250], [160, 600]]}
21+
},
22+
bids: [{
23+
bidder: 'nextroll',
24+
params: {
25+
bidfloor: 1,
26+
zoneId: "13144370",
27+
publisherId: "publisherid",
28+
sellerId: "sellerid"
29+
}
30+
}]
31+
},
32+
{
33+
code: 'div-2',
34+
mediatypes: {
35+
banner: {
36+
sizes: [[728, 90], [970, 250]]
37+
}
38+
},
39+
bids: [{
40+
bidder: 'nextroll',
41+
params: {
42+
bidfloor: 2.3,
43+
zoneId: "13144370",
44+
publisherId: "publisherid",
45+
sellerId: "sellerid"
46+
}
47+
}]
48+
}
49+
]
50+
```

0 commit comments

Comments
 (0)