Skip to content

Commit d4712be

Browse files
jsnellbakerJustas Pupelis
authored and
Justas Pupelis
committed
multiformat size validation checks (prebid#1964)
* initial commit for multiformat size validation checks * adding unit tests and changes to checkBidRequestSizes function * updates to appnexusBidAdapter
1 parent cf3bf61 commit d4712be

File tree

5 files changed

+376
-11
lines changed

5 files changed

+376
-11
lines changed

modules/appnexusBidAdapter.js

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -216,20 +216,24 @@ function newBid(serverBid, rtbBid) {
216216
body: nativeAd.desc,
217217
cta: nativeAd.ctatext,
218218
sponsoredBy: nativeAd.sponsored,
219-
image: {
220-
url: nativeAd.main_img && nativeAd.main_img.url,
221-
height: nativeAd.main_img && nativeAd.main_img.height,
222-
width: nativeAd.main_img && nativeAd.main_img.width,
223-
},
224-
icon: {
225-
url: nativeAd.icon && nativeAd.icon.url,
226-
height: nativeAd.icon && nativeAd.icon.height,
227-
width: nativeAd.icon && nativeAd.icon.width,
228-
},
229219
clickUrl: nativeAd.link.url,
230220
clickTrackers: nativeAd.link.click_trackers,
231221
impressionTrackers: nativeAd.impression_trackers,
232222
};
223+
if (nativeAd.main_img) {
224+
bid['native'].image = {
225+
url: nativeAd.main_img.url,
226+
height: nativeAd.main_img.height,
227+
width: nativeAd.main_img.width,
228+
};
229+
}
230+
if (nativeAd.icon) {
231+
bid['native'].icon = {
232+
url: nativeAd.icon.url,
233+
height: nativeAd.icon.height,
234+
width: nativeAd.icon.width,
235+
};
236+
}
233237
} else {
234238
Object.assign(bid, {
235239
width: rtbBid.rtb.banner.width,

src/adaptermanager.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ function getAdUnitCopyForClientAdapters(adUnits) {
149149

150150
exports.makeBidRequests = function(adUnits, auctionStart, auctionId, cbTimeout, labels) {
151151
let bidRequests = [];
152+
153+
adUnits = exports.checkBidRequestSizes(adUnits);
154+
152155
let bidderCodes = getBidderCodes(adUnits);
153156
if (config.getConfig('bidderSequence') === RANDOM) {
154157
bidderCodes = shuffle(bidderCodes);
@@ -211,6 +214,54 @@ exports.makeBidRequests = function(adUnits, auctionStart, auctionId, cbTimeout,
211214
return bidRequests;
212215
};
213216

217+
exports.checkBidRequestSizes = (adUnits) => {
218+
Array.prototype.forEach.call(adUnits, adUnit => {
219+
if (adUnit.sizes) {
220+
utils.logWarn('Usage of adUnits.sizes will eventually be deprecated. Please define size dimensions within the corresponding area of the mediaTypes.<object> (eg mediaTypes.banner.sizes).');
221+
}
222+
223+
const mediaTypes = adUnit.mediaTypes;
224+
if (mediaTypes && mediaTypes.banner) {
225+
const banner = mediaTypes.banner;
226+
if (banner.sizes) {
227+
adUnit.sizes = banner.sizes;
228+
} else {
229+
utils.logError('Detected a mediaTypes.banner object did not include sizes. This is a required field for the mediaTypes.banner object. Removing invalid mediaTypes.banner object from request.');
230+
delete adUnit.mediaTypes.banner;
231+
}
232+
}
233+
234+
if (mediaTypes && mediaTypes.video) {
235+
const video = mediaTypes.video;
236+
if (video.playerSize) {
237+
if (Array.isArray(video.playerSize) && video.playerSize.length === 2 && Number.isInteger(video.playerSize[0]) && Number.isInteger(video.playerSize[1])) {
238+
adUnit.sizes = video.playerSize;
239+
} else {
240+
utils.logError('Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [640, 480]. Removing invalid mediaTypes.video.playerSize property from request.');
241+
delete adUnit.mediaTypes.video.playerSize;
242+
}
243+
}
244+
}
245+
246+
if (mediaTypes && mediaTypes.native) {
247+
const native = mediaTypes.native;
248+
if (native.image && native.image.sizes && !Array.isArray(native.image.sizes)) {
249+
utils.logError('Please use an array of sizes for native.image.sizes field. Removing invalid mediaTypes.native.image.sizes property from request.');
250+
delete adUnit.mediaTypes.native.image.sizes;
251+
}
252+
if (native.image && native.image.aspect_ratios && !Array.isArray(native.image.aspect_ratios)) {
253+
utils.logError('Please use an array of sizes for native.image.aspect_ratios field. Removing invalid mediaTypes.native.image.aspect_ratios property from request.');
254+
delete adUnit.mediaTypes.native.image.aspect_ratios;
255+
}
256+
if (native.icon && native.icon.sizes && !Array.isArray(native.icon.sizes)) {
257+
utils.logError('Please use an array of sizes for native.icon.sizes field. Removing invalid mediaTypes.native.icon.sizes property from request.');
258+
delete adUnit.mediaTypes.native.icon.sizes;
259+
}
260+
}
261+
});
262+
return adUnits;
263+
}
264+
214265
exports.callBids = (adUnits, bidRequests, addBidResponse, doneCb) => {
215266
if (!bidRequests.length) {
216267
utils.logWarn('callBids executed with no bidRequests. Were they filtered by labels or sizing?');

src/native.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,18 @@ export function nativeBidIsValid(bid, bidRequests) {
8585
return false;
8686
}
8787

88+
if (deepAccess(bid, 'native.image')) {
89+
if (!deepAccess(bid, 'native.image.height') || !deepAccess(bid, 'native.image.width')) {
90+
return false;
91+
}
92+
}
93+
94+
if (deepAccess(bid, 'native.icon')) {
95+
if (!deepAccess(bid, 'native.icon.height') || !deepAccess(bid, 'native.icon.width')) {
96+
return false;
97+
}
98+
}
99+
88100
const requestedAssets = bidRequest.nativeParams;
89101
if (!requestedAssets) {
90102
return true;

test/spec/native_spec.js

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect } from 'chai';
2-
import { fireNativeTrackers, getNativeTargeting } from 'src/native';
2+
import { fireNativeTrackers, getNativeTargeting, nativeBidIsValid } from 'src/native';
33
const utils = require('src/utils');
44

55
const bid = {
@@ -44,3 +44,113 @@ describe('native.js', () => {
4444
sinon.assert.calledWith(triggerPixelStub, bid.native.clickTrackers[0]);
4545
});
4646
});
47+
48+
describe('validate native', () => {
49+
let bidReq = [{
50+
bids: [{
51+
bidderCode: 'test_bidder',
52+
bidId: 'test_bid_id',
53+
mediaTypes: {
54+
native: {
55+
title: {
56+
required: true,
57+
},
58+
body: {
59+
required: true,
60+
},
61+
image: {
62+
required: true,
63+
sizes: [150, 50],
64+
aspect_ratios: [150, 50]
65+
},
66+
icon: {
67+
required: true,
68+
sizes: [50, 50]
69+
},
70+
}
71+
}
72+
}]
73+
}];
74+
75+
let validBid = {
76+
adId: 'test_bid_id',
77+
adUnitCode: '123/prebid_native_adunit',
78+
bidder: 'test_bidder',
79+
native: {
80+
body: 'This is a Prebid Native Creative. There are many like it, but this one is mine.',
81+
clickTrackers: ['http://my.click.tracker/url'],
82+
icon: {
83+
url: 'http://my.image.file/ad_image.jpg',
84+
height: 75,
85+
width: 75
86+
},
87+
image: {
88+
url: 'http://my.icon.file/ad_icon.jpg',
89+
height: 2250,
90+
width: 3000
91+
},
92+
clickUrl: 'http://prebid.org/dev-docs/show-native-ads.html',
93+
impressionTrackers: ['http://my.imp.tracker/url'],
94+
title: 'This is an example Prebid Native creative'
95+
}
96+
};
97+
98+
let noIconDimBid = {
99+
adId: 'test_bid_id',
100+
adUnitCode: '123/prebid_native_adunit',
101+
bidder: 'test_bidder',
102+
native: {
103+
body: 'This is a Prebid Native Creative. There are many like it, but this one is mine.',
104+
clickTrackers: ['http://my.click.tracker/url'],
105+
icon: {
106+
url: 'http://my.image.file/ad_image.jpg',
107+
height: 0,
108+
width: 0
109+
},
110+
image: {
111+
url: 'http://my.icon.file/ad_icon.jpg',
112+
height: 2250,
113+
width: 3000
114+
},
115+
clickUrl: 'http://prebid.org/dev-docs/show-native-ads.html',
116+
impressionTrackers: ['http://my.imp.tracker/url'],
117+
title: 'This is an example Prebid Native creative'
118+
}
119+
};
120+
121+
let noImgDimBid = {
122+
adId: 'test_bid_id',
123+
adUnitCode: '123/prebid_native_adunit',
124+
bidder: 'test_bidder',
125+
native: {
126+
body: 'This is a Prebid Native Creative. There are many like it, but this one is mine.',
127+
clickTrackers: ['http://my.click.tracker/url'],
128+
icon: {
129+
url: 'http://my.image.file/ad_image.jpg',
130+
height: 75,
131+
width: 75
132+
},
133+
image: {
134+
url: 'http://my.icon.file/ad_icon.jpg',
135+
height: 0,
136+
width: 0
137+
},
138+
clickUrl: 'http://prebid.org/dev-docs/show-native-ads.html',
139+
impressionTrackers: ['http://my.imp.tracker/url'],
140+
title: 'This is an example Prebid Native creative'
141+
}
142+
};
143+
144+
beforeEach(() => {});
145+
146+
afterEach(() => {});
147+
148+
it('should reject bid if no image sizes are defined', () => {
149+
let result = nativeBidIsValid(validBid, bidReq);
150+
expect(result).to.be.true;
151+
result = nativeBidIsValid(noIconDimBid, bidReq);
152+
expect(result).to.be.false;
153+
result = nativeBidIsValid(noImgDimBid, bidReq);
154+
expect(result).to.be.false;
155+
});
156+
});

0 commit comments

Comments
 (0)