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

Update to Rubicon Adapter for mediaTypes support #2272

Merged
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
32 changes: 20 additions & 12 deletions modules/rubiconBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as utils from 'src/utils';
import { registerBidder } from 'src/adapters/bidderFactory';
import { config } from 'src/config';
import { BANNER, VIDEO } from 'src/mediaTypes';

const INTEGRATION = 'pbjs_lite_v$prebid.version$';

Expand Down Expand Up @@ -73,7 +74,7 @@ utils._each(sizeMap, (item, key) => sizeMap[item] = key);
export const spec = {
code: 'rubicon',
aliases: ['rubiconLite'],
supportedMediaTypes: ['banner', 'video'],
supportedMediaTypes: [BANNER, VIDEO],
/**
* @param {object} bid
* @return boolean
Expand All @@ -93,8 +94,9 @@ export const spec = {
return false;
}

if (bid.mediaType === 'video') {
if (typeof params.video !== 'object' || !params.video.size_id) {
if (spec.hasVideoMediaType(bid)) {
// support instream only
if ((utils.deepAccess(bid, `mediaTypes.${VIDEO}`) && utils.deepAccess(bid, `mediaTypes.${VIDEO}.context`) !== 'instream') || typeof params.video !== 'object' || !params.video.size_id) {
return false;
}
}
Expand All @@ -118,7 +120,7 @@ export const spec = {

page_url = bidRequest.params.secure ? page_url.replace(/^http:/i, 'https:') : page_url;

if (bidRequest.mediaType === 'video') {
if (spec.hasVideoMediaType(bidRequest)) {
let params = bidRequest.params;
let size = parseSizes(bidRequest);

Expand Down Expand Up @@ -237,6 +239,15 @@ export const spec = {
};
});
},
/**
* Test if bid has mediaType or mediaTypes set for video.
* note: 'mediaType' has been deprecated, however support will remain for a transitional period
* @param {BidRequest} bidRequest
* @returns {boolean}
*/
hasVideoMediaType: function(bidRequest) {
return bidRequest.mediaType === VIDEO || typeof utils.deepAccess(bidRequest, `mediaTypes.${VIDEO}`) !== 'undefined';
},
/**
* @param {*} responseObj
* @param {BidRequest} bidRequest
Expand All @@ -252,7 +263,7 @@ export const spec = {
}

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

Expand Down Expand Up @@ -287,7 +298,7 @@ export const spec = {
bid.mediaType = ad.creative_type;
}

if (bidRequest.mediaType === 'video') {
if (ad.creative_type === VIDEO) {
bid.width = bidRequest.params.video.playerWidth;
bid.height = bidRequest.params.video.playerHeight;
bid.vastUrl = ad.creative_depot_url;
Expand Down Expand Up @@ -360,17 +371,14 @@ function _renderCreative(script, impId) {

function parseSizes(bid) {
let params = bid.params;
if (bid.mediaType === 'video') {
if (spec.hasVideoMediaType(bid)) {
let size = [];
if (params.video.playerWidth && params.video.playerHeight) {
if (typeof params.video === 'object' && params.video.playerWidth && params.video.playerHeight) {
size = [
params.video.playerWidth,
params.video.playerHeight
];
} else if (
Array.isArray(bid.sizes) && bid.sizes.length > 0 &&
Array.isArray(bid.sizes[0]) && bid.sizes[0].length > 1
) {
} else if (Array.isArray(bid.sizes) && bid.sizes.length > 0 && Array.isArray(bid.sizes[0]) && bid.sizes[0].length > 1) {
size = bid.sizes[0];
}
return size;
Expand Down
243 changes: 232 additions & 11 deletions test/spec/modules/rubiconBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,31 @@ describe('the rubicon adapter', () => {

function createVideoBidderRequest() {
let bid = bidderRequest.bids[0];

bid.mediaTypes = {
video: {
context: 'instream'
}
};

bid.params.video = {
'language': 'en',
'p_aso.video.ext.skip': true,
'p_aso.video.ext.skipdelay': 15,
'playerHeight': 320,
'playerWidth': 640,
'size_id': 201,
'aeParams': {
'p_aso.video.ext.skip': '1',
'p_aso.video.ext.skipdelay': '15'
}
};
}

function createLegacyVideoBidderRequest() {
let bid = bidderRequest.bids[0];

// Legacy property (Prebid <1.0)
bid.mediaType = 'video';
bid.params.video = {
'language': 'en',
Expand All @@ -33,12 +58,62 @@ describe('the rubicon adapter', () => {
}

function createVideoBidderRequestNoVideo() {
let bid = bidderRequest.bids[0];
bid.mediaTypes = {
video: {
context: 'instream'
},
};
bid.params.video = '';
}

function createLegacyVideoBidderRequestNoVideo() {
let bid = bidderRequest.bids[0];
bid.mediaType = 'video';
bid.params.video = '';
}

function createVideoBidderRequestOutstream() {
let bid = bidderRequest.bids[0];
bid.mediaTypes = {
video: {
context: 'outstream'
},
};
bid.params.video = {
'language': 'en',
'p_aso.video.ext.skip': true,
'p_aso.video.ext.skipdelay': 15,
'playerHeight': 320,
'playerWidth': 640,
'size_id': 201,
'aeParams': {
'p_aso.video.ext.skip': '1',
'p_aso.video.ext.skipdelay': '15'
}
};
}

function createVideoBidderRequestNoPlayer() {
let bid = bidderRequest.bids[0];
bid.mediaTypes = {
video: {
context: 'instream'
},
};
bid.params.video = {
'language': 'en',
'p_aso.video.ext.skip': true,
'p_aso.video.ext.skipdelay': 15,
'size_id': 201,
'aeParams': {
'p_aso.video.ext.skip': '1',
'p_aso.video.ext.skipdelay': '15'
}
};
}

function createLegacyVideoBidderRequestNoPlayer() {
let bid = bidderRequest.bids[0];
bid.mediaType = 'video';
bid.params.video = {
Expand Down Expand Up @@ -81,6 +156,7 @@ describe('the rubicon adapter', () => {
referrer: 'localhost'
},
adUnitCode: '/19968336/header-bid-tag-0',
code: 'div-1',
sizes: [[300, 250], [320, 50]],
bidId: '2ffb201a808da7',
bidderRequestId: '178e34bad3658f',
Expand Down Expand Up @@ -120,8 +196,7 @@ describe('the rubicon adapter', () => {
describe('for requests', () => {
describe('to fastlane', () => {
it('should make a well-formed request objects', () => {
sandbox.stub(Math, 'random').returns(0.1);

sandbox.stub(Math, 'random').callsFake(() => 0.1);
let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest);
let data = parseQuery(request.data);

Expand Down Expand Up @@ -451,6 +526,67 @@ describe('the rubicon adapter', () => {
});

describe('for video requests', () => {
it('should make a well-formed video request with legacy mediaType config', () => {
createLegacyVideoBidderRequest();

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

let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest);
let post = request.data;

let url = request.url;

expect(url).to.equal('//fastlane-adv.rubiconproject.com/v1/auction/video');

expect(post).to.have.property('page_url').that.is.a('string');
expect(post.resolution).to.match(/\d+x\d+/);
expect(post.account_id).to.equal('14062');
expect(post.integration).to.equal(INTEGRATION);
expect(post['x_source.tid']).to.equal('d45dd707-a418-42ec-b8a7-b70a6c6fab0b');
expect(post).to.have.property('timeout').that.is.a('number');
expect(post.timeout < 5000).to.equal(true);
expect(post.stash_creatives).to.equal(true);

expect(post).to.have.property('ae_pass_through_parameters');
expect(post.ae_pass_through_parameters)
.to.have.property('p_aso.video.ext.skip')
.that.equals('1');
expect(post.ae_pass_through_parameters)
.to.have.property('p_aso.video.ext.skipdelay')
.that.equals('15');

expect(post).to.have.property('slots')
.with.length.of(1);

let slot = post.slots[0];

expect(slot.site_id).to.equal('70608');
expect(slot.zone_id).to.equal('335918');
expect(slot.position).to.equal('atf');
expect(slot.floor).to.equal(0.01);
expect(slot.element_id).to.equal(bidderRequest.bids[0].adUnitCode);
expect(slot.name).to.equal(bidderRequest.bids[0].adUnitCode);
expect(slot.language).to.equal('en');
expect(slot.width).to.equal(640);
expect(slot.height).to.equal(320);
expect(slot.size_id).to.equal(201);

expect(slot).to.have.property('inventory').that.is.an('object');
expect(slot.inventory).to.have.property('rating').that.equals('5-star');
expect(slot.inventory).to.have.property('prodtype').that.equals('tech');

expect(slot).to.have.property('keywords')
.that.is.an('array')
.of.length(3)
.that.deep.equals(['a', 'b', 'c']);

expect(slot).to.have.property('visitor').that.is.an('object');
expect(slot.visitor).to.have.property('ucat').that.equals('new');
expect(slot.visitor).to.have.property('lastsearch').that.equals('iphone');
});

it('should make a well-formed video request', () => {
createVideoBidderRequest();

Expand Down Expand Up @@ -576,17 +712,60 @@ describe('the rubicon adapter', () => {
expect(floor).to.equal(3.25);
});

it('should not validate bid request when no video object is passed in', () => {
it('should not validate bid request when a invalid video object is passed in', () => {
createVideoBidderRequestNoVideo();
sandbox.stub(Date, 'now').callsFake(() =>
bidderRequest.auctionStart + 100
);

var floorBidderRequest = clone(bidderRequest);
const bidRequestCopy = clone(bidderRequest.bids[0]);
expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false);

let result = spec.isBidRequestValid(floorBidderRequest.bids[0]);
bidRequestCopy.params.video = {};
expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false);

expect(result).to.equal(false);
bidRequestCopy.params.video = undefined;
expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false);

bidRequestCopy.params.video = 123;
expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false);

bidRequestCopy.params.video = { size_id: '' };
expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false);

delete bidRequestCopy.params.video;
expect(spec.isBidRequestValid(bidRequestCopy)).to.equal(false);
});

it('should not validate bid request when an invalid video object is passed in with legacy config mediaType', () => {
createLegacyVideoBidderRequestNoVideo();
sandbox.stub(Date, 'now').callsFake(() =>
bidderRequest.auctionStart + 100
);

const bidderRequestCopy = clone(bidderRequest);
expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false);

bidderRequestCopy.bids[0].params.video = {};
expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false);

bidderRequestCopy.bids[0].params.video = undefined;
expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false);

bidderRequestCopy.bids[0].params.video = NaN;
expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false);

delete bidderRequestCopy.bids[0].params.video;
expect(spec.isBidRequestValid(bidderRequestCopy.bids[0])).to.equal(false);
});

it('should not validate bid request when video is outstream', () => {
createVideoBidderRequestOutstream();
sandbox.stub(Date, 'now').callsFake(() =>
bidderRequest.auctionStart + 100
);

expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false);
});

it('should get size from bid.sizes too', () => {
Expand All @@ -595,13 +774,55 @@ describe('the rubicon adapter', () => {
bidderRequest.auctionStart + 100
);

var floorBidderRequest = clone(bidderRequest);
const bidRequestCopy = clone(bidderRequest);

let [request] = spec.buildRequests(floorBidderRequest.bids, floorBidderRequest);
let post = request.data;
let [request] = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy);

expect(post.slots[0].width).to.equal(300);
expect(post.slots[0].height).to.equal(250);
expect(request.data.slots[0].width).to.equal(300);
expect(request.data.slots[0].height).to.equal(250);
});

it('should get size from bid.sizes too with legacy config mediaType', () => {
createLegacyVideoBidderRequestNoPlayer();
sandbox.stub(Date, 'now').callsFake(() =>
bidderRequest.auctionStart + 100
);

const bidRequestCopy = clone(bidderRequest);

let [request] = spec.buildRequests(bidRequestCopy.bids, bidRequestCopy);

expect(request.data.slots[0].width).to.equal(300);
expect(request.data.slots[0].height).to.equal(250);
});
});

describe('hasVideoMediaType', () => {
it('should return true if mediaType is true', () => {
createVideoBidderRequest();
const legacyVideoTypeBidRequest = spec.hasVideoMediaType(bidderRequest.bids[0]);
expect(legacyVideoTypeBidRequest).is.equal(true);
});

it('should return false if bidRequest.mediaType is not equal to video', () => {
expect(spec.hasVideoMediaType({
mediaType: 'banner'
})).is.equal(false);
});

it('should return false if bidRequest.mediaType is not defined', () => {
expect(spec.hasVideoMediaType({})).is.equal(false);
});

it('should return true if bidRequest.mediaTypes.video object exists', () => {
expect(spec.hasVideoMediaType({
mediaTypes: {
video: {
context: 'outstream',
playerSize: [300, 250]
}
}
})).is.equal(true);
});
});
});
Expand Down