From 25a495cec4847c824376619efa9ef556c229620c Mon Sep 17 00:00:00 2001 From: Adam Browning <19834421+adam-browning@users.noreply.github.com> Date: Thu, 3 Jun 2021 13:32:35 +0300 Subject: [PATCH] oneVideo Bid Adapter: adomain & mediaTypes.video Support (SAPR-16874) (#6925) --- modules/oneVideoBidAdapter.js | 113 +++++++++---- modules/oneVideoBidAdapter.md | 162 ++++++++++++------- test/spec/modules/oneVideoBidAdapter_spec.js | 106 +++++++++--- 3 files changed, 262 insertions(+), 119 deletions(-) diff --git a/modules/oneVideoBidAdapter.js b/modules/oneVideoBidAdapter.js index e57c9d12ff7..dfedbd156a9 100644 --- a/modules/oneVideoBidAdapter.js +++ b/modules/oneVideoBidAdapter.js @@ -4,7 +4,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'oneVideo'; export const spec = { code: 'oneVideo', - VERSION: '3.1.0', + VERSION: '3.1.1', ENDPOINT: 'https://ads.adaptv.advertising.com/rtb/openrtb?ext_id=', E2ETESTENDPOINT: 'https://ads-wc.v.ssp.yahoo.com/rtb/openrtb?ext_id=', SYNC_ENDPOINT1: 'https://pixel.advertising.com/ups/57304/sync?gdpr=&gdpr_consent=&_origin=0&redir=true', @@ -17,26 +17,52 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function(bid) { + // Bidder code validation if (bid.bidder !== BIDDER_CODE || typeof bid.params === 'undefined') { return false; } - // Video validations - if (typeof bid.params.video === 'undefined' || typeof bid.params.video.playerWidth === 'undefined' || typeof bid.params.video.playerHeight == 'undefined' || typeof bid.params.video.mimes == 'undefined') { - return false; + // E2E test skip validations + if (bid.params && bid.params.video && bid.params.video.e2etest) { + return true; } + // MediaTypes Video / Banner validation + if (typeof bid.mediaTypes.video === 'undefined' && typeof bid.mediaTypes.banner === 'undefined') { + utils.logError('Failed validation: adUnit mediaTypes.video OR mediaTypes.banner not declared'); + return false; + }; - // Prevend DAP Outstream validation, Banner DAP validation & Multi-Format adUnit support if (bid.mediaTypes.video) { - if (bid.mediaTypes.video.context === 'outstream' && bid.params.video.display === 1) { + // Player size validation + if (typeof bid.mediaTypes.video.playerSize === 'undefined') { + if (bid.params.video && (typeof bid.params.video.playerWidth === 'undefined' || typeof bid.params.video.playerHeight === 'undefined')) { + utils.logError('Failed validation: Player size not declared in either mediaTypes.playerSize OR bid.params.video.plauerWidth & bid.params.video.playerHeight.'); + return false; + }; + }; + // Mimes validation + if (typeof bid.mediaTypes.video.mimes === 'undefined') { + if (!bid.params.video || typeof bid.params.video.mimes === 'undefined') { + utils.logError('Failed validation: adUnit mediaTypes.mimes OR params.video.mimes not declared'); + return false; + }; + }; + // Prevend DAP Outstream validation, Banner DAP validation & Multi-Format adUnit support + if (bid.mediaTypes.video.context === 'outstream' && bid.params.video && bid.params.video.display === 1) { + utils.logError('Failed validation: Dynamic Ad Placement cannot be used with context Outstream (params.video.display=1)'); return false; - } - } else if (bid.mediaTypes.banner && !bid.params.video.display) { + }; + }; + + // DAP Validation + if (bid.mediaTypes.banner && bid.params.video && !bid.params.video.display) { + utils.logError('Failed validation: If you are trying to use Dynamic Ad Placement you must pass params.video.display=1'); return false; - } + }; - // Pub Id validation + // Publisher Id (Exchange) validation if (typeof bid.params.pubId === 'undefined') { + utils.logError('Failed validation: Adapter cannot send requests without bid.params.pubId'); return false; } @@ -49,7 +75,7 @@ export const spec = { * @param bidderRequest * @return ServerRequest Info describing the request to the server. */ - buildRequests: function(bids, bidRequest) { + buildRequests: function (bids, bidRequest) { let consentData = bidRequest ? bidRequest.gdprConsent : null; return bids.map(bid => { @@ -100,7 +126,10 @@ export const spec = { currency: response.cur, ttl: (bidRequest.params.video.ttl > 0 && bidRequest.params.video.ttl <= 3600) ? bidRequest.params.video.ttl : 300, netRevenue: true, - adUnitCode: bidRequest.adUnitCode + adUnitCode: bidRequest.adUnitCode, + meta: { + advertiserDomains: bid.adomain + } }; bidResponse.mediaType = (bidRequest.mediaTypes.banner) ? 'banner' : 'video' @@ -194,40 +223,54 @@ function getRequestData(bid, consentData, bidRequest) { if (bid.params.video.display == undefined || bid.params.video.display != 1) { bidData.imp[0].video = { - mimes: bid.params.video.mimes, - w: bid.params.video.playerWidth, - h: bid.params.video.playerHeight, - pos: bid.params.video.position, + linearity: 1 + }; + if (bid.params.video.playerWidth && bid.params.video.playerHeight) { + bidData.imp[0].video.w = bid.params.video.playerWidth; + bidData.imp[0].video.h = bid.params.video.playerHeight; + } else { + const playerSize = getSize(bid.mediaTypes.video.playerSize); + bidData.imp[0].video.w = playerSize.width; + bidData.imp[0].video.h = playerSize.height; + }; + if (bid.params.video.mimes) { + bidData.imp[0].video.mimes = bid.params.video.mimes; + } else { + bidData.imp[0].video.mimes = bid.mediaTypes.video.mimes; }; - if (bid.params.video.maxbitrate) { - bidData.imp[0].video.maxbitrate = bid.params.video.maxbitrate + if (bid.mediaTypes.video.maxbitrate || bid.params.video.maxbitrate) { + bidData.imp[0].video.maxbitrate = bid.params.video.maxbitrate || bid.mediaTypes.video.maxbitrate; } - if (bid.params.video.maxduration) { - bidData.imp[0].video.maxduration = bid.params.video.maxduration + if (bid.mediaTypes.video.maxduration || bid.params.video.maxduration) { + bidData.imp[0].video.maxduration = bid.params.video.maxduration || bid.mediaTypes.video.maxduration; } - if (bid.params.video.minduration) { - bidData.imp[0].video.minduration = bid.params.video.minduration + if (bid.mediaTypes.video.minduration || bid.params.video.minduration) { + bidData.imp[0].video.minduration = bid.params.video.minduration || bid.mediaTypes.video.minduration; } - if (bid.params.video.api) { - bidData.imp[0].video.api = bid.params.video.api + if (bid.mediaTypes.video.api || bid.params.video.api) { + bidData.imp[0].video.api = bid.params.video.api || bid.mediaTypes.video.api; } - if (bid.params.video.delivery) { - bidData.imp[0].video.delivery = bid.params.video.delivery + if (bid.mediaTypes.video.delivery || bid.params.video.delivery) { + bidData.imp[0].video.delivery = bid.params.video.delivery || bid.mediaTypes.video.delivery; } - if (bid.params.video.position) { - bidData.imp[0].video.pos = bid.params.video.position + if (bid.mediaTypes.video.position || bid.params.video.position) { + bidData.imp[0].video.pos = bid.params.video.position || bid.mediaTypes.video.position; } - if (bid.params.video.playbackmethod) { - bidData.imp[0].video.playbackmethod = bid.params.video.playbackmethod + if (bid.mediaTypes.video.playbackmethod || bid.params.video.playbackmethod) { + bidData.imp[0].video.playbackmethod = bid.params.video.playbackmethod || bid.mediaTypes.video.playbackmethod; } - if (bid.params.video.placement) { - bidData.imp[0].video.placement = bid.params.video.placement + if (bid.mediaTypes.video.placement || bid.params.video.placement) { + bidData.imp[0].video.placement = bid.params.video.placement || bid.mediaTypes.video.placement; } if (bid.params.video.rewarded) { bidData.imp[0].ext.rewarded = bid.params.video.rewarded } - bidData.imp[0].video.linearity = 1; - bidData.imp[0].video.protocols = bid.params.video.protocols || [2, 5]; + if (bid.mediaTypes.video.linearity || bid.params.video.linearity) { + bidData.imp[0].video.linearity = bid.params.video.linearity || bid.mediaTypes.video.linearity || 1; + } + if (bid.mediaTypes.video.protocols || bid.params.video.protocols) { + bidData.imp[0].video.protocols = bid.params.video.protocols || bid.mediaTypes.video.protocols || [2, 5]; + } } else if (bid.params.video.display == 1) { getFloorRequestObject.mediaType = 'banner'; bidData.imp[0].banner = { @@ -307,7 +350,7 @@ function getRequestData(bid, consentData, bidRequest) { } } if (bid.params.video.e2etest) { - utils.logMessage('+++ oneVideoBidAdapter: E2E test mode enabled. \n The following parameters are being overridden by e2etest mode:\n* bidfloor:null\n* width:300\n* height:250\n* mimes: video/mp4, application/javascript\n* api:2\n* site.page/ref: verizonmedia.com\n* tmax:1000'); + utils.logMessage('E2E test mode enabled: \n The following parameters are being overridden by e2etest mode:\n* bidfloor:null\n* width:300\n* height:250\n* mimes: video/mp4, application/javascript\n* api:2\n* site.page/ref: verizonmedia.com\n* tmax:1000'); bidData.imp[0].bidfloor = null; bidData.imp[0].video.w = 300; bidData.imp[0].video.h = 250; diff --git a/modules/oneVideoBidAdapter.md b/modules/oneVideoBidAdapter.md index a42433e80b5..c1762ac0cd3 100644 --- a/modules/oneVideoBidAdapter.md +++ b/modules/oneVideoBidAdapter.md @@ -3,12 +3,18 @@ **Module Name**: One Video Bidder Adapter **Module Type**: Bidder Adapter **Maintainer**: deepthi.neeladri.sravana@verizonmedia.com + adam.browning@verizonmedia.com # Description Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to fetch bids. - +# Prebid.js V5.0 Support +The oneVideo adapter now reads `mediaTypes.video` for mandatory parameters such as `playerSize` & `mimes`. +Note: You can use the `bid.params.video` object to specify explicit overrides for whatever is declared in `mediaTypes.video`. +Important: You must pass `bid.params.video = {}` as bare minimum for the adapter to work. # Integration Examples: -## Instream Video adUnit example & parameters + +## Instream Video adUnit using mediaTypes.video +*Note:* By default, the adapter will read the mandatory parameters from mediaTypes.video. *Note:* The Video SSP ad server will respond with an VAST XML to load into your defined player. ``` var adUnits = [ @@ -17,7 +23,18 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to mediaTypes: { video: { context: 'instream', - playerSize: [480, 640] + playerSize: [480, 640], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2,5], + api: [2], + position: 1, + delivery: [2], + minduration: 10, + maxduration: 30, + placement: 1, + playbackmethod: [1,5], + protocols: [2,5], + api: [2], } }, bids: [ @@ -25,21 +42,10 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to bidder: 'oneVideo', params: { video: { - playerWidth: 480, - playerHeight: 640, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2,5], - api: [2], - position: 1, - delivery: [2], - playbackmethod: [1,5], sid: YOUR_VSSP_ORG_ID, hp: 1, rewarded: 1, - placement: 1, inventoryid: 123, - minduration: 10, - maxduration: 30, ttl: 300, custom: { key1: "value1", @@ -59,16 +65,16 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to } ] ``` -## Outstream Video adUnit example & parameters -*Note:* The Video SSP ad server will load it's own Outstream Renderer (player) as a fallback if no player is defined on the publisher page. The Outstream player will inject into the div id that has an identical adUnit code. +## Instream Video adUnit using params.video overrides +*Note:* If the mandatory parameters are not specified in mediaTypes.video the adapter will read check to see if overrides are set in params.video. Decalring values using params.video will always override the settings in mediaTypes.video. +*Note:* The Video SSP ad server will respond with an VAST XML to load into your defined player. ``` var adUnits = [ { code: 'video1', mediaTypes: { video: { - context: 'outstream', - playerSize: [480, 640] + context: 'instream', } }, bids: [ @@ -76,21 +82,74 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to bidder: 'oneVideo', params: { video: { - playerWidth: 480, - playerHeight: 640, + playerWidth: 640, + playerHeight: 480, mimes: ['video/mp4', 'application/javascript'], protocols: [2,5], api: [2], position: 1, delivery: [2], + minduration: 10, + maxduration: 30, + placement: 1, playbackmethod: [1,5], + protocols: [2,5], + api: [2], sid: YOUR_VSSP_ORG_ID, hp: 1, rewarded: 1, - placement: 1, inventoryid: 123, + ttl: 300, + custom: { + key1: "value1", + key2: 123345 + } + }, + bidfloor: 0.5, + site: { + id: 1, + page: 'https://verizonmedia.com', + referrer: 'https://verizonmedia.com' + }, + pubId: 'HBExchange' + } + } + ] + } + ] +``` +## Outstream Video adUnit example & parameters +*Note:* The Video SSP ad server will load it's own Outstream Renderer (player) as a fallback if no player is defined on the publisher page. The Outstream player will inject into the div id that has an identical adUnit code. +``` + var adUnits = [ + { + code: 'video1', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [480, 640], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2,5], + api: [2], + position: 1, + delivery: [2], minduration: 10, maxduration: 30, + placement: 1, + playbackmethod: [1,5], + protocols: [2,5], + api: [2], + + } + }, + bids: [ + { + bidder: 'oneVideo', + params: { + video: { + sid: YOUR_VSSP_ORG_ID, + hp: 1, + rewarded: 1, ttl: 250 }, bidfloor: 0.5, @@ -116,7 +175,8 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to mediaTypes: { video: { context: "instream", - playerSize: [480, 640] + playerSize: [480, 640], + mimes: ['video/mp4', 'application/javascript'], } }, bids: [ @@ -124,11 +184,7 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to bidder: 'oneVideo', params: { video: { - playerWidth: 480, - playerHeight: 640, - mimes: ['video/mp4', 'application/javascript'], - position: 1, - display: 1 + ttl: 250 }, bidfloor: 0.5, site: { @@ -190,6 +246,7 @@ var adUnits = [ video: { context: "instream", playerSize: [480, 640] + mimes: ['video/mp4', 'application/javascript'], } }, bids: [ @@ -197,12 +254,8 @@ var adUnits = [ bidder: 'oneVideo', params: { video: { - playerWidth: 300, - playerHeight: 250, - mimes: ['video/mp4', 'application/javascript'], e2etest: true - }, - pubId: 'YOUR_PUBLISHER_ID' + } } } ] @@ -226,7 +279,10 @@ var adUnits = [ mediaTypes: { video: { context: 'instream', - playerSize: [480, 640] + playerSize: [480, 640], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2,5], + api: [2], } }, bids: [ @@ -234,12 +290,7 @@ var adUnits = [ bidder: 'oneVideo', params: { video: { - playerWidth: 480, - playerHeight: 640, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2,5], - api: [2], - sid: + sid: 123456 }, bidfloor: 0.5, site: { @@ -279,7 +330,10 @@ var adUnits = [ mediaTypes: { video: { context: 'instream', - playerSize: [480, 640] + playerSize: [480, 640], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2,5], + api: [2], } }, bids: [ @@ -287,18 +341,14 @@ var adUnits = [ bidder: 'oneVideo', params: { video: { - playerWidth: 480, - playerHeight: 640, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2,5], - api: [2], + ttl: 250 }, bidfloor: 0.5, site: { id: 1, page: 'https://verizonmedia.com', referrer: 'https://verizonmedia.com' - }, + }, pubId: 'HBExchange' } } @@ -315,18 +365,16 @@ const adUnits = [{ mediaTypes: { video: { context: 'outstream', - playerSize: [640, 480] + playerSize: [640, 480], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2, 5], + api: [1, 2], } }, bids: [{ bidder: 'oneVideo', params: { video: { - playerWidth: 640, - playerHeight: 480, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2, 5], - api: [1, 2], ttl: 300, content: { id: "1234", @@ -373,18 +421,16 @@ const adUnits = [{ mediaTypes: { video: { context: 'outstream', - playerSize: [640, 480] + playerSize: [640, 480], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2, 5], + api: [1, 2], } }, bids: [{ bidder: 'oneVideo', params: { video: { - playerWidth: 640, - playerHeight: 480, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2, 5], - api: [1, 2], ttl: 300 }, bidfloor: 0.5, diff --git a/test/spec/modules/oneVideoBidAdapter_spec.js b/test/spec/modules/oneVideoBidAdapter_spec.js index 3f5304dce0a..5289203fd5b 100644 --- a/test/spec/modules/oneVideoBidAdapter_spec.js +++ b/test/spec/modules/oneVideoBidAdapter_spec.js @@ -59,11 +59,77 @@ describe('OneVideoBidAdapter', function () { }); describe('spec.isBidRequestValid', function () { - it('should return true when the required params are passed', function () { + it('should return false when mediaTypes video OR banner not declared', function () { + bidRequest.mediaTypes = {}; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return true (skip validations) when e2etest = true', function () { + bidRequest.params.video = { + e2etest: true + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when mediaTypes.video has all mandatory params', function () { + bidRequest.mediaTypes.video = { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4', 'application/javascript'], + } + bidRequest.params.video = {}; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when params.video has all override params instead of mediaTypes.video', function () { + bidRequest.mediaTypes.video = { + context: 'instream' + }; + bidRequest.params.video = { + playerWidth: 640, + playerHeight: 480, + mimes: ['video/mp4', 'application/javascript'] + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when playerWidth & playerHeight are passed in params.video', function () { + bidRequest.mediaTypes.video = { + context: 'instream', + mimes: ['video/mp4', 'application/javascript'] + }; + bidRequest.params.video = { + playerWidth: 640, + playerHeight: 480, + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when mimes is passed in params.video', function () { + bidRequest.mediaTypes.video = { + context: 'instream', + playerSizes: [640, 480] + }; + bidRequest.video = { + mimes: ['video/mp4', 'application/javascript'] + }; expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }); - it('should return false when the "video" param is missing', function () { + it('should return false when both mediaTypes.video and params.video Objects are missing', function () { + bidRequest.mediaTypes = {}; + bidRequest.params = { + pubId: 'brxd' + }; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return false when both mediaTypes.video and params.video are missing mimes and player size', function () { + bidRequest.mediaTypes = { + video: { + context: 'instream' + } + }; bidRequest.params = { pubId: 'brxd' }; @@ -76,34 +142,16 @@ describe('OneVideoBidAdapter', function () { playerWidth: 480, playerHeight: 640, mimes: ['video/mp4', 'application/javascript'], - protocols: [2, 5], - api: [2], - position: 1, - delivery: [2], - playbackmethod: [1, 5], - sid: 134, - rewarded: 1, - placement: 1, - inventoryid: 123 } }; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); + it('should return true when the "pubId" param exists', function () { - bidRequest.params = { + bidRequest.mediaTypes = { video: { - playerWidth: 480, - playerHeight: 640, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2, 5], - api: [2], - position: 1, - delivery: [2], - playbackmethod: [1, 5], - sid: 134, - rewarded: 1, - placement: 1, - inventoryid: 123 + playerSizes: [640, 480], + mimes: ['video/mp4', 'application/javascript'] }, pubId: 'brxd' }; @@ -216,7 +264,7 @@ describe('OneVideoBidAdapter', function () { const placement = bidRequest.params.video.placement; const rewarded = bidRequest.params.video.rewarded; const inventoryid = bidRequest.params.video.inventoryid; - const VERSION = '3.1.0'; + const VERSION = '3.1.1'; expect(data.imp[0].video.w).to.equal(width); expect(data.imp[0].video.h).to.equal(height); expect(data.imp[0].bidfloor).to.equal(bidRequest.params.bidfloor); @@ -640,7 +688,10 @@ describe('OneVideoBidAdapter', function () { adid: 123, crid: 2, price: 6.01, - adm: '' + adm: '', + adomain: [ + 'verizonmedia.com' + ], }] }], cur: 'USD' @@ -664,6 +715,9 @@ describe('OneVideoBidAdapter', function () { netRevenue: true, adUnitCode: bidRequest.adUnitCode, renderer: (bidRequest.mediaTypes.video.context === 'outstream') ? newRenderer(bidRequest, bidResponse) : undefined, + meta: { + advertiserDomains: ['verizonmedia.com'] + } }; expect(bidResponse).to.deep.equal(o); });