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

Allow multiple media type rubcion bid w/ invalid video to go through as banner #3037

Merged
merged 1 commit into from
Sep 12, 2018
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
84 changes: 57 additions & 27 deletions modules/rubiconBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ function isSecure() {
}

// use protocol relative urls for http or https
const FASTLANE_ENDPOINT = '//fastlane.rubiconproject.com/a/api/fastlane.json';
const VIDEO_ENDPOINT = '//fastlane-adv.rubiconproject.com/v1/auction/video';
const SYNC_ENDPOINT = 'https://eus.rubiconproject.com/usync.html';
export const FASTLANE_ENDPOINT = '//fastlane.rubiconproject.com/a/api/fastlane.json';
export const VIDEO_ENDPOINT = '//fastlane-adv.rubiconproject.com/v1/auction/video';
export const SYNC_ENDPOINT = 'https://eus.rubiconproject.com/usync.html';

const TIMEOUT_BUFFER = 500;

Expand Down Expand Up @@ -94,26 +94,7 @@ export const spec = {
return false;
}

if (hasVideoMediaType(bid)) {
// Log warning if mediaTypes contains both 'banner' and 'video'
if (utils.deepAccess(bid, `mediaTypes.${VIDEO}.context`) === 'instream' || bid.mediaType === VIDEO) {
if (typeof utils.deepAccess(bid, 'params.video.size_id') === 'undefined') {
utils.logError('Rubicon bid adapter Error: size id is missing for instream video request.');
return false;
}
} else if (utils.deepAccess(bid, `mediaTypes.${VIDEO}.context`) === 'outstream') {
if (utils.deepAccess(bid, 'params.video.size_id') !== 203) {
utils.logWarn('Rubicon bid adapter Warning: outstream video is sending invalid size id, converting size id to 203.');
}
} else {
utils.logError('Rubicon bid adapter Error: no instream or outstream context defined in mediaTypes.');
return false;
}
if (typeof utils.deepAccess(bid, `mediaTypes.${BANNER}`) !== 'undefined') {
utils.logWarn('Rubicon bid adapter Warning: video and banner requested for same ad unit, continuing with video request, multi-format request is not supported by rubicon yet.');
}
}
return parseSizes(bid).length > 0;
return !!bidType(bid, true);
},
/**
* @param {BidRequest[]} bidRequests
Expand All @@ -123,7 +104,7 @@ export const spec = {
buildRequests: function (bidRequests, bidderRequest) {
// separate video bids because the requests are structured differently
let requests = [];
const videoRequests = bidRequests.filter(hasVideoMediaType).map(bidRequest => {
const videoRequests = bidRequests.filter(bidRequest => bidType(bidRequest) === 'video').map(bidRequest => {
bidRequest.startTime = new Date().getTime();

let params = bidRequest.params;
Expand Down Expand Up @@ -190,7 +171,7 @@ export const spec = {

if (config.getConfig('rubicon.singleRequest') !== true) {
// bids are not grouped if single request mode is not enabled
requests = videoRequests.concat(bidRequests.filter(bidRequest => !hasVideoMediaType(bidRequest)).map(bidRequest => {
requests = videoRequests.concat(bidRequests.filter(bidRequest => bidType(bidRequest) === 'banner').map(bidRequest => {
const bidParams = spec.createSlotParams(bidRequest, bidderRequest);
return {
method: 'GET',
Expand All @@ -205,7 +186,7 @@ export const spec = {
} else {
// single request requires bids to be grouped by site id into a single request
// note: utils.groupBy wasn't used because deep property access was needed
const nonVideoRequests = bidRequests.filter(bidRequest => !hasVideoMediaType(bidRequest));
const nonVideoRequests = bidRequests.filter(bidRequest => bidType(bidRequest) === 'banner');
const groupedBidRequests = nonVideoRequests.reduce((groupedBids, bid) => {
(groupedBids[bid.params['siteId']] = groupedBids[bid.params['siteId']] || []).push(bid);
return groupedBids;
Expand Down Expand Up @@ -394,7 +375,7 @@ export const spec = {
let ads = responseObj.ads;

// video ads array is wrapped in an object
if (typeof bidRequest === 'object' && !Array.isArray(bidRequest) && hasVideoMediaType(bidRequest) && typeof ads === 'object') {
if (typeof bidRequest === 'object' && !Array.isArray(bidRequest) && bidType(bidRequest) === 'video' && typeof ads === 'object') {
ads = ads[bidRequest.adUnitCode];
}

Expand Down Expand Up @@ -595,6 +576,55 @@ export function hasVideoMediaType(bidRequest) {
return bidRequest.mediaType === VIDEO || typeof utils.deepAccess(bidRequest, `mediaTypes.${VIDEO}`) !== 'undefined';
}

/**
* Determine bidRequest mediaType
* @param bid the bid to test
* @param log whether we should log errors/warnings for invalid bids
* @returns {string|undefined} Returns 'video' or 'banner' if resolves to a type, or undefined otherwise (invalid).
*/
function bidType(bid, log = false) {
let validVideo;
if (hasVideoMediaType(bid)) {
validVideo = true;

if (utils.deepAccess(bid, `mediaTypes.${VIDEO}.context`) === 'instream' || bid.mediaType === VIDEO) {
if (typeof utils.deepAccess(bid, 'params.video.size_id') === 'undefined') {
if (log) {
utils.logError('Rubicon bid adapter Error: size id is missing for instream video request.');
}
validVideo = false;
}
} else if (utils.deepAccess(bid, `mediaTypes.${VIDEO}.context`) === 'outstream') {
if (utils.deepAccess(bid, 'params.video.size_id') !== 203) {
if (log) {
utils.logWarn('Rubicon bid adapter Warning: outstream video is sending invalid size id, converting size id to 203.');
}
}
} else {
if (log) {
utils.logError('Rubicon bid adapter Error: no instream or outstream context defined in mediaTypes.');
}
validVideo = false;
}
if (validVideo) {
if (typeof utils.deepAccess(bid, `mediaTypes.${BANNER}`) !== 'undefined') {
if (log) {
utils.logWarn('Rubicon bid adapter Warning: video and banner requested for same adUnit, continuing with video request, multi-format request is not supported by rubicon yet.');
}
}
return 'video';
} else if (typeof utils.deepAccess(bid, `mediaTypes.${BANNER}`) === 'undefined') {
return undefined;
}
}
if (parseSizes(bid).length > 0) {
if (log && validVideo === false) {
utils.logWarn('Rubicon bid adapter Warning: invalid video requested for adUnit, continuing with banner request.');
}
return 'banner';
}
}

export function masSizeOrdering(sizes) {
const MAS_SIZE_PRIORITY = [15, 2, 9];

Expand Down
23 changes: 21 additions & 2 deletions test/spec/modules/rubiconBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {expect} from 'chai';
import adapterManager from 'src/adaptermanager';
import {spec, masSizeOrdering, resetUserSync, hasVideoMediaType} from 'modules/rubiconBidAdapter';
import {spec, masSizeOrdering, resetUserSync, hasVideoMediaType, FASTLANE_ENDPOINT} from 'modules/rubiconBidAdapter';
import {parse as parseQuery} from 'querystring';
import {newBidder} from 'src/adapters/bidderFactory';
import {userSync} from 'src/userSync';
Expand Down Expand Up @@ -1286,7 +1286,26 @@ describe('the rubicon adapter', function () {
expect(request.data.slots[0].size_id).to.equal(203);
});

it('should get size from bid.sizes too', function () {
it('should send request as banner when invalid video bid in multiple mediaType bidRequest', function () {
createVideoBidderRequestNoVideo();

let bid = bidderRequest.bids[0];
bid.mediaTypes.banner = {
sizes: [[300, 250]]
};

sandbox.stub(Date, 'now').callsFake(() =>
bidderRequest.auctionStart + 100
);

const bidRequestCopy = clone(bidderRequest);

let requests = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy);
expect(requests.length).to.equal(1);
expect(requests[0].url).to.equal(FASTLANE_ENDPOINT);
});

it('should get size from bid.sizes too', () => {
createVideoBidderRequestNoPlayer();
sandbox.stub(Date, 'now').callsFake(() =>
bidderRequest.auctionStart + 100
Expand Down