From d911a5a9016bdf90aed13c49f2b475e9ea75f66e Mon Sep 17 00:00:00 2001 From: Serhii Mozhaiskyi Date: Fri, 12 Mar 2021 17:35:16 +0200 Subject: [PATCH 1/5] Magnite renderer support for Oustream ads --- modules/rubiconBidAdapter.js | 44 ++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 9905498edee..3b3c5c85675 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -3,9 +3,11 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import find from 'core-js-pure/features/array/find.js'; +import { Renderer } from '../src/Renderer.js'; const DEFAULT_INTEGRATION = 'pbjs_lite'; const DEFAULT_PBS_INTEGRATION = 'pbjs'; +const DEFAULT_RENDERER_URL = 'https://video-outstream.rubiconproject.com/apex-2.0.0.js'; let rubiConf = {}; // we are saving these as global to this module so that if a pub accidentally overwrites the entire @@ -109,6 +111,43 @@ var sizeMap = { }; utils._each(sizeMap, (item, key) => sizeMap[item] = key); +function renderBid(bid) { + const config = bid.renderer.getConfig(); + bid.renderer.push(() => { + window.MagniteApex.renderAd({ + width: bid.width, + height: bid.height, + vastUrl: bid.vastUrl, + placement: { + attachTo: `#${bid.adUnitCode}`, + align: config.align || 'center', + position: config.position || 'append' + }, + closeButton: config.closeButton || false, + label: config.label || undefined, + collapse: config.collapse || true + }); + }); +} + +function outstreamRenderer(rtbBid) { + const renderer = Renderer.install({ + id: rtbBid.adId, + url: rubiConf.rendererUrl || DEFAULT_RENDERER_URL, + config: rubiConf.rendererConfig || {}, + loaded: false, + adUnitCode: rtbBid.adUnitCode + }); + + try { + renderer.setRender(renderBid); + } catch (err) { + utils.logWarn('Prebid Error calling setRender on renderer', err); + } + + return renderer; +} + export const spec = { code: 'rubicon', gvlid: GVLID, @@ -628,6 +667,11 @@ export const spec = { if (bid.adm) { bidObject.vastXml = bid.adm; } if (bid.nurl) { bidObject.vastUrl = bid.nurl; } if (!bidObject.vastUrl && bid.nurl) { bidObject.vastUrl = bid.nurl; } + + const videoContext = utils.deepAccess(bidRequest, 'mediaTypes.video.context'); + if (videoContext.toLowerCase() === 'outstream') { + bidObject.renderer = outstreamRenderer(bidObject); + } } else { utils.logWarn('Rubicon: video response received non-video media type'); } From 52839aea95e3b4b8b764808c97dad7847858e62f Mon Sep 17 00:00:00 2001 From: Serhii Mozhaiskyi Date: Wed, 17 Mar 2021 14:41:10 +0200 Subject: [PATCH 2/5] add functions for hiding ad units --- modules/rubiconBidAdapter.js | 94 ++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 37 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 3b3c5c85675..059af3f0a4e 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -111,43 +111,6 @@ var sizeMap = { }; utils._each(sizeMap, (item, key) => sizeMap[item] = key); -function renderBid(bid) { - const config = bid.renderer.getConfig(); - bid.renderer.push(() => { - window.MagniteApex.renderAd({ - width: bid.width, - height: bid.height, - vastUrl: bid.vastUrl, - placement: { - attachTo: `#${bid.adUnitCode}`, - align: config.align || 'center', - position: config.position || 'append' - }, - closeButton: config.closeButton || false, - label: config.label || undefined, - collapse: config.collapse || true - }); - }); -} - -function outstreamRenderer(rtbBid) { - const renderer = Renderer.install({ - id: rtbBid.adId, - url: rubiConf.rendererUrl || DEFAULT_RENDERER_URL, - config: rubiConf.rendererConfig || {}, - loaded: false, - adUnitCode: rtbBid.adUnitCode - }); - - try { - renderer.setRender(renderBid); - } catch (err) { - utils.logWarn('Prebid Error calling setRender on renderer', err); - } - - return renderer; -} - export const spec = { code: 'rubicon', gvlid: GVLID, @@ -827,6 +790,63 @@ function _renderCreative(script, impId) { `; } +function hideGoogleAdsDiv(adUnit) { + const el = adUnit.querySelector("div[id^='google_ads']"); + if (el) { + el.style.setProperty('display', 'none'); + } +} + +function hideSmartAdServerIframe(adUnit) { + const el = adUnit.querySelector("script[id^='sas_script']"); + if (el && el.nextSibling && el.nextSibling.localName === 'iframe') { + el.nextSibling.style.setProperty('display', 'none'); + } +} + +function renderBid(bid) { + // hide existing ad units + const adUnitElement = document.getElementById(bid.adUnitCode); + hideGoogleAdsDiv(adUnitElement); + hideSmartAdServerIframe(adUnitElement); + + // configure renderer + const config = bid.renderer.getConfig(); + bid.renderer.push(() => { + window.MagniteApex.renderAd({ + width: bid.width, + height: bid.height, + vastUrl: bid.vastUrl, + placement: { + attachTo: `#${bid.adUnitCode}`, + align: config.align || 'center', + position: config.position || 'append' + }, + closeButton: config.closeButton || false, + label: config.label || undefined, + collapse: config.collapse || true + }); + }); +} + +function outstreamRenderer(rtbBid) { + const renderer = Renderer.install({ + id: rtbBid.adId, + url: rubiConf.rendererUrl || DEFAULT_RENDERER_URL, + config: rubiConf.rendererConfig || {}, + loaded: false, + adUnitCode: rtbBid.adUnitCode + }); + + try { + renderer.setRender(renderBid); + } catch (err) { + utils.logWarn('Prebid Error calling setRender on renderer', err); + } + + return renderer; +} + function parseSizes(bid, mediaType) { let params = bid.params; if (mediaType === 'video') { From 625535fedd5eaa781d674603deb93dccc46dfc76 Mon Sep 17 00:00:00 2001 From: Serhii Mozhaiskyi Date: Mon, 22 Mar 2021 17:38:35 +0200 Subject: [PATCH 3/5] Add unit tests --- test/spec/modules/rubiconBidAdapter_spec.js | 147 ++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 36890a2891b..7d9ec6b46ef 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -2852,6 +2852,153 @@ describe('the rubicon adapter', function () { }); }); + describe('for outstream video', function () { + const sandbox = sinon.createSandbox(); + beforeEach(function () { + createVideoBidderRequestOutstream(); + config.setConfig({rubicon: { + rendererConfig: { + align: 'left', + closeButton: true + }, + rendererUrl: 'https://example.test/renderer.js' + }}); + window.MagniteApex = { + renderAd: function() { + return null; + } + } + }); + + afterEach(function () { + sandbox.restore(); + delete window.MagniteApex; + }); + + it('should register a successful bid', function () { + let response = { + cur: 'USD', + seatbid: [{ + bid: [{ + id: '0', + impid: 'outstream_video1', + adomain: ['test.com'], + price: 2, + crid: '4259970', + ext: { + bidder: { + rp: { + mime: 'application/javascript', + size_id: 201, + advid: 12345 + } + }, + prebid: { + targeting: { + hb_uuid: '0c498f63-5111-4bed-98e2-9be7cb932a64' + }, + type: 'video' + } + } + }], + group: 0, + seat: 'rubicon' + }], + }; + + let bids = spec.interpretResponse({body: response}, { + bidRequest: bidderRequest.bids[0] + }); + + expect(bids).to.be.lengthOf(1); + + expect(bids[0].seatBidId).to.equal('0'); + expect(bids[0].creativeId).to.equal('4259970'); + expect(bids[0].cpm).to.equal(2); + expect(bids[0].ttl).to.equal(300); + expect(bids[0].netRevenue).to.equal(true); + expect(bids[0].adserverTargeting).to.deep.equal({hb_uuid: '0c498f63-5111-4bed-98e2-9be7cb932a64'}); + expect(bids[0].mediaType).to.equal('video'); + expect(bids[0].meta.mediaType).to.equal('video'); + expect(String(bids[0].meta.advertiserDomains)).to.equal('test.com'); + expect(bids[0].meta.advertiserId).to.equal(12345); + expect(bids[0].bidderCode).to.equal('rubicon'); + expect(bids[0].currency).to.equal('USD'); + expect(bids[0].width).to.equal(640); + expect(bids[0].height).to.equal(320); + // check custom renderer + expect(typeof bids[0].renderer).to.equal('object'); + expect(bids[0].renderer.getConfig()).to.deep.equal({ + align: 'left', + closeButton: true + }); + expect(bids[0].renderer.url).to.equal('https://example.test/renderer.js'); + }); + + it('should render ad with Magnite renderer', function () { + let response = { + cur: 'USD', + seatbid: [{ + bid: [{ + id: '0', + impid: 'outstream_video1', + adomain: ['test.com'], + price: 2, + crid: '4259970', + ext: { + bidder: { + rp: { + mime: 'application/javascript', + size_id: 201, + advid: 12345 + } + }, + prebid: { + targeting: { + hb_uuid: '0c498f63-5111-4bed-98e2-9be7cb932a64' + }, + type: 'video' + } + }, + nurl: 'https://test.com/vast.xml' + }], + group: 0, + seat: 'rubicon' + }], + }; + + sinon.spy(window.MagniteApex, 'renderAd'); + + let bids = spec.interpretResponse({body: response}, { + bidRequest: bidderRequest.bids[0] + }); + const bid = bids[0]; + bid.adUnitCode = 'outstream_video1_placement'; + const adUnit = document.createElement('div'); + adUnit.id = bid.adUnitCode; + document.body.appendChild(adUnit); + + bid.renderer.render(bid); + + const renderCall = window.MagniteApex.renderAd.getCall(0); + expect(renderCall.args[0]).to.deep.equal({ + closeButton: true, + collapse: true, + height: 320, + label: undefined, + placement: { + align: 'left', + attachTo: '#outstream_video1_placement', + position: 'append', + }, + vastUrl: 'https://test.com/vast.xml', + width: 640 + }); + // cleanup + adUnit.remove(); + }); + }); + describe('config with integration type', () => { it('should use the integration type provided in the config instead of the default', () => { config.setConfig({rubicon: {int_type: 'testType'}}); From 955170b2c1123c83f493b0f1e43ba3c5f984d6d5 Mon Sep 17 00:00:00 2001 From: bretg Date: Fri, 9 Apr 2021 11:26:54 -0400 Subject: [PATCH 4/5] adding open source location of renderer --- modules/rubiconBidAdapter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 059af3f0a4e..53b93025636 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -8,6 +8,7 @@ import { Renderer } from '../src/Renderer.js'; const DEFAULT_INTEGRATION = 'pbjs_lite'; const DEFAULT_PBS_INTEGRATION = 'pbjs'; const DEFAULT_RENDERER_URL = 'https://video-outstream.rubiconproject.com/apex-2.0.0.js'; +// renderer code at https://github.com/rubicon-project/apex2 let rubiConf = {}; // we are saving these as global to this module so that if a pub accidentally overwrites the entire From e94c3c26a5c463a33efe470bd4d23cadeea22a46 Mon Sep 17 00:00:00 2001 From: Serhii Mozhaiskyi Date: Thu, 15 Apr 2021 17:07:31 +0300 Subject: [PATCH 5/5] better minification --- modules/rubiconBidAdapter.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 059af3f0a4e..b0d74d2ae8a 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -799,8 +799,9 @@ function hideGoogleAdsDiv(adUnit) { function hideSmartAdServerIframe(adUnit) { const el = adUnit.querySelector("script[id^='sas_script']"); - if (el && el.nextSibling && el.nextSibling.localName === 'iframe') { - el.nextSibling.style.setProperty('display', 'none'); + const nextSibling = el && el.nextSibling; + if (nextSibling && nextSibling.localName === 'iframe') { + nextSibling.style.setProperty('display', 'none'); } }