Skip to content

Commit

Permalink
Fix for Outstream and MediaTypePriceGranularity (prebid#4544)
Browse files Browse the repository at this point in the history
* Add microadBidAdapter

* Remove unnecessary encodeURIComponent from microadBidAdapter

* Submit Advangelists Prebid Adapter

* Submit Advangelists Prebid Adapter 1.1

* Correct procudtion endpoint for prebid

* analytics update with wrapper name

* reverted error merge

* update changed default value of netRevenue to true

* fix video mediaTypes granularity for instream and outstream

* refactor inline code to geMediaTypeGranularity method since its used in two location

* fix missing import for INSTREAM

* added test and small code change to optimize conditionals

* add test

* refactor with added test

* added more tests, small fixes

* remove redundant comment

* updates to tests

* tiny update to comment

* updated tests

* updated with fixes test updates
  • Loading branch information
Isaac A. Dettman authored Dec 10, 2019
1 parent 6de3474 commit 5926b49
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 17 deletions.
55 changes: 44 additions & 11 deletions src/auction.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,17 @@
*/

/**
* @typedef {Object} BidRequest
* //TODO add all properties
* @typedef {Object} BidderRequest
*
* @property {string} bidderCode - adUnit bidder
* @property {number} auctionId - random UUID
* @property {string} bidderRequestId - random string, unique key set on all bidRequest.bids[]
* @property {Array.<Bid>} bids
* @property {number} auctionStart - Date.now() at auction start
* @property {number} timeout - callback timeout
* @property {refererInfo} refererInfo - referer info object
* @property {string} [tid] - random UUID (used for s2s)
* @property {string} [src] - s2s or client (used for s2s)
*/

/**
Expand All @@ -48,7 +57,7 @@
* @property {function(): void} callBids - sends requests to all adapters for bids
*/

import { flatten, timestamp, adUnitsFilter, deepAccess, getBidRequest, getValue } from './utils';
import {flatten, timestamp, adUnitsFilter, deepAccess, getBidRequest, getValue} from './utils';
import { parse as parseURL } from './url';
import { getPriceBucketString } from './cpmBucketManager';
import { getNativeTargeting } from './native';
Expand All @@ -59,6 +68,7 @@ import { userSync } from './userSync';
import { hook } from './hook';
import find from 'core-js/library/fn/array/find';
import { OUTSTREAM } from './video';
import { VIDEO } from './mediaTypes';

const { syncUsers } = userSync;
const utils = require('./utils');
Expand All @@ -85,7 +95,11 @@ const queuedCalls = [];
*
* @param {Object} requestConfig
* @param {AdUnit} requestConfig.adUnits
* @param {AdUnitCode} requestConfig.adUnitCode
* @param {AdUnitCode} requestConfig.adUnitCodes
* @param {function():void} requestConfig.callback
* @param {number} requestConfig.cbTimeout
* @param {Array.<string>} requestConfig.labels
* @param {string} requestConfig.auctionId
*
* @returns {Auction} auction instance
*/
Expand Down Expand Up @@ -490,8 +504,7 @@ function getPreparedBidForAuction({adUnitCode, bid, bidderRequest, auctionId}) {
}

// Use the config value 'mediaTypeGranularity' if it has been defined for mediaType, else use 'customPriceBucket'
const mediaTypeGranularity = config.getConfig(`mediaTypePriceGranularity.${bid.mediaType}`);

const mediaTypeGranularity = getMediaTypeGranularity(bid.mediaType, bidReq, config.getConfig('mediaTypePriceGranularity'));
const priceStringsObj = getPriceBucketString(
bidObject.cpm,
(typeof mediaTypeGranularity === 'object') ? mediaTypeGranularity : config.getConfig('customPriceBucket'),
Expand All @@ -518,14 +531,33 @@ function setupBidTargeting(bidObject, bidderRequest) {
bidObject.adserverTargeting = Object.assign(bidObject.adserverTargeting || {}, keyValues);
}

/**
* @param {MediaType} mediaType
* @param {Bid} [bidReq]
* @param {MediaTypePriceGranularity} [mediaTypePriceGranularity]
* @returns {(Object|string|undefined)}
*/
export function getMediaTypeGranularity(mediaType, bidReq, mediaTypePriceGranularity) {
if (mediaType && mediaTypePriceGranularity) {
if (mediaType === VIDEO) {
const context = deepAccess(bidReq, `mediaTypes.${VIDEO}.context`, 'instream');
if (mediaTypePriceGranularity[`${VIDEO}-${context}`]) {
return mediaTypePriceGranularity[`${VIDEO}-${context}`];
}
}
return mediaTypePriceGranularity[mediaType];
}
}

/**
* This function returns the price granularity defined. It can be either publisher defined or default value
* @param {string} mediaType
* @param {BidRequest} bidReq
* @returns {string} granularity
*/
export const getPriceGranularity = (mediaType) => {
export const getPriceGranularity = (mediaType, bidReq) => {
// Use the config value 'mediaTypeGranularity' if it has been set for mediaType, else use 'priceGranularity'
const mediaTypeGranularity = config.getConfig(`mediaTypePriceGranularity.${mediaType}`);
const mediaTypeGranularity = getMediaTypeGranularity(mediaType, bidReq, config.getConfig('mediaTypePriceGranularity'));
const granularity = (typeof mediaType === 'string' && mediaTypeGranularity) ? ((typeof mediaTypeGranularity === 'string') ? mediaTypeGranularity : 'custom') : config.getConfig('priceGranularity');
return granularity;
}
Expand Down Expand Up @@ -556,9 +588,10 @@ export const getPriceByGranularity = (granularity) => {
/**
* @param {string} mediaType
* @param {string} bidderCode
* @param {BidRequest} bidReq
* @returns {*}
*/
export function getStandardBidderSettings(mediaType, bidderCode) {
export function getStandardBidderSettings(mediaType, bidderCode, bidReq) {
// factory for key value objs
function createKeyVal(key, value) {
return {
Expand All @@ -573,7 +606,7 @@ export function getStandardBidderSettings(mediaType, bidderCode) {
};
}
const TARGETING_KEYS = CONSTANTS.TARGETING_KEYS;
const granularity = getPriceGranularity(mediaType);
const granularity = getPriceGranularity(mediaType, bidReq);

let bidderSettings = $$PREBID_GLOBAL$$.bidderSettings;
if (!bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD]) {
Expand Down Expand Up @@ -627,7 +660,7 @@ export function getKeyValueTargetingPairs(bidderCode, custBidObj, bidReq) {
// 1) set the keys from "standard" setting or from prebid defaults
if (bidderSettings) {
// initialize default if not set
const standardSettings = getStandardBidderSettings(custBidObj.mediaType, bidderCode);
const standardSettings = getStandardBidderSettings(custBidObj.mediaType, bidderCode, bidReq);
setKeys(keyValues, standardSettings, custBidObj);

// 2) set keys from specific bidder setting override if they exist
Expand Down
16 changes: 16 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
/*
* Module for getting and setting Prebid configuration.
*/

/**
* @typedef {Object} MediaTypePriceGranularity
*
* @property {(string|Object)} [banner]
* @property {(string|Object)} [native]
* @property {(string|Object)} [video]
* @property {(string|Object)} [video-instream]
* @property {(string|Object)} [video-outstream]
*/

import { isValidPriceConfig } from './cpmBucketManager';
import find from 'core-js/library/fn/array/find';
import includes from 'core-js/library/fn/array/includes';
Expand Down Expand Up @@ -106,7 +117,12 @@ export function newConfig() {
return this._customPriceBucket;
},

/**
* mediaTypePriceGranularity
* @type {MediaTypePriceGranularity}
*/
_mediaTypePriceGranularity: {},

get mediaTypePriceGranularity() {
return this._mediaTypePriceGranularity;
},
Expand Down
81 changes: 80 additions & 1 deletion test/spec/auctionmanager_spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getKeyValueTargetingPairs, auctionCallbacks, AUCTION_COMPLETED } from 'src/auction';
import CONSTANTS from 'src/constants.json';
import { adjustBids } from 'src/auction';
import { adjustBids, getMediaTypeGranularity } from 'src/auction';
import * as auctionModule from 'src/auction';
import { registerBidder } from 'src/adapters/bidderFactory';
import { createBid } from 'src/bidfactory';
Expand All @@ -25,6 +25,10 @@ const BIDDER_CODE1 = 'sampleBidder1';
const ADUNIT_CODE = 'adUnit-code';
const ADUNIT_CODE1 = 'adUnit-code-1';

/**
* @param {Object} [opts]
* @returns {Bid}
*/
function mockBid(opts) {
let bidderCode = opts && opts.bidderCode;

Expand All @@ -43,6 +47,11 @@ function mockBid(opts) {
};
}

/**
* @param {Bid} bid
* @param {Object} [opts]
* @returns {BidRequest}
*/
function mockBidRequest(bid, opts) {
if (!bid) {
throw new Error('bid required');
Expand Down Expand Up @@ -976,6 +985,76 @@ describe('auctionmanager.js', function () {
});
});

describe('getMediaTypeGranularity', function () {
it('video', function () {
let bidReq = {
'mediaTypes': { video: {id: '1'} }
};

// mediaType is video and video.context is undefined
expect(getMediaTypeGranularity('video', bidReq, {
banner: 'low',
video: 'medium'
})).to.equal('medium');

expect(getMediaTypeGranularity('video', {}, {
banner: 'low',
video: 'medium'
})).to.equal('medium');
``
expect(getMediaTypeGranularity('video', undefined, {
banner: 'low',
video: 'medium'
})).to.equal('medium');

// also when mediaTypes.video is undefined
bidReq = {
'mediaTypes': { banner: {} }
};
expect(getMediaTypeGranularity('video', bidReq, {
banner: 'low',
video: 'medium'
})).to.equal('medium');

// also when mediaTypes is undefined
expect(getMediaTypeGranularity('video', {}, {
banner: 'low',
video: 'medium'
})).to.equal('medium');
});

it('video-outstream', function () {
let bidReq = { 'mediaTypes': { video: { context: 'outstream' } } };

expect(getMediaTypeGranularity('video', bidReq, {
'banner': 'low', 'video': 'medium', 'video-outstream': 'high'
})).to.equal('high');
});

it('video-instream', function () {
let bidReq = { 'mediaTypes': { video: { context: 'instream' } } };

expect(getMediaTypeGranularity('video', bidReq, {
banner: 'low', video: 'medium', 'video-instream': 'high'
})).to.equal('high');

// fall back to video if video-instream not found
expect(getMediaTypeGranularity('video', bidReq, {
banner: 'low', video: 'medium'
})).to.equal('medium');

expect(getMediaTypeGranularity('video', {mediaTypes: {banner: {}}}, {
banner: 'low', video: 'medium'
})).to.equal('medium');
});

it('native', function () {
expect(getMediaTypeGranularity('native', {mediaTypes: {native: {}}}, {
banner: 'low', video: 'medium', native: 'high'
})).to.equal('high');
});
});

describe('auctionCallbacks', function() {
let bids = TEST_BIDS;
let bidRequests;
Expand Down
17 changes: 12 additions & 5 deletions test/spec/config_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,26 +137,33 @@ describe('config API', function () {
});

it('set mediaTypePriceGranularity', function () {
const customPriceGranularity = {
const customPriceGranularityVideo = {
'buckets': [{
'min': 0,
'max': 3,
'increment': 0.01,
'cap': true
}]
};
const customPriceGranularityInstream = utils.deepClone(customPriceGranularityVideo);
const customPriceGranularityOutstream = utils.deepClone(customPriceGranularityVideo);

setConfig({
'mediaTypePriceGranularity': {
'banner': 'medium',
'video': customPriceGranularity,
'native': 'medium'
'video': customPriceGranularityVideo,
'video-instream': customPriceGranularityInstream,
'video-outstream': customPriceGranularityOutstream,
'native': 'high'
}
});

const configResult = getConfig('mediaTypePriceGranularity');
expect(configResult.banner).to.be.equal('medium');
expect(configResult.video).to.be.equal(customPriceGranularity);
expect(configResult.native).to.be.equal('medium');
expect(configResult.video).to.be.equal(customPriceGranularityVideo);
expect(configResult['video-instream']).to.be.equal(customPriceGranularityInstream);
expect(configResult['video-outstream']).to.be.equal(customPriceGranularityOutstream);
expect(configResult.native).to.be.equal('high');
});

it('sets priceGranularity and customPriceBucket', function () {
Expand Down

0 comments on commit 5926b49

Please sign in to comment.