diff --git a/src/adaptermanager.js b/src/adaptermanager.js index 9b2abe79f01..32cbc55cd8f 100644 --- a/src/adaptermanager.js +++ b/src/adaptermanager.js @@ -49,13 +49,22 @@ function getLabels(bidOrAdUnit, activeLabels) { function getBids({bidderCode, auctionId, bidderRequestId, adUnits, labels}) { return adUnits.reduce((result, adUnit) => { - let bannerSizes = utils.deepAccess(adUnit, 'mediaTypes.banner.sizes'); - - let {active, sizes: filteredAdUnitSizes} = resolveStatus( + let { + active, + mediaTypes: filteredMediaTypes, + filterResults + } = resolveStatus( getLabels(adUnit, labels), - bannerSizes || adUnit.sizes + adUnit.mediaTypes, + adUnit.sizes ); + if (!active) { + utils.logInfo(`Size mapping disabled adUnit "${adUnit.code}"`); + } else if (filterResults) { + utils.logInfo(`Size mapping filtered adUnit "${adUnit.code}" banner sizes from `, filterResults.before, 'to ', filterResults.after); + } + if (active) { result.push(adUnit.bids.filter(bid => bid.bidder === bidderCode) .reduce((bids, bid) => { @@ -72,29 +81,33 @@ function getBids({bidderCode, auctionId, bidderRequestId, adUnits, labels}) { 'renderer' ])); - let {active, sizes} = resolveStatus(getLabels(bid, labels), filteredAdUnitSizes); - - if (adUnit.mediaTypes) { - if (utils.isValidMediaTypes(adUnit.mediaTypes)) { - if (bannerSizes) { - adUnit.mediaTypes.banner.sizes = sizes; - } - - bid = Object.assign({}, bid, { - mediaTypes: adUnit.mediaTypes - }); - } else { - utils.logError( - `mediaTypes is not correctly configured for adunit ${adUnit.code}` - ); - } + let { + active, + mediaTypes, + filterResults + } = resolveStatus(getLabels(bid, labels), filteredMediaTypes); + + if (!active) { + utils.logInfo(`Size mapping deactivated adUnit "${adUnit.code}" bidder "${bid.bidder}"`); + } else if (filterResults) { + utils.logInfo(`Size mapping filtered adUnit "${adUnit.code}" bidder "${bid.bidder}" banner sizes from `, filterResults.before, 'to ', filterResults.after); + } + + if (utils.isValidMediaTypes(mediaTypes)) { + bid = Object.assign({}, bid, { + mediaTypes + }); + } else { + utils.logError( + `mediaTypes is not correctly configured for adunit ${adUnit.code}` + ); } if (active) { bids.push(Object.assign({}, bid, { adUnitCode: adUnit.code, transactionId: adUnit.transactionId, - sizes: sizes, + sizes: utils.deepAccess(mediaTypes, 'banner.sizes') || [], bidId: bid.bid_id || utils.getUniqueIdentifierStr(), bidderRequestId, auctionId, diff --git a/src/auction.js b/src/auction.js index a77708486f2..83768ef8724 100644 --- a/src/auction.js +++ b/src/auction.js @@ -175,54 +175,58 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels}) let requests = {}; - let call = { - bidRequests, - run: () => { - startAuctionTimer(); - - _auctionStatus = AUCTION_IN_PROGRESS; - - const auctionInit = { - timestamp: _auctionStart, - auctionId: _auctionId, - timeout: _timeout - }; - events.emit(CONSTANTS.EVENTS.AUCTION_INIT, auctionInit); - - let callbacks = auctionCallbacks(auctionDone, this); - let boundObj = { - auctionAddBidResponse: callbacks.addBidResponse - } - adaptermanager.callBids(_adUnits, bidRequests, addBidResponse.bind(boundObj), callbacks.adapterDone, { - request(source, origin) { - increment(outstandingRequests, origin); - increment(requests, source); - - if (!sourceInfo[source]) { - sourceInfo[source] = { - SRA: true, - origin - }; - } - if (requests[source] > 1) { - sourceInfo[source].SRA = false; - } - }, - done(origin) { - outstandingRequests[origin]--; - if (queuedCalls[0]) { - if (runIfOriginHasCapacity(queuedCalls[0])) { - queuedCalls.shift(); + if (bidRequests.length < 1) { + utils.logWarn('No valid bid requests returned for auction'); + } else { + let call = { + bidRequests, + run: () => { + startAuctionTimer(); + + _auctionStatus = AUCTION_IN_PROGRESS; + + const auctionInit = { + timestamp: _auctionStart, + auctionId: _auctionId, + timeout: _timeout + }; + events.emit(CONSTANTS.EVENTS.AUCTION_INIT, auctionInit); + + let callbacks = auctionCallbacks(auctionDone, this); + let boundObj = { + auctionAddBidResponse: callbacks.addBidResponse + }; + adaptermanager.callBids(_adUnits, bidRequests, addBidResponse.bind(boundObj), callbacks.adapterDone, { + request(source, origin) { + increment(outstandingRequests, origin); + increment(requests, source); + + if (!sourceInfo[source]) { + sourceInfo[source] = { + SRA: true, + origin + }; + } + if (requests[source] > 1) { + sourceInfo[source].SRA = false; + } + }, + done(origin) { + outstandingRequests[origin]--; + if (queuedCalls[0]) { + if (runIfOriginHasCapacity(queuedCalls[0])) { + queuedCalls.shift(); + } } } - } - }, _timeout); - } - }; + }, _timeout); + } + }; - if (!runIfOriginHasCapacity(call)) { - utils.logWarn('queueing auction due to limited endpoint capacity'); - queuedCalls.push(call); + if (!runIfOriginHasCapacity(call)) { + utils.logWarn('queueing auction due to limited endpoint capacity'); + queuedCalls.push(call); + } } function runIfOriginHasCapacity(call) { diff --git a/src/sizeMapping.js b/src/sizeMapping.js index 5533c6b4efe..a8fec661bd1 100644 --- a/src/sizeMapping.js +++ b/src/sizeMapping.js @@ -1,5 +1,5 @@ import { config } from 'src/config'; -import { logWarn } from 'src/utils'; +import {logWarn, isPlainObject, deepAccess, deepClone} from 'src/utils'; import includes from 'core-js/library/fn/array/includes'; let sizeConfig = []; @@ -29,36 +29,67 @@ config.getConfig('sizeConfig', config => setSizeConfig(config.sizeConfig)); * @param {Array} labels Labels specified on adUnit or bidder * @param {boolean} labelAll if true, all labels must match to be enabled * @param {Array} activeLabels Labels passed in through requestBids - * @param {Array>} sizes Sizes specified on adUnit + * @param {object} mediaTypes A mediaTypes object describing the various media types (banner, video, native) + * @param {Array>} sizes Sizes specified on adUnit (deprecated) * @param {Array} configs * @returns {{labels: Array, sizes: Array>}} */ -export function resolveStatus({labels = [], labelAll = false, activeLabels = []} = {}, sizes = [], configs = sizeConfig) { +export function resolveStatus({labels = [], labelAll = false, activeLabels = []} = {}, mediaTypes, sizes, configs = sizeConfig) { let maps = evaluateSizeConfig(configs); - let filteredSizes; - if (maps.shouldFilter) { - filteredSizes = sizes.filter(size => maps.sizesSupported[size]); + if (!isPlainObject(mediaTypes)) { + mediaTypes = {}; } else { - filteredSizes = sizes; + mediaTypes = deepClone(mediaTypes); } - return { - active: filteredSizes.length > 0 && ( - labels.length === 0 || ( - (!labelAll && ( - labels.some(label => maps.labels[label]) || - labels.some(label => includes(activeLabels, label)) - )) || - (labelAll && ( - labels.reduce((result, label) => !result ? result : ( - maps.labels[label] || includes(activeLabels, label) - ), true) - )) + // add support for deprecated adUnit.sizes by creating correct banner mediaTypes if they don't already exist + if (sizes) { + if (!mediaTypes.banner) { + mediaTypes.banner = { + sizes + } + } else if (!mediaTypes.banner.sizes) { + mediaTypes.banner.sizes = sizes; + } + } + + let oldSizes = deepAccess(mediaTypes, 'banner.sizes'); + if (maps.shouldFilter && oldSizes) { + mediaTypes.banner.sizes = oldSizes.filter(size => maps.sizesSupported[size]); + } + + let allMediaTypes = Object.keys(mediaTypes); + + let results = { + active: ( + allMediaTypes.length > 1 || (allMediaTypes.length === 1 && allMediaTypes[0] !== 'banner') + ) || ( + allMediaTypes[0] === 'banner' && deepAccess(mediaTypes, 'banner.sizes.length') > 0 && ( + labels.length === 0 || ( + (!labelAll && ( + labels.some(label => maps.labels[label]) || + labels.some(label => includes(activeLabels, label)) + )) || + (labelAll && ( + labels.reduce((result, label) => !result ? result : ( + maps.labels[label] || includes(activeLabels, label) + ), true) + )) + ) ) ), - sizes: filteredSizes + mediaTypes }; + + if (oldSizes && oldSizes.length !== mediaTypes.banner.sizes.length) { + results.filterResults = { + before: oldSizes, + after: mediaTypes.banner.sizes + } + } + + return results; } function evaluateSizeConfig(configs) { diff --git a/test/spec/sizeMapping_spec.js b/test/spec/sizeMapping_spec.js index 15213958296..ac51d405e36 100644 --- a/test/spec/sizeMapping_spec.js +++ b/test/spec/sizeMapping_spec.js @@ -6,7 +6,11 @@ let utils = require('src/utils'); let deepClone = utils.deepClone; describe('sizeMapping', function () { - var testSizes = [[970, 90], [728, 90], [300, 250], [300, 100], [80, 80]]; + var testSizes = { + banner: { + sizes: [[970, 90], [728, 90], [300, 250], [300, 100], [80, 80]] + } + }; var sizeConfig = [{ 'mediaQuery': '(min-width: 1200px)', @@ -78,19 +82,34 @@ describe('sizeMapping', function () { sandbox.stub(utils, 'logWarn'); - resolveStatus(undefined, testSizes, errorConfig); + resolveStatus(undefined, testSizes, undefined, errorConfig); expect(utils.logWarn.firstCall.args[0]).to.match(/missing.+?mediaQuery/); }); + it('should allow deprecated adUnit.sizes', function() { + matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; + + let status = resolveStatus(undefined, undefined, testSizes.banner.sizes, sizeConfig); + + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal({ + banner: { + sizes: [[970, 90], [728, 90], [300, 250]] + } + }); + }); + it('when one mediaQuery block matches, it should filter the adUnit.sizes passed in', function () { matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; - let status = resolveStatus(undefined, testSizes, sizeConfig); + let status = resolveStatus(undefined, testSizes, undefined, sizeConfig); - expect(status).to.deep.equal({ - active: true, - sizes: [[970, 90], [728, 90], [300, 250]] - }) + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal({ + banner: { + sizes: [[970, 90], [728, 90], [300, 250]] + } + }); }); it('when multiple mediaQuery block matches, it should filter a union of the matched sizesSupported', function () { @@ -99,41 +118,60 @@ describe('sizeMapping', function () { '(min-width: 768px) and (max-width: 1199px)' ], str) ? {matches: true} : {matches: false}; - let status = resolveStatus(undefined, testSizes, sizeConfig); - expect(status).to.deep.equal({ - active: true, - sizes: [[970, 90], [728, 90], [300, 250], [300, 100]] - }) + let status = resolveStatus(undefined, testSizes, undefined, sizeConfig); + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal({ + banner: { + sizes: [[970, 90], [728, 90], [300, 250], [300, 100]] + } + }); }); it('if no mediaQueries match, it should allow all sizes specified', function () { matchMediaOverride = () => ({matches: false}); - let status = resolveStatus(undefined, testSizes, sizeConfig); - expect(status).to.deep.equal({ - active: true, - sizes: testSizes - }) + let status = resolveStatus(undefined, testSizes, undefined, sizeConfig); + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal(testSizes); }); it('if a mediaQuery matches and has sizesSupported: [], it should filter all sizes', function () { matchMediaOverride = (str) => str === '(min-width: 0px) and (max-width: 767px)' ? {matches: true} : {matches: false}; - let status = resolveStatus(undefined, testSizes, sizeConfig); - expect(status).to.deep.equal({ - active: false, - sizes: [] - }) + let status = resolveStatus(undefined, testSizes, undefined, sizeConfig); + expect(status.active).to.equal(false); + expect(status.mediaTypes).to.deep.equal({ + banner: { + sizes: [] + } + }); }); - it('if a mediaQuery matches and no sizesSupported specified, it should not effect adUnit.sizes', function () { + it('should filter all banner sizes but not disable adUnit if multiple mediaTypes are present', function () { + matchMediaOverride = (str) => str === '(min-width: 0px) and (max-width: 767px)' ? {matches: true} : {matches: false}; + + let status = resolveStatus(undefined, Object.assign({}, testSizes, { + native: { + type: 'image' + } + }), undefined, sizeConfig); + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal({ + banner: { + sizes: [] + }, + native: { + type: 'image' + } + }); + }); + + it('if a mediaQuery matches and no sizesSupported specified, it should not affect adUnit.sizes', function () { matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; - let status = resolveStatus(undefined, testSizes, sizeConfigWithLabels); - expect(status).to.deep.equal({ - active: true, - sizes: testSizes - }) + let status = resolveStatus(undefined, testSizes, undefined, sizeConfigWithLabels); + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal(testSizes); }); }); @@ -143,21 +181,19 @@ describe('sizeMapping', function () { let status = resolveStatus({ labels: ['desktop'] - }, testSizes, sizeConfigWithLabels); + }, testSizes, undefined, sizeConfigWithLabels); expect(status).to.deep.equal({ active: true, - sizes: testSizes + mediaTypes: testSizes }); status = resolveStatus({ labels: ['tablet'] - }, testSizes, sizeConfigWithLabels); + }, testSizes, undefined, sizeConfigWithLabels); - expect(status).to.deep.equal({ - active: false, - sizes: testSizes - }); + expect(status.active).to.equal(false); + expect(status.mediaTypes).to.deep.equal(testSizes); }); it('should active/deactivate adUnits/bidders based on requestBids labels', function () { @@ -166,44 +202,36 @@ describe('sizeMapping', function () { let status = resolveStatus({ labels: ['uk-visitor'], activeLabels - }, testSizes, sizeConfigWithLabels); + }, testSizes, undefined, sizeConfigWithLabels); - expect(status).to.deep.equal({ - active: false, - sizes: testSizes - }); + expect(status.active).to.equal(false); + expect(status.mediaTypes).to.deep.equal(testSizes); status = resolveStatus({ labels: ['us-visitor'], activeLabels - }, testSizes, sizeConfigWithLabels); + }, testSizes, undefined, sizeConfigWithLabels); - expect(status).to.deep.equal({ - active: true, - sizes: testSizes - }); + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal(testSizes); status = resolveStatus({ labels: ['us-visitor', 'tablet'], labelAll: true, activeLabels - }, testSizes, sizeConfigWithLabels); + }, testSizes, undefined, sizeConfigWithLabels); - expect(status).to.deep.equal({ - active: false, - sizes: testSizes - }); + expect(status.active).to.equal(false); + expect(status.mediaTypes).to.deep.equal(testSizes); status = resolveStatus({ labels: ['us-visitor', 'desktop'], labelAll: true, activeLabels - }, testSizes, sizeConfigWithLabels); + }, testSizes, undefined, sizeConfigWithLabels); - expect(status).to.deep.equal({ - active: true, - sizes: testSizes - }); + expect(status.active).to.equal(true); + expect(status.mediaTypes).to.deep.equal(testSizes); }); }); });