From 453f26d90cf0930eb3cb3e8c0492c80a42f70fe2 Mon Sep 17 00:00:00 2001 From: Love Sharma Date: Thu, 2 Sep 2021 08:25:54 -0400 Subject: [PATCH] IX Adapter: buildRequests refactor (#7364) * buildRequests refactor * remove use of Array.includes Co-authored-by: Love Sharma Co-authored-by: Kajan Umakanthan --- modules/ixBidAdapter.js | 180 ++++++++++++++++--------- test/spec/modules/ixBidAdapter_spec.js | 29 +--- 2 files changed, 116 insertions(+), 93 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 2d7157bc674..ab9c995aa7c 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -4,6 +4,7 @@ import { config } from '../src/config.js'; import find from 'core-js-pure/features/array/find.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { INSTREAM, OUTSTREAM } from '../src/video.js'; +import includes from 'core-js-pure/features/array/includes.js'; const BIDDER_CODE = 'ix'; const ALIAS_BIDDER_CODE = 'roundel'; @@ -108,8 +109,8 @@ function bidToVideoImp(bid) { const imp = bidToImp(bid); const videoAdUnitRef = utils.deepAccess(bid, 'mediaTypes.video'); const videoParamRef = utils.deepAccess(bid, 'params.video'); - - if (!checkVideoParams(bid, videoAdUnitRef, videoParamRef)) { + const videoParamErrors = checkVideoParams(videoAdUnitRef, videoParamRef); + if (videoParamErrors.length) { return {}; } @@ -305,7 +306,7 @@ function isValidSize(size) { * @return {boolean} True if the size object is an element of the size array, and false * otherwise. */ -function includesSize(sizeArray = [], size) { +function includesSize(sizeArray = [], size = []) { if (isValidSize(sizeArray)) { return sizeArray[0] === size[0] && sizeArray[1] === size[1]; } @@ -319,13 +320,12 @@ function includesSize(sizeArray = [], size) { /** * Checks if all required video params are present - * @param {object} bid Bid Object * @param {object} mediaTypeVideoRef Ad unit level mediaTypes object * @param {object} paramsVideoRef IX bidder params level video object - * @returns bool Are the required video params available + * @returns {string[]} Are the required video params available */ -function checkVideoParams(bid, mediaTypeVideoRef, paramsVideoRef) { - let reqParamsPresent = true; +function checkVideoParams(mediaTypeVideoRef, paramsVideoRef) { + const errorList = []; if (!mediaTypeVideoRef) { utils.logWarn('IX Bid Adapter: mediaTypes.video is the preferred location for video params in ad unit'); @@ -336,23 +336,21 @@ function checkVideoParams(bid, mediaTypeVideoRef, paramsVideoRef) { const propInVideoRef = paramsVideoRef && paramsVideoRef.hasOwnProperty(property); if (!propInMediaType && !propInVideoRef) { - utils.logError(`IX Bid Adapter: ${property} is not included in either the adunit or params level`); - reqParamsPresent = false; + errorList.push(`IX Bid Adapter: ${property} is not included in either the adunit or params level`); } } - // early return - if (!reqParamsPresent) { - return false; - } - // check protocols/protocol const protocolMediaType = mediaTypeVideoRef && mediaTypeVideoRef.hasOwnProperty('protocol'); const protocolsMediaType = mediaTypeVideoRef && mediaTypeVideoRef.hasOwnProperty('protocols'); const protocolVideoRef = paramsVideoRef && paramsVideoRef.hasOwnProperty('protocol'); const protocolsVideoRef = paramsVideoRef && paramsVideoRef.hasOwnProperty('protocols'); - return protocolMediaType || protocolsMediaType || protocolVideoRef || protocolsVideoRef; + if (!(protocolMediaType || protocolsMediaType || protocolVideoRef || protocolsVideoRef)) { + errorList.push('IX Bid Adapter: protocol/protcols is not included in either the adunit or params level'); + } + + return errorList; } /** @@ -825,6 +823,66 @@ function removeFromSizes(bannerSizeList, bannerSize) { } } +/** + * Creates IX Video impressions based on validBidRequests + * @param {object} validBidRequest valid request provided by prebid + * @param {object} videoImps reference to created video impressions + */ +function createVideoImps(validBidRequest, videoImps) { + const imp = bidToVideoImp(validBidRequest); + if (Object.keys(imp).length != 0) { + videoImps[validBidRequest.transactionId] = {}; + videoImps[validBidRequest.transactionId].ixImps = []; + videoImps[validBidRequest.transactionId].ixImps.push(imp); + } +} + +/** + * Creates IX banner impressions based on validBidRequests + * @param {object} validBidRequest valid request provided by prebid + * @param {object} missingBannerSizes reference to missing banner config sizes + * @param {object} bannerImps reference to created banner impressions + */ +function createBannerImps(validBidRequest, missingBannerSizes, bannerImps) { + const DEFAULT_IX_CONFIG = { + detectMissingSizes: true, + }; + + const ixConfig = { ...DEFAULT_IX_CONFIG, ...config.getConfig('ix') }; + + let imp = bidToBannerImp(validBidRequest); + + const bannerSizeDefined = includesSize(utils.deepAccess(validBidRequest, 'mediaTypes.banner.sizes'), utils.deepAccess(validBidRequest, 'params.size')); + + // Create IX imps from params.size + if (bannerSizeDefined) { + if (!bannerImps.hasOwnProperty(validBidRequest.transactionId)) { + bannerImps[validBidRequest.transactionId] = {}; + } + if (!bannerImps[validBidRequest.transactionId].hasOwnProperty('ixImps')) { + bannerImps[validBidRequest.transactionId].ixImps = [] + } + bannerImps[validBidRequest.transactionId].ixImps.push(imp); + } + + if (ixConfig.hasOwnProperty('detectMissingSizes') && ixConfig.detectMissingSizes) { + updateMissingSizes(validBidRequest, missingBannerSizes, imp); + } +} + +/** + * Determines IX configuration type based on IX params + * @param {object} valid IX configured param + * @returns {string} + */ +function detectParamsType(validBidRequest) { + if (utils.deepAccess(validBidRequest, 'params.video') && utils.deepAccess(validBidRequest, 'mediaTypes.video')) { + return VIDEO; + } + + return BANNER; +} + /** * Updates the Object to track missing banner sizes. * @@ -933,9 +991,15 @@ export const spec = { return false; } } - // For multi format unit - if (!mediaTypeBannerSizes && (mediaTypeVideoRef || paramsVideoRef)) { - return checkVideoParams(bid, mediaTypeVideoRef, paramsVideoRef); + + if (mediaTypeVideoRef && paramsVideoRef) { + const errorList = checkVideoParams(mediaTypeVideoRef, paramsVideoRef); + if (errorList.length) { + errorList.forEach((err) => { + utils.logError(err); + }); + return false; + } } return true; }, @@ -948,58 +1012,43 @@ export const spec = { * @return {object} Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { - let reqs = []; - let bannerImps = {}; - let videoImps = {}; - let validBidRequest = null; - - // To capture the missing sizes i.e not configured for ix - let missingBannerSizes = {}; + const reqs = []; // Stores banner + video requests + const bannerImps = {}; // Stores created banner impressions + const videoImps = {}; // Stores created video impressions + const multiFormatAdUnits = {}; // Stores references identified multi-format adUnits + const missingBannerSizes = {}; // To capture the missing sizes i.e not configured for ix + + // Step 1: Create impresssions from IX params + validBidRequests.forEach((validBidRequest) => { + const adUnitMediaTypes = Object.keys(utils.deepAccess(validBidRequest, 'mediaTypes', {})) + + switch (detectParamsType(validBidRequest)) { + case BANNER: + createBannerImps(validBidRequest, missingBannerSizes, bannerImps); + break; + case VIDEO: + createVideoImps(validBidRequest, videoImps) + break; + } - const DEFAULT_IX_CONFIG = { - detectMissingSizes: true, - }; + if (includes(adUnitMediaTypes, BANNER) && includes(adUnitMediaTypes, VIDEO)) { + multiFormatAdUnits[validBidRequest.transactionId] = validBidRequest; + } + }); - const ixConfig = { ...DEFAULT_IX_CONFIG, ...config.getConfig('ix') }; - - for (let i = 0; i < validBidRequests.length; i++) { - validBidRequest = validBidRequests[i]; - const videoAdUnitRef = utils.deepAccess(validBidRequest, 'mediaTypes.video'); - const videoParamRef = utils.deepAccess(validBidRequest, 'params.video'); - - // identify video ad unit - if (validBidRequest.mediaType === VIDEO || videoAdUnitRef || videoParamRef) { - if (!videoImps.hasOwnProperty(validBidRequest.transactionId)) { - const imp = bidToVideoImp(validBidRequest); - if (Object.keys(imp).length != 0) { - videoImps[validBidRequest.transactionId] = {}; - videoImps[validBidRequest.transactionId].ixImps = []; - videoImps[validBidRequest.transactionId].ixImps.push(imp); - } - } + // Step 2: Create impressions for multi-format adunits missing configurations + Object.keys(multiFormatAdUnits).forEach((transactionId) => { + const validBidRequest = multiFormatAdUnits[transactionId]; + if (!bannerImps[transactionId]) { + createBannerImps(validBidRequest, missingBannerSizes, bannerImps); } - if (validBidRequest.mediaType === BANNER || - (utils.deepAccess(validBidRequest, 'mediaTypes.banner.sizes')) || - (!validBidRequest.mediaType && !validBidRequest.mediaTypes)) { - let imp = bidToBannerImp(validBidRequest); - // Create IX imps from params.size - if (utils.deepAccess(validBidRequest, 'params.size')) { - if (!bannerImps.hasOwnProperty(validBidRequest.transactionId)) { - bannerImps[validBidRequest.transactionId] = {}; - } - if (!bannerImps[validBidRequest.transactionId].hasOwnProperty('ixImps')) { - bannerImps[validBidRequest.transactionId].ixImps = [] - } - bannerImps[validBidRequest.transactionId].ixImps.push(imp); - } - if (ixConfig.hasOwnProperty('detectMissingSizes') && ixConfig.detectMissingSizes) { - updateMissingSizes(validBidRequest, missingBannerSizes, imp); - } + if (!videoImps[transactionId]) { + createVideoImps(validBidRequest, videoImps) } - } + }); - // Finding the missing banner sizes, and making impressions for them + // Step 3: Update banner impressions with missing sizes for (var transactionId in missingBannerSizes) { if (missingBannerSizes.hasOwnProperty(transactionId)) { let missingSizes = missingBannerSizes[transactionId].missingSizes; @@ -1014,13 +1063,14 @@ export const spec = { let origImp = missingBannerSizes[transactionId].impression; for (let i = 0; i < missingSizes.length; i++) { - let newImp = createMissingBannerImp(validBidRequest, origImp, missingSizes[i]); + let newImp = createMissingBannerImp(validBidRequests[0], origImp, missingSizes[i]); bannerImps[transactionId].missingImps.push(newImp); bannerImps[transactionId].missingCount++; } } } + // Step 4: Build banner & video requests if (Object.keys(bannerImps).length > 0) { reqs.push(...buildRequest(validBidRequests, bidderRequest, bannerImps, BANNER_ENDPOINT_VERSION)); } diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 53bfc74af7f..85236571383 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -1372,12 +1372,6 @@ describe('IndexexchangeAdapter', function () { const requestWithoutSchain = spec.buildRequests(bidWithoutSchain, DEFAULT_OPTION)[0]; const queryWithoutSchain = requestWithoutSchain.data; - const bidWithoutMediaType = utils.deepClone(DEFAULT_BANNER_VALID_BID); - delete bidWithoutMediaType[0].mediaTypes; - bidWithoutMediaType[0].sizes = [[300, 250], [300, 600]]; - const requestWithoutMediaType = spec.buildRequests(bidWithoutMediaType, DEFAULT_OPTION)[0]; - const queryWithoutMediaType = requestWithoutMediaType.data; - it('request should be made to IX endpoint with GET method', function () { expect(requestMethod).to.equal('GET'); expect(requestUrl).to.equal(IX_SECURE_ENDPOINT); @@ -1598,27 +1592,6 @@ describe('IndexexchangeAdapter', function () { }); }); - it('payload without mediaType should have correct format and value', function () { - const payload = JSON.parse(queryWithoutMediaType.r); - - expect(payload.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidderRequestId); - expect(payload.site.page).to.equal(DEFAULT_OPTION.refererInfo.referer); - expect(payload.site.ref).to.equal(document.referrer); - expect(payload.ext.source).to.equal('prebid'); - expect(payload.imp).to.be.an('array'); - expect(payload.imp).to.have.lengthOf(1); - }); - - it('impression without mediaType should have correct format and value', function () { - const impression = JSON.parse(queryWithoutMediaType.r).imp[0]; - - expect(impression.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); - expect(impression.banner.format).to.be.length(1); - expect(impression.banner.format[0].w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]); - expect(impression.banner.format[0].h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]); - expect(impression.banner.topframe).to.be.oneOf([0, 1]); - }); - it('impression should have sid if id is configured as number', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.id = 50; @@ -2005,7 +1978,7 @@ describe('IndexexchangeAdapter', function () { it('should handle unexpected context', function () { const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); - bid.mediaTypes.video.context = 'VaccineJanssen'; + bid.mediaTypes.video.context = 'not-valid'; const request = spec.buildRequests([bid])[0]; const impression = JSON.parse(request.data.r).imp[0]; expect(impression.video.placement).to.be.undefined;