Skip to content

Commit

Permalink
IX Bid Adapter: Multiformat support, video adunit param, IXdiags (pre…
Browse files Browse the repository at this point in the history
…bid#6177)

* Multiformat support, video adunit param, IXdiags

* Lint fixing

* IE fix [cant handle includes]

* updated ixdiag count and validation logic

Co-authored-by: Ix-Prebid-Support <ix-prebid-support@indexexchange.com>
  • Loading branch information
umakajan and Ix-Prebid-Support authored Jan 21, 2021
1 parent a79d7d8 commit 892fd93
Show file tree
Hide file tree
Showing 3 changed files with 328 additions and 17 deletions.
135 changes: 120 additions & 15 deletions modules/ixBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,20 @@ function bidToBannerImp(bid) {
*/
function bidToVideoImp(bid) {
const imp = bidToImp(bid);
const videoAdUnitRef = utils.deepAccess(bid, 'mediaTypes.video');
const context = utils.deepAccess(bid, 'mediaTypes.video.context');
const videoAdUnitWhitelist = [
'mimes', 'minduration', 'maxduration', 'protocols', 'protocol',
'startdelay', 'placement', 'linearity', 'skip', 'skipmin',
'skipafter', 'sequence', 'battr', 'maxextended', 'minbitrate',
'maxbitrate', 'boxingallowed', 'playbackmethod', 'playbackend',
'delivery', 'pos', 'companionad', 'api', 'companiontype', 'ext'
];

imp.video = utils.deepClone(bid.params.video)
imp.video.w = bid.params.size[0];
imp.video.h = bid.params.size[1];

const context = utils.deepAccess(bid, 'mediaTypes.video.context');
if (context) {
if (context === 'instream') {
imp.video.placement = 1;
Expand All @@ -60,6 +68,12 @@ function bidToVideoImp(bid) {
}
}

for (let adUnitProperty in videoAdUnitRef) {
if (videoAdUnitWhitelist.indexOf(adUnitProperty) !== -1 && !imp.video.hasOwnProperty(adUnitProperty)) {
imp.video[adUnitProperty] = videoAdUnitRef[adUnitProperty];
}
}

return imp;
}

Expand Down Expand Up @@ -285,6 +299,12 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) {
r.ext.source = 'prebid';
r.ext.ixdiag = {};

// getting ixdiags for adunits of the video, outstream & multi format (MF) style
let ixdiag = buildIXDiag(validBidRequests);
for (var key in ixdiag) {
r.ext.ixdiag[key] = ixdiag[key];
}

// if an schain is provided, send it along
if (validBidRequests[0].schain) {
r.source = {
Expand Down Expand Up @@ -382,7 +402,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) {
data: payload
};

const BASE_REQ_SIZE = new Blob([`${request.url}${utils.parseQueryStringParameters({...request.data, r: JSON.stringify(r)})}`]).size;
const BASE_REQ_SIZE = new Blob([`${request.url}${utils.parseQueryStringParameters({ ...request.data, r: JSON.stringify(r) })}`]).size;
let currReqSize = BASE_REQ_SIZE;

const MAX_REQ_SIZE = 8000;
Expand Down Expand Up @@ -466,6 +486,66 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) {

return requests;
}

/**
* Calculates IX diagnostics values and packages them into an object
*
* @param {array} validBidRequests The valid bid requests from prebid
* @return {Object} IX diag values for ad units
*/
function buildIXDiag(validBidRequests) {
var adUnitMap = validBidRequests
.map(bidRequest => bidRequest.transactionId)
.filter((value, index, arr) => arr.indexOf(value) === index)

var ixdiag = {
mfu: 0,
bu: 0,
iu: 0,
nu: 0,
ou: 0,
allU: 0,
ren: false
};

// create ad unit map and collect the required diag properties
for (let i = 0; i < adUnitMap.length; i++) {
var bid = validBidRequests.filter(bidRequest => bidRequest.transactionId === adUnitMap[i])[0];

if (utils.deepAccess(bid, 'mediaTypes')) {
if (Object.keys(bid.mediaTypes).length > 1) {
ixdiag.mfu++;
}

if (utils.deepAccess(bid, 'mediaTypes.native')) {
ixdiag.nu++;
}

if (utils.deepAccess(bid, 'mediaTypes.banner')) {
ixdiag.bu++;
}

if (utils.deepAccess(bid, 'mediaTypes.video.context') === 'outstream') {
ixdiag.ou++;
// renderer only needed for outstream

const hasRenderer = typeof (utils.deepAccess(bid, 'renderer') || utils.deepAccess(bid, 'mediaTypes.video.renderer')) === 'object';

// if any one ad unit is missing renderer, set ren status to false in diag
ixdiag.ren = ixdiag.ren && hasRenderer ? (utils.deepAccess(ixdiag, 'ren')) : hasRenderer;
}

if (utils.deepAccess(bid, 'mediaTypes.video.context') === 'instream') {
ixdiag.iu++;
}

ixdiag.allU++;
}
}

return ixdiag;
}

/**
*
* @param {Object} impressions containing ixImps and possibly missingImps
Expand Down Expand Up @@ -525,7 +605,8 @@ function updateMissingSizes(validBidRequest, missingBannerSizes, imp) {
if (utils.deepAccess(validBidRequest, 'mediaTypes.banner.sizes')) {
let sizeList = utils.deepClone(validBidRequest.mediaTypes.banner.sizes);
removeFromSizes(sizeList, validBidRequest.params.size);
let newAdUnitEntry = { 'missingSizes': sizeList,
let newAdUnitEntry = {
'missingSizes': sizeList,
'impression': imp
};
missingBannerSizes[transactionID] = newAdUnitEntry;
Expand Down Expand Up @@ -560,32 +641,57 @@ export const spec = {
* @return {boolean} True if this is a valid bid, and false otherwise.
*/
isBidRequestValid: function (bid) {
const paramsVideoRef = utils.deepAccess(bid, 'params.video');
const paramsSize = utils.deepAccess(bid, 'params.size');
const mediaTypeBannerSizes = utils.deepAccess(bid, 'mediaTypes.banner.sizes');
const mediaTypeVideoRef = utils.deepAccess(bid, 'mediaTypes.video');
const mediaTypeVideoPlayerSize = utils.deepAccess(bid, 'mediaTypes.video.playerSize');
const hasBidFloor = bid.params.hasOwnProperty('bidFloor');
const hasBidFloorCur = bid.params.hasOwnProperty('bidFloorCur');

if (!isValidSize(bid.params.size)) {
utils.logError('ix bidder params: bid size has invalid format.');
return false;
}

if (!includesSize(bid.sizes, bid.params.size)) {
utils.logError('ix bidder params: bid size is not included in ad unit sizes.');
if (bid.hasOwnProperty('mediaType') && !(utils.contains(SUPPORTED_AD_TYPES, bid.mediaType))) {
return false;
}

if (bid.hasOwnProperty('mediaType') && !(utils.contains(SUPPORTED_AD_TYPES, bid.mediaType))) {
if (bid.hasOwnProperty('mediaTypes') && !(mediaTypeBannerSizes || mediaTypeVideoPlayerSize)) {
return false;
}

if (bid.hasOwnProperty('mediaTypes') && !(utils.deepAccess(bid, 'mediaTypes.banner.sizes') || utils.deepAccess(bid, 'mediaTypes.video.playerSize'))) {
if (!includesSize(bid.sizes, paramsSize) && !((mediaTypeVideoPlayerSize && includesSize(mediaTypeVideoPlayerSize, paramsSize)) ||
(mediaTypeBannerSizes && includesSize(mediaTypeBannerSizes, paramsSize)))) {
utils.logError('ix bidder params: bid size is not included in ad unit sizes or player size.');
return false;
}

if (mediaTypeVideoRef && paramsVideoRef) {
const requiredIXParams = ['mimes', 'minduration', 'maxduration', 'protocols'];
let isParamsLevelValid = true;
for (let property of requiredIXParams) {
if (!mediaTypeVideoRef.hasOwnProperty(property) && !paramsVideoRef.hasOwnProperty(property)) {
const isProtocolsValid = (property === 'protocols' && (mediaTypeVideoRef.hasOwnProperty('protocol') || paramsVideoRef.hasOwnProperty('protocol')));
if (isProtocolsValid) {
continue;
}
utils.logError('ix bidder params: ' + property + ' is not included in either the adunit or params level');
isParamsLevelValid = false;
}
}

if (!isParamsLevelValid) {
return false;
}
}

if (typeof bid.params.siteId !== 'string' && typeof bid.params.siteId !== 'number') {
utils.logError('ix bidder params: siteId must be string or number value.');
return false;
}

const hasBidFloor = bid.params.hasOwnProperty('bidFloor');
const hasBidFloorCur = bid.params.hasOwnProperty('bidFloorCur');

if (hasBidFloor || hasBidFloorCur) {
if (!(hasBidFloor && hasBidFloorCur && isValidBidFloorParams(bid.params.bidFloor, bid.params.bidFloorCur))) {
utils.logError('ix bidder params: bidFloor / bidFloorCur parameter has invalid format.');
Expand Down Expand Up @@ -616,7 +722,7 @@ export const spec = {
detectMissingSizes: true,
};

const ixConfig = {...DEFAULT_IX_CONFIG, ...config.getConfig('ix')};
const ixConfig = { ...DEFAULT_IX_CONFIG, ...config.getConfig('ix') };

for (let i = 0; i < validBidRequests.length; i++) {
validBidRequest = validBidRequests[i];
Expand All @@ -631,11 +737,10 @@ export const spec = {
}

videoImps[validBidRequest.transactionId].ixImps.push(bidToVideoImp(validBidRequest));
} else {
utils.logError('Bid size is not included in video playerSize')
}
}
if (validBidRequest.mediaType === BANNER || utils.deepAccess(validBidRequest, 'mediaTypes.banner') ||
if (validBidRequest.mediaType === BANNER ||
(utils.deepAccess(validBidRequest, 'mediaTypes.banner') && includesSize(utils.deepAccess(validBidRequest, 'mediaTypes.banner.sizes'), validBidRequest.params.size)) ||
(!validBidRequest.mediaType && !validBidRequest.mediaTypes)) {
let imp = bidToBannerImp(validBidRequest);

Expand Down Expand Up @@ -726,7 +831,7 @@ export const spec = {
* @param {Boolean} isOpenRtb boolean to check openrtb2 protocol
* @return {Object} params bid params
*/
transformBidParams: function(params, isOpenRtb) {
transformBidParams: function (params, isOpenRtb) {
return utils.convertTypes({
'siteID': 'number'
}, params);
Expand Down
2 changes: 1 addition & 1 deletion modules/ixBidAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ object are detailed here.
| --- | --- | --- | ---
| siteId | Required | String | An IX-specific identifier that is associated with a specific size on this ad unit. This is similar to a placement ID or an ad unit ID that some other modules have. Examples: `'3723'`, `'6482'`, `'3639'`
| size | Required | Number[] | The single size associated with the site ID. It should be one of the sizes listed in the ad unit under `adUnits[].sizes` or `adUnits[].mediaTypes.video.playerSize`. Examples: `[300, 250]`, `[300, 600]`
| video | Required | Hash | The video object will serve as the properties of the video ad. You can create any field under the video object that is mentioned in the `OpenRTB Spec v2.5`. Some fields like `mimes, protocols, minduration, maxduration` are required.
| video | Required | Hash | The video object will serve as the properties of the video ad. You can create any field under the video object that is mentioned in the `OpenRTB Spec v2.5`. Some fields like `mimes, protocols, minduration, maxduration` are required. Properties not defined at this level, will be pulled from the Adunit level.
| video.mimes | Required | String[] | Array list of content MIME types supported. Popular MIME types include, but are not limited to, `"video/x-ms- wmv"` for Windows Media and `"video/x-flv"` for Flash Video.
|video.minduration| Required | Integer | Minimum video ad duration in seconds.
|video.maxduration| Required | Integer | Maximum video ad duration in seconds.
Expand Down
Loading

0 comments on commit 892fd93

Please sign in to comment.