Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IX Adapter: buildRequests refactor #7364

Merged
merged 2 commits into from
Sep 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 115 additions & 65 deletions modules/ixBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 {};
}

Expand Down Expand Up @@ -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];
}
Expand All @@ -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');
Expand All @@ -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;
}

/**
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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;
},
Expand All @@ -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;
Expand All @@ -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));
}
Expand Down
29 changes: 1 addition & 28 deletions test/spec/modules/ixBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down