Skip to content

Commit 271d634

Browse files
travisbealejsnellbaker
travisbeale
authored andcommitted
Somo prebid 3.0 updates (prebid#4595)
* Re-add Somo bid adapter with v3 compliance * Somo: fixed malformed url in test * Use an alternative method of getting the domain from a url when the URL API isn't supported (IE11) * Somo: fixed indent error
1 parent 927d36e commit 271d634

File tree

3 files changed

+845
-3
lines changed

3 files changed

+845
-3
lines changed

modules/somoBidAdapter.js

+289
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
import * as utils from '../src/utils';
2+
import { registerBidder } from '../src/adapters/bidderFactory';
3+
import includes from 'core-js/library/fn/array/includes';
4+
import {BANNER, VIDEO} from '../src/mediaTypes';
5+
6+
const VIDEO_TARGETING = ['mimes', 'minduration', 'maxduration', 'protocols',
7+
'startdelay', 'linearity', 'skip', 'delivery',
8+
'pos', 'api', 'ext', 'battr'];
9+
const BANNER_TARGETING = ['battr', 'btype', 'pos', 'mimes', 'ext'];
10+
11+
const SITE_TARGETING = ['name', 'domain', 'cat', 'keywords', 'content']
12+
const APP_TARGETING = ['name', 'bundle', 'domain', 'storeUrl', 'cat', 'ver', 'keywords', 'content']
13+
14+
export const spec = {
15+
16+
code: 'somo',
17+
18+
supportedMediaTypes: [BANNER, VIDEO],
19+
aliases: ['somoaudience'],
20+
21+
isBidRequestValid: bid => (
22+
!!(bid && bid.params && bid.params.placementId)
23+
),
24+
25+
buildRequests: function(bidRequests, bidderRequest) {
26+
return bidRequests.map(bidRequest => {
27+
let da = openRtbRequest(bidRequest, bidderRequest);
28+
29+
return {
30+
method: 'POST',
31+
url: 'https://publisher-east.mobileadtrading.com/rtb/bid?s=' + bidRequest.params.placementId.toString(),
32+
data: da,
33+
bidRequest: bidRequest
34+
};
35+
});
36+
},
37+
38+
interpretResponse(response, request) {
39+
return bidResponseAvailable(request, response);
40+
},
41+
42+
getUserSyncs: (syncOptions, serverResponses, gdprConsent) => {
43+
const syncs = [];
44+
var url = 'https://publisher-east.mobileadtrading.com/usersync';
45+
46+
if (syncOptions.pixelEnabled) {
47+
if (gdprConsent && typeof gdprConsent.consentString === 'string') {
48+
// add 'gdpr' only if 'gdprApplies' is defined
49+
if (typeof gdprConsent.gdprApplies === 'boolean') {
50+
url += `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`;
51+
}
52+
}
53+
syncs.push({
54+
type: 'image',
55+
url: url
56+
});
57+
}
58+
return syncs;
59+
}
60+
};
61+
62+
function bidResponseAvailable(bidRequest, bidResponse) {
63+
let bidResponses = [];
64+
65+
if (bidResponse.body) {
66+
let bidData = bidResponse.body.seatbid[0].bid[0];
67+
const bid = {
68+
requestId: bidResponse.body.id,
69+
cpm: bidData.price,
70+
width: bidData.w,
71+
height: bidData.h,
72+
ad: bidData.adm,
73+
ttl: 360,
74+
creativeId: bidData.crid,
75+
adId: bidData.impid,
76+
netRevenue: false,
77+
currency: 'USD',
78+
adUnitCode: bidRequest.bidRequest.adUnitCode
79+
};
80+
if (isVideo(bidRequest.bidRequest)) {
81+
bid.vastXml = bidData.adm;
82+
bid.mediaType = 'video';
83+
} else {
84+
bid.ad = bidData.adm;
85+
bid.mediaType = 'banner';
86+
}
87+
bidResponses.push(bid);
88+
}
89+
return bidResponses;
90+
}
91+
92+
function openRtbRequest(bidRequest, bidderRequest) {
93+
var openRtbRequest = {
94+
id: bidRequest.bidId,
95+
imp: [openRtbImpression(bidRequest)],
96+
at: 1,
97+
tmax: 400,
98+
site: openRtbSite(bidRequest, bidderRequest),
99+
app: openRtbApp(bidRequest),
100+
device: openRtbDevice(),
101+
bcat: openRtbBCat(bidRequest),
102+
badv: openRtbBAdv(bidRequest),
103+
ext: {
104+
prebid: '$prebid.version$',
105+
},
106+
};
107+
if (typeof bidderRequest !== 'undefined') {
108+
openRtbRequest = populateOpenRtbGdpr(bidderRequest.gdprConsent, openRtbRequest);
109+
}
110+
111+
return openRtbRequest;
112+
}
113+
114+
function populateOpenRtbGdpr(gdpr, bidRequest) {
115+
if (gdpr && bidRequest && 'gdprApplies' in gdpr) {
116+
if (!('reqs' in bidRequest)) {
117+
bidRequest.reqs = {};
118+
}
119+
if (!('ext' in bidRequest.reqs)) {
120+
bidRequest.reqs.ext = {};
121+
}
122+
bidRequest.reqs.ext.gdpr = gdpr.gdprApplies;
123+
124+
if ('consentString' in gdpr) {
125+
if (!('user' in bidRequest)) {
126+
bidRequest.user = {};
127+
}
128+
if (!('ext' in bidRequest.user)) {
129+
bidRequest.user.ext = {};
130+
}
131+
bidRequest.user.ext.consent = gdpr.consentString;
132+
}
133+
}
134+
135+
return bidRequest;
136+
}
137+
138+
function openRtbImpression(bidRequest) {
139+
const imp = {
140+
'id': bidRequest.bidId,
141+
bidfloor: bidRequest.params.bidfloor || 0,
142+
};
143+
if (isVideo(bidRequest)) {
144+
imp.video = {};
145+
if (bidRequest.mediaTypes &&
146+
bidRequest.mediaTypes.video &&
147+
bidRequest.mediaTypes.video.sizes) {
148+
const sizes = getSizes(bidRequest.mediaTypes.video.sizes);
149+
imp.video.w = sizes[0];
150+
imp.video.h = sizes[1];
151+
}
152+
if (bidRequest.params.video) {
153+
Object.keys(bidRequest.params.video)
154+
.filter(param => includes(VIDEO_TARGETING, param))
155+
.forEach(param => imp.video[param] = bidRequest.params.video[param]);
156+
}
157+
} else {
158+
imp.banner = {
159+
topframe: 0
160+
};
161+
if (bidRequest.mediaTypes &&
162+
bidRequest.mediaTypes.banner &&
163+
bidRequest.mediaTypes.banner.sizes) {
164+
const sizes = getSizes(bidRequest.mediaTypes.banner.sizes);
165+
imp.banner.w = sizes[0];
166+
imp.banner.h = sizes[1];
167+
}
168+
if (bidRequest.params.banner) {
169+
Object.keys(bidRequest.params.banner)
170+
.filter(param => includes(BANNER_TARGETING, param))
171+
.forEach(param => imp.banner[param] = bidRequest.params.banner[param]);
172+
}
173+
}
174+
return imp;
175+
}
176+
177+
function isApp(bidRequest) {
178+
if (bidRequest.params.app) {
179+
return true;
180+
} else {
181+
return false;
182+
}
183+
}
184+
185+
function openRtbSite(bidRequest, bidderRequest) {
186+
if (!isApp(bidRequest)) {
187+
const site = {};
188+
189+
if (bidderRequest && bidderRequest.refererInfo) {
190+
site.ref = bidderRequest.refererInfo.referer;
191+
site.page = bidderRequest.refererInfo.canonicalUrl;
192+
}
193+
194+
if (bidRequest.params.site) {
195+
Object.keys(bidRequest.params.site)
196+
.filter(param => includes(SITE_TARGETING, param))
197+
.forEach(param => site[param] = bidRequest.params.site[param]);
198+
}
199+
if (typeof site.domain === 'undefined' &&
200+
typeof site.page !== 'undefined') {
201+
if (typeof window.URL === 'function') {
202+
site.domain = (new window.URL(site.page)).hostname;
203+
} else {
204+
site.domain = getDomainFromUrl(site.page);
205+
}
206+
}
207+
208+
return site;
209+
} else {
210+
return null;
211+
}
212+
}
213+
214+
function getDomainFromUrl(url) {
215+
var domain = url;
216+
217+
if (url.indexOf('//') > -1) {
218+
domain = url.split('/')[2];
219+
} else {
220+
domain = url.split('/')[0];
221+
}
222+
223+
domain = domain.split(':')[0];
224+
domain = domain.split('?')[0];
225+
226+
return domain;
227+
}
228+
229+
function openRtbApp(bidRequest) {
230+
if (isApp(bidRequest)) {
231+
const app = {
232+
233+
}
234+
Object.keys(bidRequest.params.app)
235+
.filter(param => includes(APP_TARGETING, param))
236+
.forEach(param => app[param] = bidRequest.params.app[param]);
237+
238+
return app;
239+
} else {
240+
return null;
241+
}
242+
}
243+
244+
function openRtbDevice() {
245+
return {
246+
ip: 'check',
247+
ua: navigator.userAgent,
248+
language: (navigator.language || navigator.browserLanguage || navigator.userLanguage || navigator.systemLanguage),
249+
};
250+
}
251+
252+
function openRtbBCat(bidRequest) {
253+
if (utils.isArray(bidRequest.params.bcat)) {
254+
return bidRequest.params.bcat;
255+
}
256+
return [];
257+
}
258+
259+
function openRtbBAdv(bidRequest) {
260+
if (utils.isArray(bidRequest.params.badv)) {
261+
return bidRequest.params.badv;
262+
}
263+
return [];
264+
}
265+
266+
function isVideo(format) {
267+
return utils.deepAccess(format, 'mediaTypes.video') || format.mediaType == 'video';
268+
}
269+
270+
/* Turn bid request sizes into compatible format */
271+
function getSizes(requestSizes) {
272+
let width = 0;
273+
let height = 0;
274+
if (utils.isArray(requestSizes) && requestSizes.length === 2 &&
275+
!utils.isArray(requestSizes[0])) {
276+
width = parseInt(requestSizes[0], 10);
277+
height = parseInt(requestSizes[1], 10);
278+
} else if (typeof requestSizes === 'object') {
279+
for (let i = 0; i < requestSizes.length; i++) {
280+
let size = requestSizes[i];
281+
width = parseInt(size[0], 10);
282+
height = parseInt(size[1], 10);
283+
break;
284+
}
285+
}
286+
return [width, height];
287+
}
288+
289+
registerBidder(spec);

modules/somoBidAdapter.md

+11-3
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@
77
Connects to Somo Audience demand source.
88
Please use ```somo``` as the bidder code.
99

10-
For video integration, somoAudience returns content as vastXML and requires the publisher to define the cache url in config passed to Prebid for it to be valid in the auction
10+
For video integration, Somo Audience returns content as vastXML and requires the publisher to define the cache url in config passed to Prebid for it to be valid in the auction
1111
# Test Site Parameters
1212
```
1313
var adUnits = [{
1414
code: 'banner-ad-div',
15-
sizes: [[300, 250]],
15+
mediaTypes: {
16+
banner: {
17+
sizes: [[300, 250]]
18+
}
19+
},
1620
bids: [{
1721
bidder: 'somo',
1822
params: {
@@ -25,7 +29,11 @@ For video integration, somoAudience returns content as vastXML and requires the
2529
```
2630
var adUnits = [{
2731
code: 'banner-ad-div',
28-
sizes: [[300, 250]],
32+
mediaTypes: {
33+
banner: {
34+
sizes: [[300, 250]]
35+
}
36+
},
2937
bids: [{
3038
bidder: 'somo',
3139
params: {

0 commit comments

Comments
 (0)