Skip to content

Commit 6cc1ec3

Browse files
authored
Add Native ad capability to AdYouLike adapter (#6198)
* Remove useless bidderCode in bid response * send all the available sizes in the bid request * Use the banner sizes if given * avoid compatibility issue with old bid format * ad iframe and publisher domain paramters to bid requests * add publisher domain info in ad request * add a check in unit tests for publisherDomain * encode uri components * add native assets support * add native information to ad request * fix native ad parsing * fix nativ condition to set mediatype * fix image access and add trackers * fix and add unit test on native ad * update md file * remove usage of URLSearchParams * allox pure native ad with no adm provided Co-authored-by: Guillaume <[email protected]>
1 parent 62ca969 commit 6cc1ec3

File tree

3 files changed

+346
-34
lines changed

3 files changed

+346
-34
lines changed

modules/adyoulikeBidAdapter.js

+163-14
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import * as utils from '../src/utils.js';
22
import { registerBidder } from '../src/adapters/bidderFactory.js';
33
import { config } from '../src/config.js';
44
import find from 'core-js-pure/features/array/find.js';
5+
import {BANNER, NATIVE} from '../src/mediaTypes.js';
56

67
const VERSION = '1.0';
78
const BIDDER_CODE = 'adyoulike';
89
const DEFAULT_DC = 'hb-api';
910

1011
export const spec = {
1112
code: BIDDER_CODE,
13+
supportedMediaTypes: [BANNER, NATIVE],
1214
aliases: ['ayl'], // short code
1315
/**
1416
* Determines whether or not the given bid request is valid.
@@ -18,10 +20,11 @@ export const spec = {
1820
*/
1921
isBidRequestValid: function (bid) {
2022
const sizes = getSize(getSizeArray(bid));
21-
if (!bid.params || !bid.params.placement || !sizes.width || !sizes.height) {
22-
return false;
23-
}
24-
return true;
23+
const sizeValid = sizes.width > 0 && sizes.height > 0;
24+
25+
// allows no size fornative only
26+
return (bid.params && bid.params.placement &&
27+
(sizeValid || (bid.mediaTypes && bid.mediaTypes.native)));
2528
},
2629
/**
2730
* Make a server request from the list of BidRequests.
@@ -41,6 +44,7 @@ export const spec = {
4144
accumulator[bid.bidId].Width = size.width;
4245
accumulator[bid.bidId].Height = size.height;
4346
accumulator[bid.bidId].AvailableSizes = sizesArray.join(',');
47+
if (bid.mediaTypes && bid.mediaTypes.native) accumulator[bid.bidId].Native = bid.mediaTypes.native;
4448
return accumulator;
4549
}, {}),
4650
PageRefreshed: getPageRefreshed()
@@ -171,9 +175,9 @@ function createEndpointQS(bidderRequest) {
171175
}
172176

173177
function getSizeArray(bid) {
174-
let inputSize = bid.sizes;
178+
let inputSize = bid.sizes || [];
175179
if (bid.mediaTypes && bid.mediaTypes.banner) {
176-
inputSize = bid.mediaTypes.banner.sizes;
180+
inputSize = bid.mediaTypes.banner.sizes || [];
177181
}
178182

179183
return utils.parseSizesInput(inputSize);
@@ -203,34 +207,179 @@ function getSize(sizesArray) {
203207
return parsed;
204208
}
205209

210+
function getInternalImgUrl(uid) {
211+
if (!uid) return '';
212+
return 'https://blobs.omnitagjs.com/blobs/' + uid.substr(16, 2) + '/' + uid.substr(16) + '/' + uid;
213+
}
214+
215+
function getImageUrl(config, resource, width, height) {
216+
let url = '';
217+
218+
switch (resource.Kind) {
219+
case 'INTERNAL':
220+
url = getInternalImgUrl(resource.Data.Internal.BlobReference.Uid);
221+
222+
break;
223+
224+
case 'EXTERNAL':
225+
const dynPrefix = config.DynamicPrefix;
226+
let extUrl = resource.Data.External.Url;
227+
extUrl = extUrl.replace(/\[height\]/i, '' + height);
228+
extUrl = extUrl.replace(/\[width\]/i, '' + width);
229+
230+
if (extUrl.indexOf(dynPrefix) >= 0) {
231+
const urlmatch = (/.*url=([^&]*)/gm).exec(extUrl);
232+
url = urlmatch ? urlmatch[1] : '';
233+
if (!url) {
234+
url = getInternalImgUrl((/.*key=([^&]*)/gm).exec(extUrl)[1]);
235+
}
236+
} else {
237+
url = extUrl;
238+
}
239+
240+
break;
241+
}
242+
243+
return url;
244+
}
245+
246+
function getTrackers(eventsArray, jsTrackers) {
247+
const result = [];
248+
249+
if (!eventsArray) return result;
250+
251+
eventsArray.map((item, index) => {
252+
if ((jsTrackers && item.Kind === 'JAVASCRIPT_URL') ||
253+
(!jsTrackers && item.Kind === 'PIXEL_URL')) {
254+
result.push(item.Url);
255+
}
256+
});
257+
return result;
258+
}
259+
260+
function getNativeAssets(response, nativeConfig) {
261+
const native = {};
262+
263+
var adJson = {};
264+
var textsJson = {};
265+
if (typeof response.Ad === 'string') {
266+
adJson = JSON.parse(response.Ad.match(/\/\*PREBID\*\/(.*)\/\*PREBID\*\//)[1]);
267+
textsJson = adJson.Content.Preview.Text;
268+
269+
var impressionUrl = adJson.TrackingPrefix +
270+
'/pixel?event_kind=IMPRESSION&attempt=' + adJson.Attempt;
271+
272+
if (adJson.Campaign) {
273+
impressionUrl += '&campaign=' + adJson.Campaign;
274+
}
275+
276+
native.clickUrl = adJson.TrackingPrefix + '/ar?event_kind=CLICK&attempt=' + adJson.Attempt +
277+
'&campaign=' + adJson.Campaign + '&url=' + encodeURIComponent(adJson.Content.Landing.Url);
278+
279+
native.clickTrackers = getTrackers(adJson.OnEvents['CLICK']);
280+
native.impressionTrackers = getTrackers(adJson.OnEvents['IMPRESSION']);
281+
native.impressionTrackers.push(impressionUrl);
282+
native.javascriptTrackers = getTrackers(adJson.OnEvents['IMPRESSION'], true);
283+
}
284+
285+
Object.keys(nativeConfig).map(function(key, index) {
286+
if (typeof response.Native === 'object') {
287+
native[key] = response.Native[key];
288+
} else {
289+
switch (key) {
290+
case 'title':
291+
native[key] = textsJson.TITLE;
292+
break;
293+
case 'body':
294+
native[key] = textsJson.DESCRIPTION;
295+
break;
296+
case 'cta':
297+
native[key] = textsJson.CALLTOACTION;
298+
break;
299+
case 'sponsoredBy':
300+
native[key] = adJson.Content.Preview.Sponsor.Name;
301+
break;
302+
case 'image':
303+
// main image requested size
304+
const imgSize = nativeConfig.image.sizes || [];
305+
if (!imgSize.length) {
306+
imgSize[0] = response.Width || 300;
307+
imgSize[1] = response.Height || 250;
308+
}
309+
310+
native[key] = {
311+
url: getImageUrl(adJson, adJson.Content.Preview.Thumbnail.Image, imgSize[0], imgSize[1]),
312+
width: imgSize[0],
313+
height: imgSize[1]
314+
};
315+
break;
316+
case 'icon':
317+
if (adJson.HasSponsorImage) {
318+
// icon requested size
319+
const iconSize = nativeConfig.icon.sizes || [];
320+
if (!iconSize.length) {
321+
iconSize[0] = 50;
322+
iconSize[1] = 50;
323+
}
324+
325+
native[key] = {
326+
url: getImageUrl(adJson, adJson.Content.Preview.Sponsor.Logo.Resource, iconSize[0], iconSize[1]),
327+
width: iconSize[0],
328+
height: iconSize[1]
329+
};
330+
}
331+
break;
332+
case 'privacyIcon':
333+
native[key] = getImageUrl(adJson, adJson.Content.Preview.Credit.Logo.Resource, 25, 25);
334+
break;
335+
case 'privacyLink':
336+
native[key] = adJson.Content.Preview.Credit.Url;
337+
break;
338+
}
339+
}
340+
});
341+
342+
return native;
343+
}
344+
206345
/* Create bid from response */
207346
function createBid(response, bidRequests) {
208-
if (!response || !response.Ad) {
347+
if (!response || (!response.Ad && !response.Native)) {
209348
return
210349
}
211350

351+
const request = bidRequests && bidRequests[response.BidID];
352+
212353
// In case we don't retreive the size from the adserver, use the given one.
213-
if (bidRequests && bidRequests[response.BidID]) {
354+
if (request) {
214355
if (!response.Width || response.Width === '0') {
215-
response.Width = bidRequests[response.BidID].Width;
356+
response.Width = request.Width;
216357
}
217358

218359
if (!response.Height || response.Height === '0') {
219-
response.Height = bidRequests[response.BidID].Height;
360+
response.Height = request.Height;
220361
}
221362
}
222363

223-
return {
364+
const bid = {
224365
requestId: response.BidID,
225-
width: response.Width,
226-
height: response.Height,
227-
ad: response.Ad,
228366
ttl: 3600,
229367
creativeId: response.CreativeID,
230368
cpm: response.Price,
231369
netRevenue: true,
232370
currency: 'USD'
233371
};
372+
373+
if (request && request.Native) {
374+
bid.native = getNativeAssets(response, request.Native);
375+
bid.mediaType = 'native';
376+
} else {
377+
bid.width = response.Width;
378+
bid.height = response.Height;
379+
bid.ad = response.Ad;
380+
}
381+
382+
return bid;
234383
}
235384

236385
registerBidder(spec);

modules/adyoulikeBidAdapter.md

+45-15
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,49 @@ Banner formats are supported.
1111

1212
# Test Parameters
1313
```
14-
var adUnits = [
15-
{
16-
code: 'test-div',
17-
sizes: [[300, 250]],
18-
bids: [
19-
{
20-
bidder: "adyoulike",
21-
params: {
22-
placement: 194f787b85c829fb8822cdaf1ae64435,
23-
DC: 'fra01', // Optional for set the data center name
24-
}
25-
}
26-
]
27-
}
28-
];
14+
var adUnits = {
15+
"code": "test-div",
16+
"mediaTypes": {
17+
"banner": {
18+
"sizes": ["300x250"]
19+
},
20+
"native": {
21+
"image": {
22+
"required": true,
23+
},
24+
"title": {
25+
"required": true,
26+
"len": 80
27+
},
28+
"cta": {
29+
"required": false
30+
},
31+
"sponsoredBy": {
32+
"required": true
33+
},
34+
"clickUrl": {
35+
"required": true
36+
},
37+
"privacyIcon": {
38+
"required": false
39+
},
40+
"privacyLink": {
41+
"required": false
42+
},
43+
"body": {
44+
"required": true
45+
},
46+
"icon": {
47+
"required": true,
48+
"sizes": []
49+
}
50+
}
51+
bids: [{
52+
bidder: "adyoulike",
53+
params: {
54+
placement: 194 f787b85c829fb8822cdaf1ae64435,
55+
DC: "fra01", // Optional for set the data center name
56+
}
57+
}]
58+
};
2959
```

0 commit comments

Comments
 (0)