From 2dd30b1427d1a2e231220152ba0755cb6c5baeb5 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Tue, 19 Apr 2022 09:40:29 -0400 Subject: [PATCH 01/48] jwplayerBidAdapter module created --- modules/jwplayerBidAdapter.js | 0 modules/jwplayerBidAdapter.md | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 modules/jwplayerBidAdapter.js create mode 100644 modules/jwplayerBidAdapter.md diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/modules/jwplayerBidAdapter.md b/modules/jwplayerBidAdapter.md new file mode 100644 index 00000000000..e69de29bb2d From 18df88899f57d5e56af8486e26605b93f32571d6 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Tue, 19 Apr 2022 09:59:36 -0400 Subject: [PATCH 02/48] created initial skeleton of jwplayerBidAdapter --- modules/jwplayerBidAdapter.js | 48 +++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index e69de29bb2d..102d760ff65 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -0,0 +1,48 @@ +import * as utils from 'src/utils'; +import { registerBidder } from 'src/adapters/bidderFactory'; +import { config } from 'src/config'; +import { BANNER, VIDEO, NATIVE } from 'src/mediaTypes'; +const BIDDER_CODE = 'jwplayer'; +const GVLID = 1046; + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + /** + * Determines whether or not the given bid request is valid. + * + * @param {object} bid The bid to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function(bid) {}, + + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be sent to the Server. + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests, bidderRequest) {}, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse, request) {}, + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) {}, + onTimeout: function(timeoutData) {}, + + /** + * Add element selector to javascript tracker to improve native viewability + * @param {Bid} bid + */ + onBidWon: function(bid) {}, + onSetTargeting: function(bid) {}, + onBidderError: function({ error, bidderRequest }) {} +}; + +registerBidder(spec); \ No newline at end of file From 6a48b9c8ae40d5fb60e15da664c977181d1d51d6 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Tue, 19 Apr 2022 10:57:16 -0400 Subject: [PATCH 03/48] cleaned up initial skeleton for jwplayerBidAdapter --- modules/jwplayerBidAdapter.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 102d760ff65..09aab66578c 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -1,14 +1,15 @@ import * as utils from 'src/utils'; import { registerBidder } from 'src/adapters/bidderFactory'; import { config } from 'src/config'; -import { BANNER, VIDEO, NATIVE } from 'src/mediaTypes'; +import { VIDEO } from 'src/mediaTypes'; const BIDDER_CODE = 'jwplayer'; const GVLID = 1046; +const SUPPORTED_AD_TYPES = [VIDEO]; export const spec = { code: BIDDER_CODE, gvlid: GVLID, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], + supportedMediaTypes: SUPPORTED_AD_TYPES, /** * Determines whether or not the given bid request is valid. @@ -34,15 +35,13 @@ export const spec = { */ interpretResponse: function(serverResponse, request) {}, getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) {}, - onTimeout: function(timeoutData) {}, - /** - * Add element selector to javascript tracker to improve native viewability - * @param {Bid} bid - */ + + // Optional? + onTimeout: function(timeoutData) {}, onBidWon: function(bid) {}, onSetTargeting: function(bid) {}, onBidderError: function({ error, bidderRequest }) {} }; -registerBidder(spec); \ No newline at end of file +registerBidder(spec); From 49785313bc3c22a2ee7d61484318bf313771206a Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Tue, 19 Apr 2022 10:57:33 -0400 Subject: [PATCH 04/48] added description to jwplayerBidAdapter.md --- modules/jwplayerBidAdapter.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/modules/jwplayerBidAdapter.md b/modules/jwplayerBidAdapter.md index e69de29bb2d..e9be2c70885 100644 --- a/modules/jwplayerBidAdapter.md +++ b/modules/jwplayerBidAdapter.md @@ -0,0 +1,17 @@ +# Overview + +``` +Module Name: JWPlayer Bid Adapter +Module Type: Bidder Adapter +Maintainer: jrocha@jwplayer.com +``` + +# Description + +Connects to JWPlayer's demand sources. + +JWPlayer bid adapter supports Video (instream and outstream). + +# Test Parameters +``` +``` From 8f539293dd6c965647157ae9d3fbbbf04ebf0002 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Tue, 19 Apr 2022 11:14:01 -0400 Subject: [PATCH 05/48] created jwplayerBidAdapter unit test suite --- test/spec/modules/jwplayerBidAdapter_spec.js | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 test/spec/modules/jwplayerBidAdapter_spec.js diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js new file mode 100644 index 00000000000..4573fd1f116 --- /dev/null +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -0,0 +1,25 @@ +import { expect, assert } from 'chai'; +import { spec } from 'modules/jwplayerBidAdapter.js'; +import { config } from 'src/config.js'; + +describe('jwplayer adapter tests', function() { + var sandbox, clock, frozenNow = new Date(); + + beforeEach(function() { + sandbox = sinon.sandbox.create(); + clock = sinon.useFakeTimers(frozenNow.getTime()); + }); + + afterEach(function() { + sandbox.restore(); + clock.restore(); + }); + + describe('isBidRequestValid', function() {}); + + describe('buildRequests for video', function() {}); + + describe('interpretResponse for video', function() {}); + + describe('user sync handler', function() {}); +}); From 9d410f8cb50759f4408ff1a5acafa34047075bbd Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Wed, 20 Apr 2022 01:08:32 -0400 Subject: [PATCH 06/48] implemented isBidRequestValid with unit tests --- modules/jwplayerBidAdapter.js | 40 +++++++++++-------- test/spec/modules/jwplayerBidAdapter_spec.js | 42 +++++++++++++++----- 2 files changed, 54 insertions(+), 28 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 09aab66578c..37fa480b560 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -1,15 +1,16 @@ -import * as utils from 'src/utils'; -import { registerBidder } from 'src/adapters/bidderFactory'; -import { config } from 'src/config'; -import { VIDEO } from 'src/mediaTypes'; +// import * as utils from 'src/utils'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +// import { config } from 'src/config'; +import { VIDEO } from '../src/mediaTypes.js'; + const BIDDER_CODE = 'jwplayer'; const GVLID = 1046; const SUPPORTED_AD_TYPES = [VIDEO]; export const spec = { - code: BIDDER_CODE, - gvlid: GVLID, - supportedMediaTypes: SUPPORTED_AD_TYPES, + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: SUPPORTED_AD_TYPES, /** * Determines whether or not the given bid request is valid. @@ -17,7 +18,13 @@ export const spec = { * @param {object} bid The bid to validate. * @return boolean True if this is a valid bid, and false otherwise. */ - isBidRequestValid: function(bid) {}, + isBidRequestValid: function(bid) { + if (!bid || !bid.params) { + return false; + } + + return !!bid.params.placementId && !!bid.params.pubId; + }, /** * Make a server request from the list of BidRequests. @@ -25,7 +32,7 @@ export const spec = { * @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be sent to the Server. * @return ServerRequest Info describing the request to the server. */ - buildRequests: function(validBidRequests, bidderRequest) {}, + buildRequests: function(validBidRequests, bidderRequest) {}, /** * Unpack the response from the server into a list of bids. @@ -33,15 +40,14 @@ export const spec = { * @param {*} serverResponse A successful response from the server. * @return {Bid[]} An array of bids which were nested inside the server. */ - interpretResponse: function(serverResponse, request) {}, - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) {}, - + interpretResponse: function(serverResponse, request) {}, + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) {}, - // Optional? - onTimeout: function(timeoutData) {}, - onBidWon: function(bid) {}, - onSetTargeting: function(bid) {}, - onBidderError: function({ error, bidderRequest }) {} + // Optional? + // onTimeout: function(timeoutData) {}, + // onBidWon: function(bid) {}, + // onSetTargeting: function(bid) {}, + // onBidderError: function({ error, bidderRequest }) {} }; registerBidder(spec); diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js index 4573fd1f116..5abf6b7e8c7 100644 --- a/test/spec/modules/jwplayerBidAdapter_spec.js +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -3,23 +3,43 @@ import { spec } from 'modules/jwplayerBidAdapter.js'; import { config } from 'src/config.js'; describe('jwplayer adapter tests', function() { - var sandbox, clock, frozenNow = new Date(); + var sandbox, clock, frozenNow = new Date(); - beforeEach(function() { - sandbox = sinon.sandbox.create(); - clock = sinon.useFakeTimers(frozenNow.getTime()); + beforeEach(function() { + sandbox = sinon.sandbox.create(); + clock = sinon.useFakeTimers(frozenNow.getTime()); + }); + + afterEach(function() { + sandbox.restore(); + clock.restore(); + }); + + describe('isBidRequestValid', function() { + it('passes when the bid includes a placement ID and a publisher ID', function() { + assert(spec.isBidRequestValid({params: {placementId: 'foo', pubId: 'bar'}}) === true); + }); + + it('fails when the bid does not include a placement ID', function() { + assert(spec.isBidRequestValid({params: {pubId: 'foo'}}) === false); }); - afterEach(function() { - sandbox.restore(); - clock.restore(); + it('fails when the bid does not include a publisher ID', function() { + assert(spec.isBidRequestValid({params: {placementId: 'foo'}}) === false); }); - describe('isBidRequestValid', function() {}); + it('fails when bid is falsey', function() { + assert(spec.isBidRequestValid() === false); + }); + + it('fails when the bid has no params at all', function() { + assert(spec.isBidRequestValid({}) === false); + }); + }); - describe('buildRequests for video', function() {}); + describe('buildRequests for video', function() {}); - describe('interpretResponse for video', function() {}); + describe('interpretResponse for video', function() {}); - describe('user sync handler', function() {}); + describe('user sync handler', function() {}); }); From 3480a193c31732c27ef28829cbcf672131cc4078 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Mon, 9 May 2022 16:41:37 -0400 Subject: [PATCH 07/48] initial creation of the buildRequests function --- modules/jwplayerBidAdapter.js | 92 ++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 37fa480b560..ca5a1197ed3 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -2,8 +2,11 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; // import { config } from 'src/config'; import { VIDEO } from '../src/mediaTypes.js'; +import { deepSetValue, isFn } from '../src/utils.js'; const BIDDER_CODE = 'jwplayer'; +const URL = 'https://ib.adnxs.com/openrtb2/prebid'; + const GVLID = 1046; const SUPPORTED_AD_TYPES = [VIDEO]; @@ -30,9 +33,24 @@ export const spec = { * Make a server request from the list of BidRequests. * * @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be sent to the Server. + * @param bidderRequest * @return ServerRequest Info describing the request to the server. */ - buildRequests: function(validBidRequests, bidderRequest) {}, + buildRequests: function(bidRequests, bidderRequest) { + if (!bidRequests) { + return; + } + + return bidRequests.map(bidRequest => { + const payload = buildRequest(bidRequest, bidderRequests); + + return { + method: 'POST', + url: URL, + data: payload + } + }); + }, /** * Unpack the response from the server into a list of bids. @@ -50,4 +68,76 @@ export const spec = { // onBidderError: function({ error, bidderRequest }) {} }; +function buildRequest(bidRequest, bidderRequest) { + const {params} = bidRequest; + + const videoAdUnit = deepAccess(bidRequest, 'mediaTypes.video', {}); + const videoBidderParams = deepAccess(bidRequest, 'params.video', {}); + + const videoParams = { + ...videoAdUnit, + ...videoBidderParams + } + + const video = { + w: parseInt(videoParams.playerSize[0][0], 10), + h: parseInt(videoParams.playerSize[0][1], 10) + } + + // Bid FLoor + const bidFloorRequest = { + currency: bidRequest.params.cur || 'USD', + mediaType: 'video', + size: '*' + }; + + let floorData = bidRquest.params; + if (isFn(bidRequest.getFloor)) { + floorData = bidRequest.getFloor(bidFloorRequest); + } else { + if (params.bidfloor) { + floorData = {floor: params.bidfloor, currency: params.currency || 'USD'}; + } + } + + // Open RTB Request Object + const openrtbRequest = { + id: bidRequest.bidId, + imp: [ + { + id: '1', + video: video, + secure: isSecure() ? 1 : 0, + bidfloor: floorData.floor, + bidfloorcur: floorData.currency + } + ], + site: { + domain: window.location.hostname, + page: window.location.href, + ref: bidRequest.refererInfo ? bidRequest.refererInfo.referer || null : null + }, + ext: { + hb: 1, + prebidver: '$prebid.version$', + adapterver: spec.VERSION + } + } + + // content + + // Attaching GDPR Consent Params + if (bidderRequest.gdprConsent) { + deepSetValue(openrtbRequest, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + deepSetValue(openrtbRequest, 'regs.ext.gdpr', (bidderRequest.gdprConsent.gdprApplies ? 1 : 0)); + } + + // CCPA + if (bidderRequest.uspConsent) { + deepSetValue(openrtbRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); + } + + return JSON.stringify(openrtbRequest); +} + registerBidder(spec); From 30909e6124112b0d763b284fe63b87e138d72bb3 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Thu, 12 May 2022 21:16:20 -0400 Subject: [PATCH 08/48] incorporated feedback from pr and refactored. --- modules/jwplayerBidAdapter.js | 187 ++++++++++++++++++++++++---------- 1 file changed, 136 insertions(+), 51 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index ca5a1197ed3..08f73ebab9d 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -10,6 +10,23 @@ const URL = 'https://ib.adnxs.com/openrtb2/prebid'; const GVLID = 1046; const SUPPORTED_AD_TYPES = [VIDEO]; +const VIDEO_ORTB_PARAMS = [ + 'mimes', + 'minduration', + 'maxduration', + 'placement', + 'protocols', + 'startdelay', + 'skip', + 'skipafter', + 'minbitrate', + 'maxbitrate', + 'delivery', + 'playbackmethod', + 'api', + 'linearity' +]; + export const spec = { code: BIDDER_CODE, gvlid: GVLID, @@ -32,7 +49,7 @@ export const spec = { /** * Make a server request from the list of BidRequests. * - * @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be sent to the Server. + * @param {BidRequest[]} bidRequests A non-empty list of bid requests, or ad units, which should be sent to the server. * @param bidderRequest * @return ServerRequest Info describing the request to the server. */ @@ -42,7 +59,7 @@ export const spec = { } return bidRequests.map(bidRequest => { - const payload = buildRequest(bidRequest, bidderRequests); + const payload = buildRequest(bidRequest, bidderRequest); return { method: 'POST', @@ -69,75 +86,143 @@ export const spec = { }; function buildRequest(bidRequest, bidderRequest) { - const {params} = bidRequest; + // bidRequest.mediaTypes.video + // bidRequest.params (bid parameters) - const videoAdUnit = deepAccess(bidRequest, 'mediaTypes.video', {}); - const videoBidderParams = deepAccess(bidRequest, 'params.video', {}); + // bidderRequest.gdprConsent + // bidderRequest.uspConsent + + // Open RTB Request Object + const openrtbRequest = { + id: params.bidId, + imp: buildRequestImpression(bidRequest, bidderRequest), + site: buildRequestSite(), + device: buildRequestDevice() + }; - const videoParams = { - ...videoAdUnit, - ...videoBidderParams + // Attaching GDPR Consent Params + if (bidderRequest.gdprConsent) { + deepSetValue(openrtbRequest, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + deepSetValue(openrtbRequest, 'regs.ext.gdpr', (bidderRequest.gdprConsent.gdprApplies ? 1 : 0)); + } + + // CCPA + if (bidderRequest.uspConsent) { + deepSetValue(openrtbRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); } + return JSON.stringify(openrtbRequest);; +} + +function buildRequestImpression(bidRequest, bidderRequest) { + const impressions = []; + + const impressionObject = { + id: bidRequest.adUnitCode, + secure: isSecure() ? 1 : 0 + }; + + impressionObject.video = buildImpressionVideo(bidRequest); + + const bidFloorData = buildBidFloorData(bidRequest); + impressionObject.bidfloor = bidFloorData.floor; + impressionObject.bidfloorcur = bidFloorData.currency; + + impressionObject.ext = buildImpressionExtension(); // TODO: Complete + + impressions.push(impressionObject); + + return impressions; +} + +function buildImpressionVideo(bidRequest) { + const videoAdUnit = deepAccess(bidRequest, 'mediaTypes.video', {}); + + const playerSize = videoAdUnit.playerSize; + + const contentWidth = playerSize[0][0]; + const contentHeight = playerSize[0][1]; + const video = { - w: parseInt(videoParams.playerSize[0][0], 10), - h: parseInt(videoParams.playerSize[0][1], 10) + w: parseInt(contentWidth, 10), + h: parseInt(contentHeight, 10) + } + + // Obtain all ORTB params related video from Ad Unit + VIDEO_ORTB_PARAMS.forEach((param) => { + if (videoAdUnit.hasOwnProperty(param)) { + video[param] = videoAdUnit[param]; + } + }); + + // Placement Inference Rules: + // - If no placement is defined then default to 1 (In Stream) + video.placement = video.placement || 2; + + // - If product is instream (for instream context) then override placement to 1 + if (params.context === 'instream') { + video.startdelay = video.startdelay || 0; + video.placement = 1; } +} - // Bid FLoor +function buildBidFloorData(bidRequest) { + const {params} = bidRequest; + // Bid Floor const bidFloorRequest = { - currency: bidRequest.params.cur || 'USD', + currency: params.currency || 'USD', mediaType: 'video', size: '*' }; - let floorData = bidRquest.params; + let floorData; if (isFn(bidRequest.getFloor)) { floorData = bidRequest.getFloor(bidFloorRequest); - } else { - if (params.bidfloor) { - floorData = {floor: params.bidfloor, currency: params.currency || 'USD'}; - } - } - - // Open RTB Request Object - const openrtbRequest = { - id: bidRequest.bidId, - imp: [ - { - id: '1', - video: video, - secure: isSecure() ? 1 : 0, - bidfloor: floorData.floor, - bidfloorcur: floorData.currency - } - ], - site: { - domain: window.location.hostname, - page: window.location.href, - ref: bidRequest.refererInfo ? bidRequest.refererInfo.referer || null : null - }, - ext: { - hb: 1, - prebidver: '$prebid.version$', - adapterver: spec.VERSION - } + } else if (params.bidfloor) { + floorData = {floor: params.bidfloor, currency: params.currency || 'USD'}; } - // content + return floorData; +} - // Attaching GDPR Consent Params - if (bidderRequest.gdprConsent) { - deepSetValue(openrtbRequest, 'user.ext.consent', bidderRequest.gdprConsent.consentString); - deepSetValue(openrtbRequest, 'regs.ext.gdpr', (bidderRequest.gdprConsent.gdprApplies ? 1 : 0)); - } +function buildRequestSite(bidRequest) { + const site = { + domain: window.location.hostname, + page: window.location.href, + ref: bidRequest.refererInfo ? bidRequest.refererInfo.referer || null : null + }; - // CCPA - if (bidderRequest.uspConsent) { - deepSetValue(openrtbRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); + // Site Content + /* + if (videoAdUnit.content && isPlainObject(videoAdUnit.content)) { + openrtbRequest.site.content = {}; + const contentStringKeys = ['id', 'title', 'series', 'season', 'genre', 'contentrating', 'language', 'url']; + const contentNumberkeys = ['episode', 'prodq', 'context', 'livestream', 'len']; + const contentArrayKeys = ['cat']; + const contentObjectKeys = ['ext']; + for (const contentKey in videoBidderParams.content) { + if ( + (contentStringKeys.indexOf(contentKey) > -1 && isStr(videoAdUnit.content[contentKey])) || + (contentNumberkeys.indexOf(contentKey) > -1 && isNumber(videoAdUnit.content[contentKey])) || + (contentObjectKeys.indexOf(contentKey) > -1 && isPlainObject(videoAdUnit.content[contentKey])) || + (contentArrayKeys.indexOf(contentKey) > -1 && isArray(videoAdUnit.content[contentKey]) && + videoAdUnit.content[contentKey].every(catStr => isStr(catStr)))) { + site.content[contentKey] = videoAdUnit.content[contentKey]; + } else { + logMessage('JWPlayer bid adapter validation error: ', contentKey, ' is either not supported is OpenRTB V2.5 or value is undefined'); + } + } } + */ - return JSON.stringify(openrtbRequest); + return site; +} + +function buildRequestDevice() { + return { + ua: navigator.userAgent, + ip: '' + }; } registerBidder(spec); From e44c89be99321cad73da000e88a7380412859115 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Thu, 19 May 2022 10:16:44 -0400 Subject: [PATCH 09/48] added video object, impExt object, and site content object to jwplayerBidAdapter --- modules/jwplayerBidAdapter.js | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 08f73ebab9d..574c329f449 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -128,7 +128,7 @@ function buildRequestImpression(bidRequest, bidderRequest) { impressionObject.bidfloor = bidFloorData.floor; impressionObject.bidfloorcur = bidFloorData.currency; - impressionObject.ext = buildImpressionExtension(); // TODO: Complete + impressionObject.ext = buildImpressionExtension(bidRequest); impressions.push(impressionObject); @@ -164,6 +164,16 @@ function buildImpressionVideo(bidRequest) { video.startdelay = video.startdelay || 0; video.placement = 1; } + + return video; +} + +function buildImpressionExtension(bidRequest) { + return { + appnexus: { + placement_id: bidRequest.params.placementId + } + }; } function buildBidFloorData(bidRequest) { @@ -192,9 +202,10 @@ function buildRequestSite(bidRequest) { ref: bidRequest.refererInfo ? bidRequest.refererInfo.referer || null : null }; + const videoParams = deepAccess(bidRequest, 'mediaTypes.video', {}); + // Site Content - /* - if (videoAdUnit.content && isPlainObject(videoAdUnit.content)) { + if (videoParams.content && isPlainObject(videoParams.content)) { openrtbRequest.site.content = {}; const contentStringKeys = ['id', 'title', 'series', 'season', 'genre', 'contentrating', 'language', 'url']; const contentNumberkeys = ['episode', 'prodq', 'context', 'livestream', 'len']; @@ -202,19 +213,17 @@ function buildRequestSite(bidRequest) { const contentObjectKeys = ['ext']; for (const contentKey in videoBidderParams.content) { if ( - (contentStringKeys.indexOf(contentKey) > -1 && isStr(videoAdUnit.content[contentKey])) || - (contentNumberkeys.indexOf(contentKey) > -1 && isNumber(videoAdUnit.content[contentKey])) || - (contentObjectKeys.indexOf(contentKey) > -1 && isPlainObject(videoAdUnit.content[contentKey])) || - (contentArrayKeys.indexOf(contentKey) > -1 && isArray(videoAdUnit.content[contentKey]) && - videoAdUnit.content[contentKey].every(catStr => isStr(catStr)))) { - site.content[contentKey] = videoAdUnit.content[contentKey]; + (contentStringKeys.indexOf(contentKey) > -1 && isStr(videoParams.content[contentKey])) || + (contentNumberkeys.indexOf(contentKey) > -1 && isNumber(videoParams.content[contentKey])) || + (contentObjectKeys.indexOf(contentKey) > -1 && isPlainObject(videoParams.content[contentKey])) || + (contentArrayKeys.indexOf(contentKey) > -1 && isArray(videoParams.content[contentKey]) && + videoParams.content[contentKey].every(catStr => isStr(catStr)))) { + site.content[contentKey] = videoParams.content[contentKey]; } else { logMessage('JWPlayer bid adapter validation error: ', contentKey, ' is either not supported is OpenRTB V2.5 or value is undefined'); } } } - */ - return site; } From 1ffdf1bea9dfa09949281b3060045a0e5dd7bb22 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Thu, 19 May 2022 10:21:39 -0400 Subject: [PATCH 10/48] added feedback from pr --- modules/jwplayerBidAdapter.js | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 574c329f449..afefc5fa4f6 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -10,13 +10,15 @@ const URL = 'https://ib.adnxs.com/openrtb2/prebid'; const GVLID = 1046; const SUPPORTED_AD_TYPES = [VIDEO]; +// Video Parameters +// https://docs.prebid.org/dev-docs/bidder-adaptor.html#step-2-accept-video-parameters-and-pass-them-to-your-server const VIDEO_ORTB_PARAMS = [ 'mimes', 'minduration', 'maxduration', - 'placement', 'protocols', 'startdelay', + 'placement', 'skip', 'skipafter', 'minbitrate', @@ -136,35 +138,17 @@ function buildRequestImpression(bidRequest, bidderRequest) { } function buildImpressionVideo(bidRequest) { - const videoAdUnit = deepAccess(bidRequest, 'mediaTypes.video', {}); - - const playerSize = videoAdUnit.playerSize; + const videoParams = deepAccess(bidRequest, 'mediaTypes.video', {}); - const contentWidth = playerSize[0][0]; - const contentHeight = playerSize[0][1]; - - const video = { - w: parseInt(contentWidth, 10), - h: parseInt(contentHeight, 10) - } + const video = {}; // Obtain all ORTB params related video from Ad Unit VIDEO_ORTB_PARAMS.forEach((param) => { - if (videoAdUnit.hasOwnProperty(param)) { - video[param] = videoAdUnit[param]; + if (videoParams.hasOwnProperty(param)) { + video[param] = videoParams[param]; } }); - // Placement Inference Rules: - // - If no placement is defined then default to 1 (In Stream) - video.placement = video.placement || 2; - - // - If product is instream (for instream context) then override placement to 1 - if (params.context === 'instream') { - video.startdelay = video.startdelay || 0; - video.placement = 1; - } - return video; } From 1806a93b970f92c9fde7eb76744a9687a6ca0465 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Wed, 25 May 2022 15:32:17 -0400 Subject: [PATCH 11/48] fixed parameters for methods in jwplayerBidAdapter --- modules/jwplayerBidAdapter.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index afefc5fa4f6..41f6535bec8 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -88,17 +88,11 @@ export const spec = { }; function buildRequest(bidRequest, bidderRequest) { - // bidRequest.mediaTypes.video - // bidRequest.params (bid parameters) - - // bidderRequest.gdprConsent - // bidderRequest.uspConsent - // Open RTB Request Object const openrtbRequest = { - id: params.bidId, + id: bidRequest.params.bidId, imp: buildRequestImpression(bidRequest, bidderRequest), - site: buildRequestSite(), + site: buildRequestSite(bidRequest), device: buildRequestDevice() }; @@ -116,7 +110,7 @@ function buildRequest(bidRequest, bidderRequest) { return JSON.stringify(openrtbRequest);; } -function buildRequestImpression(bidRequest, bidderRequest) { +function buildRequestImpression(bidRequest) { const impressions = []; const impressionObject = { From 2ccf74076538be4252687c9ef447debd4aa00462 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Thu, 2 Jun 2022 18:20:48 -0400 Subject: [PATCH 12/48] added feedback from pr and unit tests --- modules/jwplayerBidAdapter.js | 61 +++++++++---------- test/spec/modules/jwplayerBidAdapter_spec.js | 64 +++++++++++++++++++- 2 files changed, 91 insertions(+), 34 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 41f6535bec8..17b2e83ff34 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -2,7 +2,14 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; // import { config } from 'src/config'; import { VIDEO } from '../src/mediaTypes.js'; -import { deepSetValue, isFn } from '../src/utils.js'; +import { isStr, + isPlainObject, + isNumber, + isArray, + isFn, + deepAccess, + deepSetValue, + logMessage } from '../src/utils.js'; const BIDDER_CODE = 'jwplayer'; const URL = 'https://ib.adnxs.com/openrtb2/prebid'; @@ -10,7 +17,7 @@ const URL = 'https://ib.adnxs.com/openrtb2/prebid'; const GVLID = 1046; const SUPPORTED_AD_TYPES = [VIDEO]; -// Video Parameters +// Video Parameters // https://docs.prebid.org/dev-docs/bidder-adaptor.html#step-2-accept-video-parameters-and-pass-them-to-your-server const VIDEO_ORTB_PARAMS = [ 'mimes', @@ -90,7 +97,7 @@ export const spec = { function buildRequest(bidRequest, bidderRequest) { // Open RTB Request Object const openrtbRequest = { - id: bidRequest.params.bidId, + id: bidRequest.bidId, imp: buildRequestImpression(bidRequest, bidderRequest), site: buildRequestSite(bidRequest), device: buildRequestDevice() @@ -107,7 +114,7 @@ function buildRequest(bidRequest, bidderRequest) { deepSetValue(openrtbRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - return JSON.stringify(openrtbRequest);; + return JSON.stringify(openrtbRequest); } function buildRequestImpression(bidRequest) { @@ -115,15 +122,16 @@ function buildRequestImpression(bidRequest) { const impressionObject = { id: bidRequest.adUnitCode, - secure: isSecure() ? 1 : 0 }; impressionObject.video = buildImpressionVideo(bidRequest); const bidFloorData = buildBidFloorData(bidRequest); - impressionObject.bidfloor = bidFloorData.floor; - impressionObject.bidfloorcur = bidFloorData.currency; - + if (bidFloorData) { + impressionObject.bidfloor = bidFloorData.floor; + impressionObject.bidfloorcur = bidFloorData.currency; + } + impressionObject.ext = buildImpressionExtension(bidRequest); impressions.push(impressionObject); @@ -156,18 +164,19 @@ function buildImpressionExtension(bidRequest) { function buildBidFloorData(bidRequest) { const {params} = bidRequest; - // Bid Floor - const bidFloorRequest = { - currency: params.currency || 'USD', - mediaType: 'video', - size: '*' - }; + const currency = params.currency || 'USD'; let floorData; if (isFn(bidRequest.getFloor)) { + // Bid Floor + const bidFloorRequest = { + currency: currency, + mediaType: 'video', + size: '*' + }; floorData = bidRequest.getFloor(bidFloorRequest); } else if (params.bidfloor) { - floorData = {floor: params.bidfloor, currency: params.currency || 'USD'}; + floorData = {floor: params.bidfloor, currency: currency}; } return floorData; @@ -184,22 +193,9 @@ function buildRequestSite(bidRequest) { // Site Content if (videoParams.content && isPlainObject(videoParams.content)) { - openrtbRequest.site.content = {}; - const contentStringKeys = ['id', 'title', 'series', 'season', 'genre', 'contentrating', 'language', 'url']; - const contentNumberkeys = ['episode', 'prodq', 'context', 'livestream', 'len']; - const contentArrayKeys = ['cat']; - const contentObjectKeys = ['ext']; - for (const contentKey in videoBidderParams.content) { - if ( - (contentStringKeys.indexOf(contentKey) > -1 && isStr(videoParams.content[contentKey])) || - (contentNumberkeys.indexOf(contentKey) > -1 && isNumber(videoParams.content[contentKey])) || - (contentObjectKeys.indexOf(contentKey) > -1 && isPlainObject(videoParams.content[contentKey])) || - (contentArrayKeys.indexOf(contentKey) > -1 && isArray(videoParams.content[contentKey]) && - videoParams.content[contentKey].every(catStr => isStr(catStr)))) { - site.content[contentKey] = videoParams.content[contentKey]; - } else { - logMessage('JWPlayer bid adapter validation error: ', contentKey, ' is either not supported is OpenRTB V2.5 or value is undefined'); - } + site.content = {}; + for (const contentKey in videoParams.content) { + site.content[contentKey] = videoParams.content[contentKey]; } } return site; @@ -207,8 +203,7 @@ function buildRequestSite(bidRequest) { function buildRequestDevice() { return { - ua: navigator.userAgent, - ip: '' + ua: navigator.userAgent }; } diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js index 5abf6b7e8c7..62e34c32b47 100644 --- a/test/spec/modules/jwplayerBidAdapter_spec.js +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -6,6 +6,14 @@ describe('jwplayer adapter tests', function() { var sandbox, clock, frozenNow = new Date(); beforeEach(function() { + this.defaultBidderRequest = { + 'gdprConsent': { + 'consentString': '', + 'gdprApplies': true + }, + 'uspConsent': true + } + sandbox = sinon.sandbox.create(); clock = sinon.useFakeTimers(frozenNow.getTime()); }); @@ -37,7 +45,61 @@ describe('jwplayer adapter tests', function() { }); }); - describe('buildRequests for video', function() {}); + describe('buildRequests for video', function() { + it('buildRequests works', function() { + const bidRequests = [ + { + 'bidder': 'jwplayer', + 'params': { + 'placementId': 12345 + }, + 'mediaTypes': { + 'video': { + 'playerSize': [640, 480], + 'content': {} + } + }, + 'bidRequestsCount': 1, + 'adUnitCode': 'testAdUnitCode', + 'bidId': 'testBidId' + } + ] + + const serverRequests = spec.buildRequests(bidRequests, this.defaultBidderRequest); + + /* eslint-disable no-console */ + + serverRequests.forEach(serverRequest => { + expect(serverRequest.url).to.have.string('https://ib.adnxs.com/openrtb2/prebid'); + expect(serverRequest.method).to.equal('POST'); + + const openrtbRequest = JSON.parse(serverRequest.data); + console.log(openrtbRequest); + expect(openrtbRequest.id).to.not.equal(null); + expect(openrtbRequest.id).to.have.string('testBidId'); + + expect(openrtbRequest.site).to.not.equal(null); + expect(openrtbRequest.device).to.not.equal(null); + expect(openrtbRequest.device.ua).to.equal(navigator.userAgent); + + expect(openrtbRequest.imp).to.not.equal(null); + expect(openrtbRequest.imp[0]).to.not.equal(null); + expect(openrtbRequest.imp[0].video).to.not.equal(null); + expect(openrtbRequest.imp[0].ext).to.not.equal(null); + expect(openrtbRequest.imp[0].ext.appnexus).to.not.equal(null); + expect(openrtbRequest.imp[0].ext.appnexus.placement_id).to.not.equal(null); + expect(openrtbRequest.imp[0].ext.appnexus.placement_id).to.equal(12345); + + expect(openrtbRequest.user).to.not.equal(null); + expect(openrtbRequest.user.ext).to.not.equal(null); + + expect(openrtbRequest.regs).to.not.equal(null); + expect(openrtbRequest.regs.ext).to.not.equal(null); + expect(openrtbRequest.regs.ext.gdpr).to.equal(1); + expect(openrtbRequest.regs.ext.us_privacy).to.equal(true); + }) + }); + }); describe('interpretResponse for video', function() {}); From 147170a67146fb6c544eddf1f28c7f5792fc398a Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Thu, 2 Jun 2022 19:16:46 -0400 Subject: [PATCH 13/48] removed superfluous array --- modules/jwplayerBidAdapter.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 17b2e83ff34..f6421b3a0f1 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -118,8 +118,6 @@ function buildRequest(bidRequest, bidderRequest) { } function buildRequestImpression(bidRequest) { - const impressions = []; - const impressionObject = { id: bidRequest.adUnitCode, }; @@ -134,9 +132,7 @@ function buildRequestImpression(bidRequest) { impressionObject.ext = buildImpressionExtension(bidRequest); - impressions.push(impressionObject); - - return impressions; + return [impressionObject]; } function buildImpressionVideo(bidRequest) { From 2bc3a217eb39414c2f591ba619644c2aae3c7b4b Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Tue, 7 Jun 2022 14:50:06 -0400 Subject: [PATCH 14/48] fixed nits --- modules/jwplayerBidAdapter.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index f6421b3a0f1..f1283e50b58 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -95,7 +95,6 @@ export const spec = { }; function buildRequest(bidRequest, bidderRequest) { - // Open RTB Request Object const openrtbRequest = { id: bidRequest.bidId, imp: buildRequestImpression(bidRequest, bidderRequest), @@ -140,7 +139,6 @@ function buildImpressionVideo(bidRequest) { const video = {}; - // Obtain all ORTB params related video from Ad Unit VIDEO_ORTB_PARAMS.forEach((param) => { if (videoParams.hasOwnProperty(param)) { video[param] = videoParams[param]; @@ -164,10 +162,9 @@ function buildBidFloorData(bidRequest) { let floorData; if (isFn(bidRequest.getFloor)) { - // Bid Floor const bidFloorRequest = { currency: currency, - mediaType: 'video', + mediaType: VIDEO, size: '*' }; floorData = bidRequest.getFloor(bidFloorRequest); From 6064e4000a32a39e8dca8052750019bd584024f6 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Wed, 8 Jun 2022 14:50:10 -0400 Subject: [PATCH 15/48] added feedback to jw adapter and tests for buildRequests --- modules/jwplayerBidAdapter.js | 24 ++++++++------------ test/spec/modules/jwplayerBidAdapter_spec.js | 2 +- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index f1283e50b58..7fe74dec7ee 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -1,15 +1,11 @@ -// import * as utils from 'src/utils'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -// import { config } from 'src/config'; import { VIDEO } from '../src/mediaTypes.js'; -import { isStr, +import { isPlainObject, - isNumber, - isArray, isFn, deepAccess, - deepSetValue, - logMessage } from '../src/utils.js'; + deepSetValue } from '../src/utils.js'; +import { config } from '../src/config.js'; const BIDDER_CODE = 'jwplayer'; const URL = 'https://ib.adnxs.com/openrtb2/prebid'; @@ -176,19 +172,19 @@ function buildBidFloorData(bidRequest) { } function buildRequestSite(bidRequest) { + const configSite = config.getConfig('ortb2.site'); + const site = { - domain: window.location.hostname, - page: window.location.href, + domain: (configSite && configSite.domain) ? configSite.domain : window.location.hostname, + page: (configSite && configSite.page) ? configSite.page : window.location.href, ref: bidRequest.refererInfo ? bidRequest.refererInfo.referer || null : null }; - const videoParams = deepAccess(bidRequest, 'mediaTypes.video', {}); - // Site Content - if (videoParams.content && isPlainObject(videoParams.content)) { + if (configSite && configSite.content && isPlainObject(configSite.content)) { site.content = {}; - for (const contentKey in videoParams.content) { - site.content[contentKey] = videoParams.content[contentKey]; + for (const contentKey in configSite.content) { + site.content[contentKey] = configSite.content[contentKey]; } } return site; diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js index 62e34c32b47..5b461e952c3 100644 --- a/test/spec/modules/jwplayerBidAdapter_spec.js +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -74,7 +74,7 @@ describe('jwplayer adapter tests', function() { expect(serverRequest.method).to.equal('POST'); const openrtbRequest = JSON.parse(serverRequest.data); - console.log(openrtbRequest); + expect(openrtbRequest.id).to.not.equal(null); expect(openrtbRequest.id).to.have.string('testBidId'); From b3092c15b49589ae87879cfc6a55e82b11297684 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Fri, 10 Jun 2022 14:58:13 -0400 Subject: [PATCH 16/48] added feedback --- modules/jwplayerBidAdapter.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 7fe74dec7ee..fb9e8b24ace 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -172,20 +172,18 @@ function buildBidFloorData(bidRequest) { } function buildRequestSite(bidRequest) { - const configSite = config.getConfig('ortb2.site'); + const configSite = config.getConfig('ortb2.site') || {}; const site = { - domain: (configSite && configSite.domain) ? configSite.domain : window.location.hostname, - page: (configSite && configSite.page) ? configSite.page : window.location.href, + domain: config.publisherDomain || configSite.domain || window.location.hostname, + page: config.pageUrl || configSite.page || window.location.href, ref: bidRequest.refererInfo ? bidRequest.refererInfo.referer || null : null }; // Site Content if (configSite && configSite.content && isPlainObject(configSite.content)) { site.content = {}; - for (const contentKey in configSite.content) { - site.content[contentKey] = configSite.content[contentKey]; - } + site.content = configSite.content; } return site; } From 13c3debd876ed1e812b6c7e45fcee8ab5a684d79 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Fri, 10 Jun 2022 16:28:31 -0400 Subject: [PATCH 17/48] one last feedback for request site --- modules/jwplayerBidAdapter.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index fb9e8b24ace..20df594fe28 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -177,12 +177,15 @@ function buildRequestSite(bidRequest) { const site = { domain: config.publisherDomain || configSite.domain || window.location.hostname, page: config.pageUrl || configSite.page || window.location.href, - ref: bidRequest.refererInfo ? bidRequest.refererInfo.referer || null : null }; + const referer = bidRequest.refererInfo && bidRequest.refererInfo.referer; + if (referer) { + site.ref = referer; + } + // Site Content if (configSite && configSite.content && isPlainObject(configSite.content)) { - site.content = {}; site.content = configSite.content; } return site; From adef057366b2bced12db54b772c048ddc07f54d8 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Fri, 10 Jun 2022 16:38:39 -0400 Subject: [PATCH 18/48] added site object unit test --- modules/jwplayerBidAdapter.js | 2 +- test/spec/modules/jwplayerBidAdapter_spec.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 20df594fe28..0649e6c6dfd 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -181,7 +181,7 @@ function buildRequestSite(bidRequest) { const referer = bidRequest.refererInfo && bidRequest.refererInfo.referer; if (referer) { - site.ref = referer; + site.ref = referer; } // Site Content diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js index 5b461e952c3..8bd0cc7a01e 100644 --- a/test/spec/modules/jwplayerBidAdapter_spec.js +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -79,6 +79,7 @@ describe('jwplayer adapter tests', function() { expect(openrtbRequest.id).to.have.string('testBidId'); expect(openrtbRequest.site).to.not.equal(null); + expect(openrtbRequest.site).to.be.an('object'); expect(openrtbRequest.device).to.not.equal(null); expect(openrtbRequest.device.ua).to.equal(navigator.userAgent); From c504d05a6a006c93a8c4106224d10a13b7ef371d Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Mon, 20 Jun 2022 16:50:27 -0400 Subject: [PATCH 19/48] fixed buildRequestSite object --- modules/jwplayerBidAdapter.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 0649e6c6dfd..a66e6e7276d 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -172,22 +172,16 @@ function buildBidFloorData(bidRequest) { } function buildRequestSite(bidRequest) { - const configSite = config.getConfig('ortb2.site') || {}; + const site = config.getConfig('ortb2.site') || {}; - const site = { - domain: config.publisherDomain || configSite.domain || window.location.hostname, - page: config.pageUrl || configSite.page || window.location.href, - }; + site.domain = site.domain || config.publisherDomain || window.location.hostname; + site.page = site.page || config.pageUrl || window.location.href; const referer = bidRequest.refererInfo && bidRequest.refererInfo.referer; - if (referer) { + if (!site.ref && referer) { site.ref = referer; } - // Site Content - if (configSite && configSite.content && isPlainObject(configSite.content)) { - site.content = configSite.content; - } return site; } From d746cf488ffd542ae01d5e7604ff883080c8bfbb Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Mon, 20 Jun 2022 17:47:43 -0400 Subject: [PATCH 20/48] added unit tests for site object --- modules/jwplayerBidAdapter.js | 3 +- test/spec/modules/jwplayerBidAdapter_spec.js | 35 ++++++++++++++------ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index a66e6e7276d..2bd13a41d37 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -1,7 +1,6 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO } from '../src/mediaTypes.js'; import { - isPlainObject, isFn, deepAccess, deepSetValue } from '../src/utils.js'; @@ -172,7 +171,9 @@ function buildBidFloorData(bidRequest) { } function buildRequestSite(bidRequest) { + /* eslint-disable no-console */ const site = config.getConfig('ortb2.site') || {}; + console.log(site); site.domain = site.domain || config.publisherDomain || window.location.hostname; site.page = site.page || config.pageUrl || window.location.href; diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js index 8bd0cc7a01e..d4fa08d29d3 100644 --- a/test/spec/modules/jwplayerBidAdapter_spec.js +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -3,24 +3,17 @@ import { spec } from 'modules/jwplayerBidAdapter.js'; import { config } from 'src/config.js'; describe('jwplayer adapter tests', function() { - var sandbox, clock, frozenNow = new Date(); - beforeEach(function() { this.defaultBidderRequest = { 'gdprConsent': { 'consentString': '', 'gdprApplies': true }, - 'uspConsent': true + 'uspConsent': true, + 'refererInfo': { + 'referer': 'https://example.com' + } } - - sandbox = sinon.sandbox.create(); - clock = sinon.useFakeTimers(frozenNow.getTime()); - }); - - afterEach(function() { - sandbox.restore(); - clock.restore(); }); describe('isBidRequestValid', function() { @@ -65,6 +58,19 @@ describe('jwplayer adapter tests', function() { } ] + let sandbox = sinon.sandbox.create(); + sandbox.stub(config, 'getConfig').callsFake((key) => { + const config = { + 'ortb2': { + site: { + domain: 'page.example.com', + page: 'https://examplepage.com' + } + } + }; + return config[key]; + }) + const serverRequests = spec.buildRequests(bidRequests, this.defaultBidderRequest); /* eslint-disable no-console */ @@ -80,6 +86,13 @@ describe('jwplayer adapter tests', function() { expect(openrtbRequest.site).to.not.equal(null); expect(openrtbRequest.site).to.be.an('object'); + expect(openrtbRequest.site.domain).to.be.a('string'); + expect(openrtbRequest.site.domain).to.equal('page.example.com'); + expect(openrtbRequest.site.page).to.be.a('string'); + expect(openrtbRequest.site.page).to.equal('https://examplepage.com'); + expect(openrtbRequest.site.ref).to.be.a('string'); + expect(openrtbRequest.site.ref).to.equal('https://example.com'); + expect(openrtbRequest.device).to.not.equal(null); expect(openrtbRequest.device.ua).to.equal(navigator.userAgent); From 8e54e50372efa72a978fd045cd269b2dc1670db2 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Tue, 28 Jun 2022 17:18:58 -0400 Subject: [PATCH 21/48] fixed unit tests for buildRequestSite --- modules/jwplayerBidAdapter.js | 8 +++---- test/spec/modules/jwplayerBidAdapter_spec.js | 22 +++++++++----------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 2bd13a41d37..21c68b1f712 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -93,7 +93,7 @@ function buildRequest(bidRequest, bidderRequest) { const openrtbRequest = { id: bidRequest.bidId, imp: buildRequestImpression(bidRequest, bidderRequest), - site: buildRequestSite(bidRequest), + site: buildRequestSite(bidderRequest), device: buildRequestDevice() }; @@ -170,15 +170,13 @@ function buildBidFloorData(bidRequest) { return floorData; } -function buildRequestSite(bidRequest) { - /* eslint-disable no-console */ +function buildRequestSite(bidderRequest) { const site = config.getConfig('ortb2.site') || {}; - console.log(site); site.domain = site.domain || config.publisherDomain || window.location.hostname; site.page = site.page || config.pageUrl || window.location.href; - const referer = bidRequest.refererInfo && bidRequest.refererInfo.referer; + const referer = bidderRequest.refererInfo && bidderRequest.refererInfo.referer; if (!site.ref && referer) { site.ref = referer; } diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js index d4fa08d29d3..9f443e62654 100644 --- a/test/spec/modules/jwplayerBidAdapter_spec.js +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -61,20 +61,16 @@ describe('jwplayer adapter tests', function() { let sandbox = sinon.sandbox.create(); sandbox.stub(config, 'getConfig').callsFake((key) => { const config = { - 'ortb2': { - site: { - domain: 'page.example.com', - page: 'https://examplepage.com' - } + 'ortb2.site': { + domain: 'page.example.com', + page: 'https://examplepage.com' } }; return config[key]; - }) + }); const serverRequests = spec.buildRequests(bidRequests, this.defaultBidderRequest); - /* eslint-disable no-console */ - serverRequests.forEach(serverRequest => { expect(serverRequest.url).to.have.string('https://ib.adnxs.com/openrtb2/prebid'); expect(serverRequest.method).to.equal('POST'); @@ -87,11 +83,11 @@ describe('jwplayer adapter tests', function() { expect(openrtbRequest.site).to.not.equal(null); expect(openrtbRequest.site).to.be.an('object'); expect(openrtbRequest.site.domain).to.be.a('string'); - expect(openrtbRequest.site.domain).to.equal('page.example.com'); + expect(openrtbRequest.site.domain).to.have.string('page.example.com'); expect(openrtbRequest.site.page).to.be.a('string'); - expect(openrtbRequest.site.page).to.equal('https://examplepage.com'); + expect(openrtbRequest.site.page).to.have.string('https://examplepage.com'); expect(openrtbRequest.site.ref).to.be.a('string'); - expect(openrtbRequest.site.ref).to.equal('https://example.com'); + expect(openrtbRequest.site.ref).to.have.string('https://example.com'); expect(openrtbRequest.device).to.not.equal(null); expect(openrtbRequest.device.ua).to.equal(navigator.userAgent); @@ -111,7 +107,9 @@ describe('jwplayer adapter tests', function() { expect(openrtbRequest.regs.ext).to.not.equal(null); expect(openrtbRequest.regs.ext.gdpr).to.equal(1); expect(openrtbRequest.regs.ext.us_privacy).to.equal(true); - }) + }); + + sandbox.restore(); }); }); From 6b0ef4745e634e4caac3bc21111415a91242d380 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Tue, 7 Jun 2022 11:32:51 -0400 Subject: [PATCH 22/48] skeleton of interpretResponse --- modules/jwplayerBidAdapter.js | 46 ++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 21c68b1f712..6ae9fe330b4 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -77,9 +77,53 @@ export const spec = { * Unpack the response from the server into a list of bids. * * @param {*} serverResponse A successful response from the server. + * @param bidderRequest * @return {Bid[]} An array of bids which were nested inside the server. */ - interpretResponse: function(serverResponse, request) {}, + interpretResponse: function(serverResponse, bidderRequest) { + const bidResponses = []; + const serverResponseBody = serverResponse.body; + // const serverResponseHead = serverResponse.headers.get(); + + if (serverResponseBody && isArray(serverResponseBody.seatbid)) { + serverResponseBody.seatbid.forEach(seatBids => { + seatBids.bid.forEach(bid => { + const bidResponse = { + requestId: serverResponseBody.requestId, + cpm: serverResponseBody.cpm, + currency: serverResponseBody.currency, + width: serverResponseBody.width, + height: serverResponseBody.height, + creativeId: serverResponseBody.creativeId, + netRevenue: true, + ttl: TIME_TO_LIVE, + ad: CREATIVE_BODY, + mediaType: VIDEO, + meta: { + advertiserDomains: [ARRAY_OF_ADVERTISER_DOMAINS], + advertiserId: ADVERTISER_ID, + advertiserName: ADVERTISER_NAME, + agencyId: AGENCY_ID, + agencyName: AGENCY_NAME, + brandId: BRAND_ID, + brandName: BRAND_NAME, + dchain: DEMAND_CHAIN_OBJECT, + demandSource: DEMAND_SOURCE, + mediaType: MEDIA_TYPE, + networkId: NETWORK_ID, + networkName: NETWORK_NAME, + primaryCatId: IAB_CATEGORY, + secondaryCatIds: [ARRAY_OF_IAB_CATEGORIES] + } + }; + bidResponses.push(bidResponse); + }); + }); + + }; + return bidResponses; + }, + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) {}, // Optional? From 61a4705609dcdf1bc1cf65b6ad208941802c22d4 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Mon, 13 Jun 2022 16:47:36 -0400 Subject: [PATCH 23/48] initial attempt at interpretResponse --- modules/jwplayerBidAdapter.js | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 6ae9fe330b4..8f4c7c40084 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -95,26 +95,11 @@ export const spec = { width: serverResponseBody.width, height: serverResponseBody.height, creativeId: serverResponseBody.creativeId, - netRevenue: true, - ttl: TIME_TO_LIVE, - ad: CREATIVE_BODY, - mediaType: VIDEO, - meta: { - advertiserDomains: [ARRAY_OF_ADVERTISER_DOMAINS], - advertiserId: ADVERTISER_ID, - advertiserName: ADVERTISER_NAME, - agencyId: AGENCY_ID, - agencyName: AGENCY_NAME, - brandId: BRAND_ID, - brandName: BRAND_NAME, - dchain: DEMAND_CHAIN_OBJECT, - demandSource: DEMAND_SOURCE, - mediaType: MEDIA_TYPE, - networkId: NETWORK_ID, - networkName: NETWORK_NAME, - primaryCatId: IAB_CATEGORY, - secondaryCatIds: [ARRAY_OF_IAB_CATEGORIES] - } + vastXml: serverResponseBody.vastXml, + netRevenue: serverResponseBody.netRevenue, + ttl: serverResponseBody.ttl, + ad: serverResponseBody.ad, + mediaType: serverResponseBody.mediaType, }; bidResponses.push(bidResponse); }); From 9cd64d94375119c9a05369eea69bf55b76ba3087 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Mon, 20 Jun 2022 16:36:52 -0400 Subject: [PATCH 24/48] started unit test --- test/spec/modules/jwplayerBidAdapter_spec.js | 23 +++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js index 9f443e62654..bda78f95867 100644 --- a/test/spec/modules/jwplayerBidAdapter_spec.js +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -113,7 +113,28 @@ describe('jwplayer adapter tests', function() { }); }); - describe('interpretResponse for video', function() {}); + describe('interpretResponse for video', function() { + + const bidResponse = { + id: 'testId', + impid: '274395c06a24e5', + price: 1, + w: 300, + h: 250, + } + + const serverResponse = { + body: { + id: 'testId', + seatbid: [ + { + bid: [ bidResponse ], + seat: 1000 + } + ] + } + } + }); describe('user sync handler', function() {}); }); From a361926bde7c8101f8157417ec9d34eade711951 Mon Sep 17 00:00:00 2001 From: Jorge Rocha Date: Mon, 27 Jun 2022 22:52:39 -0400 Subject: [PATCH 25/48] finished interpretResponse with unit test --- modules/jwplayerBidAdapter.js | 33 +++++++++-------- test/spec/modules/jwplayerBidAdapter_spec.js | 37 +++++++++++++++----- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 8f4c7c40084..e46ef5060e3 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -1,11 +1,11 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO } from '../src/mediaTypes.js'; import { + isArray, isFn, deepAccess, deepSetValue } from '../src/utils.js'; import { config } from '../src/config.js'; - const BIDDER_CODE = 'jwplayer'; const URL = 'https://ib.adnxs.com/openrtb2/prebid'; @@ -80,31 +80,34 @@ export const spec = { * @param bidderRequest * @return {Bid[]} An array of bids which were nested inside the server. */ - interpretResponse: function(serverResponse, bidderRequest) { + interpretResponse: function(serverResponse) { const bidResponses = []; const serverResponseBody = serverResponse.body; - // const serverResponseHead = serverResponse.headers.get(); + + const bidId = serverResponse.bidid; + const cur = serverResponse.cur; if (serverResponseBody && isArray(serverResponseBody.seatbid)) { serverResponseBody.seatbid.forEach(seatBids => { seatBids.bid.forEach(bid => { const bidResponse = { - requestId: serverResponseBody.requestId, - cpm: serverResponseBody.cpm, - currency: serverResponseBody.currency, - width: serverResponseBody.width, - height: serverResponseBody.height, - creativeId: serverResponseBody.creativeId, - vastXml: serverResponseBody.vastXml, - netRevenue: serverResponseBody.netRevenue, - ttl: serverResponseBody.ttl, - ad: serverResponseBody.ad, - mediaType: serverResponseBody.mediaType, + requestId: bidId, + cpm: bid.price, + currency: cur, + width: bid.w, + height: bid.h, + creativeId: bid.adid, + vastXml: bid.adm, + netRevenue: true, + ttl: 500, + ad: bid.adm, + meta: { + advertiserDomains: bid.adomain + } }; bidResponses.push(bidResponse); }); }); - }; return bidResponses; }, diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js index bda78f95867..9037108284e 100644 --- a/test/spec/modules/jwplayerBidAdapter_spec.js +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -114,26 +114,47 @@ describe('jwplayer adapter tests', function() { }); describe('interpretResponse for video', function() { - const bidResponse = { id: 'testId', - impid: '274395c06a24e5', - price: 1, - w: 300, - h: 250, + impid: 'test-imp-id', + price: 1.000000, + adid: '97517771', + adm: 'some-test-ad', + adomain: ['prebid.com'], + w: 1, + h: 1, } - + const serverResponse = { body: { - id: 'testId', + id: 'test-request-id', seatbid: [ { bid: [ bidResponse ], seat: 1000 } ] - } + }, + bidid: '123456789', + cur: 'USD' } + + const bidResponses = spec.interpretResponse(serverResponse); + + expect(bidResponses[0]).to.not.equal(null); + expect(bidResponses[0].requestId).to.equal('123456789'); + expect(bidResponses[0].cpm).to.equal(1); + expect(bidResponses[0].currency).to.equal('USD'); + expect(bidResponses[0].width).to.equal(1); + expect(bidResponses[0].height).to.equal(1); + expect(bidResponses[0].creativeId).to.equal('97517771'); + expect(bidResponses[0].vastXml).to.equal('some-test-ad'); + expect(bidResponses[0].netRevenue).to.equal(true); + expect(bidResponses[0].ttl).to.equal(500); + expect(bidResponses[0].ad).to.equal('some-test-ad'); + expect(bidResponses[0].meta).to.not.equal(null); + expect(bidResponses[0].meta.advertiserDomains).to.not.equal(null); + expect(bidResponses[0].meta.advertiserDomains[0]).to.equal('prebid.com'); }); describe('user sync handler', function() {}); From 93a1fcf0e9dead93223aaae63bcd406a932e7d6a Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Wed, 19 Jul 2023 19:12:51 -0400 Subject: [PATCH 26/48] populates schain as well as other params --- modules/jwplayerBidAdapter.js | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index e46ef5060e3..6c1336f3f9a 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -21,6 +21,7 @@ const VIDEO_ORTB_PARAMS = [ 'protocols', 'startdelay', 'placement', + 'plcmt', 'skip', 'skipafter', 'minbitrate', @@ -43,11 +44,12 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function(bid) { - if (!bid || !bid.params) { + const params = bid && bid.params; + if (!params) { return false; } - return !!bid.params.placementId && !!bid.params.pubId; + return !!params.placementId && !!params.publisherId && !!params.siteId; }, /** @@ -129,7 +131,7 @@ function buildRequest(bidRequest, bidderRequest) { device: buildRequestDevice() }; - // Attaching GDPR Consent Params + // GDPR Consent Params if (bidderRequest.gdprConsent) { deepSetValue(openrtbRequest, 'user.ext.consent', bidderRequest.gdprConsent.consentString); deepSetValue(openrtbRequest, 'regs.ext.gdpr', (bidderRequest.gdprConsent.gdprApplies ? 1 : 0)); @@ -140,6 +142,12 @@ function buildRequest(bidRequest, bidderRequest) { deepSetValue(openrtbRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); } + if (bidderRequest.schain) { + deepSetValue(openrtbRequest, 'source.schain', bidderRequest.schain); + } + + openrtbRequest.tmax = bidderRequest.timeout || 200; + return JSON.stringify(openrtbRequest); } @@ -177,14 +185,18 @@ function buildImpressionVideo(bidRequest) { function buildImpressionExtension(bidRequest) { return { - appnexus: { - placement_id: bidRequest.params.placementId + prebid: { + bidder: { + jwplayer: { + placementId: bidRequest.params.placementId + } + } } }; } function buildBidFloorData(bidRequest) { - const {params} = bidRequest; + const { params } = bidRequest; const currency = params.currency || 'USD'; let floorData; @@ -196,7 +208,7 @@ function buildBidFloorData(bidRequest) { }; floorData = bidRequest.getFloor(bidFloorRequest); } else if (params.bidfloor) { - floorData = {floor: params.bidfloor, currency: currency}; + floorData = { floor: params.bidfloor, currency: currency }; } return floorData; @@ -213,6 +225,9 @@ function buildRequestSite(bidderRequest) { site.ref = referer; } + deepSetValue(site, 'publisher.ext.jwplayer.publisherId', bidderRequest.params.publisherId); + deepSetValue(site, 'publisher.ext.jwplayer.siteId', bidderRequest.params.siteId); + return site; } From ac73525a63306f5eb08eadde4c530793faf327de Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Wed, 19 Jul 2023 19:24:27 -0400 Subject: [PATCH 27/48] reads schain from bidrequest --- modules/jwplayerBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 6c1336f3f9a..5f0c63a5d01 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -142,8 +142,8 @@ function buildRequest(bidRequest, bidderRequest) { deepSetValue(openrtbRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - if (bidderRequest.schain) { - deepSetValue(openrtbRequest, 'source.schain', bidderRequest.schain); + if (bidRequest.schain) { + deepSetValue(openrtbRequest, 'source.schain', bidRequest.schain); } openrtbRequest.tmax = bidderRequest.timeout || 200; From 3a36eaf2640c9931274e1ab5fddd91f3712eb586 Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Thu, 20 Jul 2023 12:01:25 -0400 Subject: [PATCH 28/48] updates unit tests --- modules/jwplayerBidAdapter.js | 8 ++--- test/spec/modules/jwplayerBidAdapter_spec.js | 36 ++++++++++++-------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 5f0c63a5d01..7f355fb2141 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -127,7 +127,7 @@ function buildRequest(bidRequest, bidderRequest) { const openrtbRequest = { id: bidRequest.bidId, imp: buildRequestImpression(bidRequest, bidderRequest), - site: buildRequestSite(bidderRequest), + site: buildRequestSite(bidRequest, bidderRequest), device: buildRequestDevice() }; @@ -214,7 +214,7 @@ function buildBidFloorData(bidRequest) { return floorData; } -function buildRequestSite(bidderRequest) { +function buildRequestSite(bidRequest, bidderRequest) { const site = config.getConfig('ortb2.site') || {}; site.domain = site.domain || config.publisherDomain || window.location.hostname; @@ -225,8 +225,8 @@ function buildRequestSite(bidderRequest) { site.ref = referer; } - deepSetValue(site, 'publisher.ext.jwplayer.publisherId', bidderRequest.params.publisherId); - deepSetValue(site, 'publisher.ext.jwplayer.siteId', bidderRequest.params.siteId); + deepSetValue(site, 'publisher.ext.jwplayer.publisherId', bidRequest.params.publisherId); + deepSetValue(site, 'publisher.ext.jwplayer.siteId', bidRequest.params.siteId); return site; } diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js index 9037108284e..e9ade798fed 100644 --- a/test/spec/modules/jwplayerBidAdapter_spec.js +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -2,7 +2,7 @@ import { expect, assert } from 'chai'; import { spec } from 'modules/jwplayerBidAdapter.js'; import { config } from 'src/config.js'; -describe('jwplayer adapter tests', function() { +describe('jwplayer bid adapter tests', function() { beforeEach(function() { this.defaultBidderRequest = { 'gdprConsent': { @@ -17,39 +17,48 @@ describe('jwplayer adapter tests', function() { }); describe('isBidRequestValid', function() { - it('passes when the bid includes a placement ID and a publisher ID', function() { - assert(spec.isBidRequestValid({params: {placementId: 'foo', pubId: 'bar'}}) === true); + it('passes when the bid includes a placement ID, a publisher ID and a site ID', function() { + assert(spec.isBidRequestValid({params: {placementId: 'foo', publisherId: 'bar', siteId: 'siteId '}}) === true); }); - it('fails when the bid does not include a placement ID', function() { - assert(spec.isBidRequestValid({params: {pubId: 'foo'}}) === false); + it('fails when the bid request only includes a publisher ID', function() { + assert(spec.isBidRequestValid({params: {publisherId: 'foo'}}) === false); }); - it('fails when the bid does not include a publisher ID', function() { + it('fails when the bid request only includes a placement ID', function() { assert(spec.isBidRequestValid({params: {placementId: 'foo'}}) === false); }); - it('fails when bid is falsey', function() { + it('fails when the bid request only includes a site ID', function() { + assert(spec.isBidRequestValid({params: {siteId: 'foo'}}) === false); + }); + + it('fails when bid is undefined', function() { assert(spec.isBidRequestValid() === false); }); - it('fails when the bid has no params at all', function() { + it('fails when bid is null', function() { + assert(spec.isBidRequestValid(null) === false); + }); + + it('fails when the bid has no params', function() { assert(spec.isBidRequestValid({}) === false); }); }); describe('buildRequests for video', function() { - it('buildRequests works', function() { + it('should build requests', function() { const bidRequests = [ { 'bidder': 'jwplayer', 'params': { - 'placementId': 12345 + 'placementId': 'testPlacementId', + 'publisherId': 'testPublisherId', + 'siteId': 'testSiteId' }, 'mediaTypes': { 'video': { 'playerSize': [640, 480], - 'content': {} } }, 'bidRequestsCount': 1, @@ -96,9 +105,8 @@ describe('jwplayer adapter tests', function() { expect(openrtbRequest.imp[0]).to.not.equal(null); expect(openrtbRequest.imp[0].video).to.not.equal(null); expect(openrtbRequest.imp[0].ext).to.not.equal(null); - expect(openrtbRequest.imp[0].ext.appnexus).to.not.equal(null); - expect(openrtbRequest.imp[0].ext.appnexus.placement_id).to.not.equal(null); - expect(openrtbRequest.imp[0].ext.appnexus.placement_id).to.equal(12345); + expect(openrtbRequest.imp[0].ext.prebid.bidder.jwplayer.placementId).to.not.equal(null); + expect(openrtbRequest.imp[0].ext.prebid.bidder.jwplayer.placementId).to.equal('testPlacementId'); expect(openrtbRequest.user).to.not.equal(null); expect(openrtbRequest.user.ext).to.not.equal(null); From 9a49f6a4b37d838de7b02b2db424c1ec82110e24 Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Thu, 20 Jul 2023 17:54:49 -0400 Subject: [PATCH 29/48] improves tests --- modules/jwplayerBidAdapter.js | 6 +- test/spec/modules/jwplayerBidAdapter_spec.js | 125 +++++++++++++------ 2 files changed, 92 insertions(+), 39 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 7f355fb2141..bb74d7f31b8 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -7,7 +7,7 @@ import { deepSetValue } from '../src/utils.js'; import { config } from '../src/config.js'; const BIDDER_CODE = 'jwplayer'; -const URL = 'https://ib.adnxs.com/openrtb2/prebid'; +const URL = 'https://vpb-server.jwplayer.com/openrtb2/auction'; const GVLID = 1046; const SUPPORTED_AD_TYPES = [VIDEO]; @@ -207,8 +207,8 @@ function buildBidFloorData(bidRequest) { size: '*' }; floorData = bidRequest.getFloor(bidFloorRequest); - } else if (params.bidfloor) { - floorData = { floor: params.bidfloor, currency: currency }; + } else if (params.bidFloor) { + floorData = { floor: params.bidFloor, currency: currency }; } return floorData; diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js index e9ade798fed..1498063f971 100644 --- a/test/spec/modules/jwplayerBidAdapter_spec.js +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -6,10 +6,10 @@ describe('jwplayer bid adapter tests', function() { beforeEach(function() { this.defaultBidderRequest = { 'gdprConsent': { - 'consentString': '', + 'consentString': 'testConsentString', 'gdprApplies': true }, - 'uspConsent': true, + 'uspConsent': 'testCCPA', 'refererInfo': { 'referer': 'https://example.com' } @@ -47,23 +47,55 @@ describe('jwplayer bid adapter tests', function() { }); describe('buildRequests for video', function() { - it('should build requests', function() { + it('should include proper ortb params in requests', function() { const bidRequests = [ { - 'bidder': 'jwplayer', - 'params': { - 'placementId': 'testPlacementId', - 'publisherId': 'testPublisherId', - 'siteId': 'testSiteId' + bidder: 'jwplayer', + params: { + placementId: 'testPlacementId', + publisherId: 'testPublisherId', + siteId: 'testSiteId', + bidFloor: 10, + currency: 'EUR', }, - 'mediaTypes': { - 'video': { - 'playerSize': [640, 480], + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream', + mimes: [ + 'video/mp4', + 'application/javascript' + ], + protocols: [2, 3, 5, 6], + maxduration: 60, + minduration: 3, + startdelay: 0, + linearity: 1, + placement: 1, + plcmt: 1, + skip: 1, + skipafter: 4, + minbitrate: 500, + maxbitrate: 1000, + api: [2], + delivery: [2], + playbackmethod: [1], } }, - 'bidRequestsCount': 1, - 'adUnitCode': 'testAdUnitCode', - 'bidId': 'testBidId' + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'publisher.com', + sid: '00001', + hp: 1 + } + ] + }, + bidRequestsCount: 1, + adUnitCode: 'testAdUnitCode', + bidId: 'testBidId' } ] @@ -81,40 +113,61 @@ describe('jwplayer bid adapter tests', function() { const serverRequests = spec.buildRequests(bidRequests, this.defaultBidderRequest); serverRequests.forEach(serverRequest => { - expect(serverRequest.url).to.have.string('https://ib.adnxs.com/openrtb2/prebid'); + expect(serverRequest.url).to.equal('https://vpb-server.jwplayer.com/openrtb2/auction'); expect(serverRequest.method).to.equal('POST'); const openrtbRequest = JSON.parse(serverRequest.data); - expect(openrtbRequest.id).to.not.equal(null); - expect(openrtbRequest.id).to.have.string('testBidId'); + expect(openrtbRequest.id).to.equal('testBidId'); - expect(openrtbRequest.site).to.not.equal(null); - expect(openrtbRequest.site).to.be.an('object'); - expect(openrtbRequest.site.domain).to.be.a('string'); - expect(openrtbRequest.site.domain).to.have.string('page.example.com'); - expect(openrtbRequest.site.page).to.be.a('string'); - expect(openrtbRequest.site.page).to.have.string('https://examplepage.com'); - expect(openrtbRequest.site.ref).to.be.a('string'); - expect(openrtbRequest.site.ref).to.have.string('https://example.com'); + expect(openrtbRequest.site.domain).to.equal('page.example.com'); + expect(openrtbRequest.site.page).to.equal('https://examplepage.com'); + expect(openrtbRequest.site.ref).to.equal('https://example.com'); + expect(openrtbRequest.site.publisher.ext.jwplayer.publisherId).to.equal('testPublisherId'); + expect(openrtbRequest.site.publisher.ext.jwplayer.siteId).to.equal('testSiteId'); - expect(openrtbRequest.device).to.not.equal(null); expect(openrtbRequest.device.ua).to.equal(navigator.userAgent); - expect(openrtbRequest.imp).to.not.equal(null); - expect(openrtbRequest.imp[0]).to.not.equal(null); - expect(openrtbRequest.imp[0].video).to.not.equal(null); - expect(openrtbRequest.imp[0].ext).to.not.equal(null); - expect(openrtbRequest.imp[0].ext.prebid.bidder.jwplayer.placementId).to.not.equal(null); + expect(openrtbRequest.imp[0].id).to.equal('testAdUnitCode'); + expect(openrtbRequest.imp[0].video.mimes).to.deep.equal(['video/mp4', 'application/javascript']); + expect(openrtbRequest.imp[0].video.protocols).to.deep.equal([2, 3, 5, 6]); + expect(openrtbRequest.imp[0].video.api).to.deep.equal([2]); + expect(openrtbRequest.imp[0].video.startdelay).to.equal(0); + expect(openrtbRequest.imp[0].video.placement).to.equal(1); + expect(openrtbRequest.imp[0].video.plcmt).to.equal(1); + expect(openrtbRequest.imp[0].video.minduration).to.equal(3); + expect(openrtbRequest.imp[0].video.maxduration).to.equal(60); + expect(openrtbRequest.imp[0].video.skip).to.equal(1); + expect(openrtbRequest.imp[0].video.skipafter).to.equal(4); + expect(openrtbRequest.imp[0].video.minbitrate).to.equal(500); + expect(openrtbRequest.imp[0].video.maxbitrate).to.equal(1000); + expect(openrtbRequest.imp[0].video.delivery).to.deep.equal([2]); + expect(openrtbRequest.imp[0].video.playbackmethod).to.deep.equal([1]); + expect(openrtbRequest.imp[0].video.linearity).to.equal(1); + + expect(openrtbRequest.imp[0].bidfloor).to.equal(10); + expect(openrtbRequest.imp[0].bidfloorcur).to.equal('EUR'); + expect(openrtbRequest.imp[0].ext.prebid.bidder.jwplayer.placementId).to.equal('testPlacementId'); - expect(openrtbRequest.user).to.not.equal(null); - expect(openrtbRequest.user.ext).to.not.equal(null); + expect(openrtbRequest.user.ext.consent).to.equal('testConsentString'); - expect(openrtbRequest.regs).to.not.equal(null); - expect(openrtbRequest.regs.ext).to.not.equal(null); expect(openrtbRequest.regs.ext.gdpr).to.equal(1); - expect(openrtbRequest.regs.ext.us_privacy).to.equal(true); + expect(openrtbRequest.regs.ext.us_privacy).to.equal('testCCPA'); + + expect(openrtbRequest.source.schain).to.deep.equal({ + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'publisher.com', + sid: '00001', + hp: 1 + } + ] + }); + + expect(openrtbRequest.tmax).to.not.equal(null); }); sandbox.restore(); From 7cbc23c20759e265f3ea75c8ccb60566d8d1e4d7 Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Fri, 21 Jul 2023 17:32:52 -0400 Subject: [PATCH 30/48] tests tmax --- test/spec/modules/jwplayerBidAdapter_spec.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js index 1498063f971..71d59a3af01 100644 --- a/test/spec/modules/jwplayerBidAdapter_spec.js +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -12,7 +12,8 @@ describe('jwplayer bid adapter tests', function() { 'uspConsent': 'testCCPA', 'refererInfo': { 'referer': 'https://example.com' - } + }, + timeout: 1000, } }); @@ -167,7 +168,7 @@ describe('jwplayer bid adapter tests', function() { ] }); - expect(openrtbRequest.tmax).to.not.equal(null); + expect(openrtbRequest.tmax).to.equal(1000); }); sandbox.restore(); From 22ba376cdff52b9624bd216ec0bc7e97158e62a2 Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Fri, 21 Jul 2023 17:53:46 -0400 Subject: [PATCH 31/48] deletes obsolete comments --- modules/jwplayerBidAdapter.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index bb74d7f31b8..a696092f248 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -37,12 +37,6 @@ export const spec = { gvlid: GVLID, supportedMediaTypes: SUPPORTED_AD_TYPES, - /** - * Determines whether or not the given bid request is valid. - * - * @param {object} bid The bid to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ isBidRequestValid: function(bid) { const params = bid && bid.params; if (!params) { @@ -52,13 +46,6 @@ export const spec = { return !!params.placementId && !!params.publisherId && !!params.siteId; }, - /** - * Make a server request from the list of BidRequests. - * - * @param {BidRequest[]} bidRequests A non-empty list of bid requests, or ad units, which should be sent to the server. - * @param bidderRequest - * @return ServerRequest Info describing the request to the server. - */ buildRequests: function(bidRequests, bidderRequest) { if (!bidRequests) { return; From fac80723168be93ec8fe9ffef5bcf41f0eb3d0ab Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Mon, 24 Jul 2023 19:05:58 -0400 Subject: [PATCH 32/48] errors when url is missing --- modules/jwplayerBidAdapter.js | 87 +++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 10 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index a696092f248..aa5a7b5a8a9 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -1,11 +1,8 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO } from '../src/mediaTypes.js'; -import { - isArray, - isFn, - deepAccess, - deepSetValue } from '../src/utils.js'; +import { isArray, isFn, deepAccess, deepSetValue, getDNT, logError, logWarn } from '../src/utils.js'; import { config } from '../src/config.js'; + const BIDDER_CODE = 'jwplayer'; const URL = 'https://vpb-server.jwplayer.com/openrtb2/auction'; @@ -15,6 +12,10 @@ const SUPPORTED_AD_TYPES = [VIDEO]; // Video Parameters // https://docs.prebid.org/dev-docs/bidder-adaptor.html#step-2-accept-video-parameters-and-pass-them-to-your-server const VIDEO_ORTB_PARAMS = [ + 'pos', + 'w', + 'h', + 'playbackend', 'mimes', 'minduration', 'maxduration', @@ -51,6 +52,16 @@ export const spec = { return; } + if (!hasContentUrl(bidderRequest.ortb2)) { + logError(`${BIDDER_CODE}: cannot bid without a valid Content URL. Please populate ortb2.site.content.url`); + return; + } + + const warnings = getWarnings(bidderRequest); + warnings.forEach(warning => { + logWarn(`${BIDDER_CODE}: ${warning}`); + }) + return bidRequests.map(bidRequest => { const payload = buildRequest(bidRequest, bidderRequest); @@ -202,7 +213,7 @@ function buildBidFloorData(bidRequest) { } function buildRequestSite(bidRequest, bidderRequest) { - const site = config.getConfig('ortb2.site') || {}; + const site = bidderRequest.ortb2 || {}; site.domain = site.domain || config.publisherDomain || window.location.hostname; site.page = site.page || config.pageUrl || window.location.href; @@ -212,16 +223,72 @@ function buildRequestSite(bidRequest, bidderRequest) { site.ref = referer; } - deepSetValue(site, 'publisher.ext.jwplayer.publisherId', bidRequest.params.publisherId); - deepSetValue(site, 'publisher.ext.jwplayer.siteId', bidRequest.params.siteId); + const jwplayerPublisherExtChain = 'publisher.ext.jwplayer.'; + + deepSetValue(site, jwplayerPublisherExtChain + 'publisherId', bidRequest.params.publisherId); + deepSetValue(site, jwplayerPublisherExtChain + 'siteId', bidRequest.params.siteId); return site; } function buildRequestDevice() { - return { - ua: navigator.userAgent + const device = { + h: screen.height, + w: screen.width, + ua: navigator.userAgent, + dnt: getDNT() ? 1 : 0, + js: 1 }; + + const language = getLanguage(); + if (language) { + device.language = language; + } + + return device; +} + +function getLanguage() { + const navigatorLanguage = navigator.language; + if (!navigatorLanguage) { + return; + } + + const languageCodeSegments = navigatorLanguage.split('-'); + if (!languageCodeSegments.length) { + return; + } + + return languageCodeSegments[0]; +} + +function hasContentUrl(ortb2) { + const site = ortb2.site; + const content = site && site.content; + return !!(content && content.url); +} + +function getWarnings(bidderRequest) { + const content = bidderRequest.ortb2.site.content; + const contentChain = 'ortb2.site.content.'; + const warnings = []; + if (!content.id) { + warnings.push(getMissingFieldMessage(contentChain + 'id')); + } + + if (!content.title) { + warnings.push(getMissingFieldMessage(contentChain + 'title')); + } + + if (!content.ext && !content.ext.description) { + warnings.push(getMissingFieldMessage(contentChain + 'ext.description')); + } + + return warnings; +} + +function getMissingFieldMessage(fieldName) { + return `Optional field ${fieldName} is not populated; we recommend populating for maximum performance.` } registerBidder(spec); From ef51ebf670a24eb49238a9c5f03867f277b12e0b Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Tue, 25 Jul 2023 21:31:27 -0400 Subject: [PATCH 33/48] updates tests --- modules/jwplayerBidAdapter.js | 5 +- test/spec/modules/jwplayerBidAdapter_spec.js | 77 +++++++++++++++----- 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index aa5a7b5a8a9..c9991c62da3 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -213,7 +213,7 @@ function buildBidFloorData(bidRequest) { } function buildRequestSite(bidRequest, bidderRequest) { - const site = bidderRequest.ortb2 || {}; + const site = bidderRequest.ortb2.site || {}; site.domain = site.domain || config.publisherDomain || window.location.hostname; site.page = site.page || config.pageUrl || window.location.href; @@ -280,10 +280,11 @@ function getWarnings(bidderRequest) { warnings.push(getMissingFieldMessage(contentChain + 'title')); } - if (!content.ext && !content.ext.description) { + if (!content.ext || !content.ext.description) { warnings.push(getMissingFieldMessage(contentChain + 'ext.description')); } + console.log('karimWarn: ', warnings); return warnings; } diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js index 71d59a3af01..fd4abaa29b8 100644 --- a/test/spec/modules/jwplayerBidAdapter_spec.js +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -5,15 +5,43 @@ import { config } from 'src/config.js'; describe('jwplayer bid adapter tests', function() { beforeEach(function() { this.defaultBidderRequest = { - 'gdprConsent': { - 'consentString': 'testConsentString', - 'gdprApplies': true + gdprConsent: { + consentString: 'testConsentString', + gdprApplies: true }, - 'uspConsent': 'testCCPA', - 'refererInfo': { - 'referer': 'https://example.com' + uspConsent: 'testCCPA', + refererInfo: { + referer: 'https://example.com' }, - timeout: 1000, + ortb2: { + site: { + domain: 'page.example.com', + page: 'https://examplepage.com', + content: { + url: 'media.mp4', + id: 'testMediaId', + title: 'testTile', + data: [{ + name: 'jwplayer.com', + segment: [{ + id: '00000000' + }, { + id: '88888888' + }, { + id: '80808080' + }], + ext: { + segtax: 502, + cids: ['testMediaId', 'externalTestId'], + } + }], + ext: { + description: 'testDescription' + } + } + } + }, + timeout: 1000 } }); @@ -61,6 +89,7 @@ describe('jwplayer bid adapter tests', function() { }, mediaTypes: { video: { + pos: 3, playerSize: [640, 480], context: 'instream', mimes: [ @@ -81,6 +110,7 @@ describe('jwplayer bid adapter tests', function() { api: [2], delivery: [2], playbackmethod: [1], + playbackend: 2 } }, schain: { @@ -98,18 +128,7 @@ describe('jwplayer bid adapter tests', function() { adUnitCode: 'testAdUnitCode', bidId: 'testBidId' } - ] - - let sandbox = sinon.sandbox.create(); - sandbox.stub(config, 'getConfig').callsFake((key) => { - const config = { - 'ortb2.site': { - domain: 'page.example.com', - page: 'https://examplepage.com' - } - }; - return config[key]; - }); + ]; const serverRequests = spec.buildRequests(bidRequests, this.defaultBidderRequest); @@ -124,9 +143,26 @@ describe('jwplayer bid adapter tests', function() { expect(openrtbRequest.site.domain).to.equal('page.example.com'); expect(openrtbRequest.site.page).to.equal('https://examplepage.com'); expect(openrtbRequest.site.ref).to.equal('https://example.com'); + expect(openrtbRequest.site.publisher.ext.jwplayer.publisherId).to.equal('testPublisherId'); expect(openrtbRequest.site.publisher.ext.jwplayer.siteId).to.equal('testSiteId'); + expect(openrtbRequest.site.content.url).to.equal('media.mp4'); + expect(openrtbRequest.site.content.id).to.equal('testMediaId'); + expect(openrtbRequest.site.content.title).to.equal('testTile'); + expect(openrtbRequest.site.content.ext.description).to.equal('testDescription'); + expect(openrtbRequest.site.content.data.length).to.equal(1); + const datum = openrtbRequest.site.content.data[0]; + expect(datum.name).to.equal('jwplayer.com'); + expect(datum.segment).to.deep.equal([{ + id: '00000000' + }, { + id: '88888888' + }, { + id: '80808080' + }]); + expect(datum.ext.segtax).to.equal(502); + expect(datum.ext.cids).to.deep.equal(['testMediaId', 'externalTestId']); expect(openrtbRequest.device.ua).to.equal(navigator.userAgent); expect(openrtbRequest.imp[0].id).to.equal('testAdUnitCode'); @@ -136,6 +172,7 @@ describe('jwplayer bid adapter tests', function() { expect(openrtbRequest.imp[0].video.startdelay).to.equal(0); expect(openrtbRequest.imp[0].video.placement).to.equal(1); expect(openrtbRequest.imp[0].video.plcmt).to.equal(1); + expect(openrtbRequest.imp[0].video.pos).to.equal(3); expect(openrtbRequest.imp[0].video.minduration).to.equal(3); expect(openrtbRequest.imp[0].video.maxduration).to.equal(60); expect(openrtbRequest.imp[0].video.skip).to.equal(1); @@ -144,6 +181,7 @@ describe('jwplayer bid adapter tests', function() { expect(openrtbRequest.imp[0].video.maxbitrate).to.equal(1000); expect(openrtbRequest.imp[0].video.delivery).to.deep.equal([2]); expect(openrtbRequest.imp[0].video.playbackmethod).to.deep.equal([1]); + expect(openrtbRequest.imp[0].video.playbackend).to.equal(2); expect(openrtbRequest.imp[0].video.linearity).to.equal(1); expect(openrtbRequest.imp[0].bidfloor).to.equal(10); @@ -171,7 +209,6 @@ describe('jwplayer bid adapter tests', function() { expect(openrtbRequest.tmax).to.equal(1000); }); - sandbox.restore(); }); }); From 9e1ccbc3687541779ec2a50a97eba90b9c55201c Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Wed, 26 Jul 2023 15:43:46 -0400 Subject: [PATCH 34/48] implements usersync --- modules/jwplayerBidAdapter.js | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index c9991c62da3..0295686180f 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -2,9 +2,13 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO } from '../src/mediaTypes.js'; import { isArray, isFn, deepAccess, deepSetValue, getDNT, logError, logWarn } from '../src/utils.js'; import { config } from '../src/config.js'; +import { hasPurpose1Consent } from '../src/utils/gpdr' const BIDDER_CODE = 'jwplayer'; -const URL = 'https://vpb-server.jwplayer.com/openrtb2/auction'; +/cookie_sync +const URL = 'https://vpb-server.jwplayer.com/openrtb2/'; +const AUCTION_URL = URL + 'auction'; +const USER_SYNC_URL = URL + 'setuid'; const GVLID = 1046; const SUPPORTED_AD_TYPES = [VIDEO]; @@ -67,19 +71,12 @@ export const spec = { return { method: 'POST', - url: URL, + url: AUCTION_URL, data: payload } }); }, - /** - * Unpack the response from the server into a list of bids. - * - * @param {*} serverResponse A successful response from the server. - * @param bidderRequest - * @return {Bid[]} An array of bids which were nested inside the server. - */ interpretResponse: function(serverResponse) { const bidResponses = []; const serverResponseBody = serverResponse.body; @@ -112,7 +109,18 @@ export const spec = { return bidResponses; }, - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) {}, + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { + if (!hasPurpose1Consent(gdprConsent) || !syncOptions.pixelEnabled) { + return []; + } + + const gdpr = gdprConsent.gdprApplies ? 1 : 0; + + return [{ + type: 'image', + url: `https://ib.adnxs.com/getuid?${USER_SYNC_URL}?bidder=jwplayer&uid=$UID&gdpr=${gdpr}&gdpr_consent=${gdprConsent.consentString}` + }]; + } // Optional? // onTimeout: function(timeoutData) {}, @@ -284,7 +292,6 @@ function getWarnings(bidderRequest) { warnings.push(getMissingFieldMessage(contentChain + 'ext.description')); } - console.log('karimWarn: ', warnings); return warnings; } From fd930713b598f5d1655c9587546affe5f11983f0 Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Wed, 26 Jul 2023 15:53:19 -0400 Subject: [PATCH 35/48] supports iframe sync --- modules/jwplayerBidAdapter.js | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 0295686180f..232c0fe032d 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -2,10 +2,9 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO } from '../src/mediaTypes.js'; import { isArray, isFn, deepAccess, deepSetValue, getDNT, logError, logWarn } from '../src/utils.js'; import { config } from '../src/config.js'; -import { hasPurpose1Consent } from '../src/utils/gpdr' +import { hasPurpose1Consent } from '../src/utils/gpdr.js'; const BIDDER_CODE = 'jwplayer'; -/cookie_sync const URL = 'https://vpb-server.jwplayer.com/openrtb2/'; const AUCTION_URL = URL + 'auction'; const USER_SYNC_URL = URL + 'setuid'; @@ -110,16 +109,29 @@ export const spec = { }, getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { - if (!hasPurpose1Consent(gdprConsent) || !syncOptions.pixelEnabled) { + if (!hasPurpose1Consent(gdprConsent)) { return []; } + const userSyncs = []; const gdpr = gdprConsent.gdprApplies ? 1 : 0; + const url = `https://ib.adnxs.com/getuid?${USER_SYNC_URL}?bidder=jwplayer&uid=$UID&gdpr=${gdpr}&gdpr_consent=${gdprConsent.consentString}`; - return [{ - type: 'image', - url: `https://ib.adnxs.com/getuid?${USER_SYNC_URL}?bidder=jwplayer&uid=$UID&gdpr=${gdpr}&gdpr_consent=${gdprConsent.consentString}` - }]; + if (syncOptions.iframeEnabled) { + userSyncs.push({ + type: 'iframe', + url + }); + }g + + if (syncOptions.pixelEnabled) { + userSyncs.push({ + type: 'image', + url + }); + } + + return userSyncs; } // Optional? From 32410de71f7f834255145d3574a117fbdae38792 Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Thu, 27 Jul 2023 23:27:41 -0400 Subject: [PATCH 36/48] registers user sync properly --- .../noadserver/jwplayer_test.html | 333 ++++++++++++++++++ modules/jwplayerBidAdapter.js | 32 +- 2 files changed, 358 insertions(+), 7 deletions(-) create mode 100644 integrationExamples/noadserver/jwplayer_test.html diff --git a/integrationExamples/noadserver/jwplayer_test.html b/integrationExamples/noadserver/jwplayer_test.html new file mode 100644 index 00000000000..f544b9b55c6 --- /dev/null +++ b/integrationExamples/noadserver/jwplayer_test.html @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 232c0fe032d..31368ad47d1 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -5,10 +5,9 @@ import { config } from '../src/config.js'; import { hasPurpose1Consent } from '../src/utils/gpdr.js'; const BIDDER_CODE = 'jwplayer'; -const URL = 'https://vpb-server.jwplayer.com/openrtb2/'; -const AUCTION_URL = URL + 'auction'; -const USER_SYNC_URL = URL + 'setuid'; - +const BASE_URL = 'https://vpb-server.jwplayer.com/'; +const AUCTION_URL = BASE_URL + 'openrtb2/auction'; +const USER_SYNC_URL = BASE_URL + 'setuid'; const GVLID = 1046; const SUPPORTED_AD_TYPES = [VIDEO]; @@ -114,15 +113,15 @@ export const spec = { } const userSyncs = []; - const gdpr = gdprConsent.gdprApplies ? 1 : 0; - const url = `https://ib.adnxs.com/getuid?${USER_SYNC_URL}?bidder=jwplayer&uid=$UID&gdpr=${gdpr}&gdpr_consent=${gdprConsent.consentString}`; + const consentQueryParams = getUserSyncConsentQueryParams(gdprConsent); + const url = `https://ib.adnxs.com/getuid?${USER_SYNC_URL}?bidder=jwplayer&uid=$UID&f=i` + consentQueryParams if (syncOptions.iframeEnabled) { userSyncs.push({ type: 'iframe', url }); - }g + } if (syncOptions.pixelEnabled) { userSyncs.push({ @@ -141,6 +140,25 @@ export const spec = { // onBidderError: function({ error, bidderRequest }) {} }; +function getUserSyncConsentQueryParams(gdprConsent) { + if (!gdprConsent) { + return ''; + } + + const consentString = gdprConsent.consentString; + if (!consentString) { + return ''; + } + + let gdpr = 0; + const gdprApplies = gdprConsent.gdprApplies; + if (typeof gdprApplies === 'boolean') { + gdpr = Number(gdprApplies) + } + + return `&gdpr=${gdpr}&gdpr_consent=${consentString}`; +} + function buildRequest(bidRequest, bidderRequest) { const openrtbRequest = { id: bidRequest.bidId, From 694c83b8d30d3fd9134223a5d51ee1191b74be47 Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Fri, 28 Jul 2023 17:02:31 -0400 Subject: [PATCH 37/48] tests user sync --- modules/jwplayerBidAdapter.js | 323 ++++++++++--------- test/spec/modules/jwplayerBidAdapter_spec.js | 111 ++++++- 2 files changed, 272 insertions(+), 162 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 31368ad47d1..fec667c5652 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -11,8 +11,6 @@ const USER_SYNC_URL = BASE_URL + 'setuid'; const GVLID = 1046; const SUPPORTED_AD_TYPES = [VIDEO]; -// Video Parameters -// https://docs.prebid.org/dev-docs/bidder-adaptor.html#step-2-accept-video-parameters-and-pass-them-to-your-server const VIDEO_ORTB_PARAMS = [ 'pos', 'w', @@ -35,21 +33,17 @@ const VIDEO_ORTB_PARAMS = [ 'linearity' ]; -export const spec = { - code: BIDDER_CODE, - gvlid: GVLID, - supportedMediaTypes: SUPPORTED_AD_TYPES, - - isBidRequestValid: function(bid) { +function getBidAdapter() { + function isBidRequestValid(bid) { const params = bid && bid.params; if (!params) { return false; } return !!params.placementId && !!params.publisherId && !!params.siteId; - }, + } - buildRequests: function(bidRequests, bidderRequest) { + function buildRequests(bidRequests, bidderRequest) { if (!bidRequests) { return; } @@ -62,7 +56,7 @@ export const spec = { const warnings = getWarnings(bidderRequest); warnings.forEach(warning => { logWarn(`${BIDDER_CODE}: ${warning}`); - }) + }); return bidRequests.map(bidRequest => { const payload = buildRequest(bidRequest, bidderRequest); @@ -73,9 +67,9 @@ export const spec = { data: payload } }); - }, + } - interpretResponse: function(serverResponse) { + function interpretResponse(serverResponse) { const bidResponses = []; const serverResponseBody = serverResponse.body; @@ -103,11 +97,12 @@ export const spec = { bidResponses.push(bidResponse); }); }); - }; + } + return bidResponses; - }, + } - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { + function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) { if (!hasPurpose1Consent(gdprConsent)) { return []; } @@ -133,200 +128,212 @@ export const spec = { return userSyncs; } + return { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: SUPPORTED_AD_TYPES, + isBidRequestValid, + buildRequests, + interpretResponse, + getUserSyncs + } + // Optional? // onTimeout: function(timeoutData) {}, // onBidWon: function(bid) {}, // onSetTargeting: function(bid) {}, // onBidderError: function({ error, bidderRequest }) {} -}; - -function getUserSyncConsentQueryParams(gdprConsent) { - if (!gdprConsent) { - return ''; - } - const consentString = gdprConsent.consentString; - if (!consentString) { - return ''; - } - - let gdpr = 0; - const gdprApplies = gdprConsent.gdprApplies; - if (typeof gdprApplies === 'boolean') { - gdpr = Number(gdprApplies) - } + function getUserSyncConsentQueryParams(gdprConsent) { + if (!gdprConsent) { + return ''; + } - return `&gdpr=${gdpr}&gdpr_consent=${consentString}`; -} + const consentString = gdprConsent.consentString; + if (!consentString) { + return ''; + } -function buildRequest(bidRequest, bidderRequest) { - const openrtbRequest = { - id: bidRequest.bidId, - imp: buildRequestImpression(bidRequest, bidderRequest), - site: buildRequestSite(bidRequest, bidderRequest), - device: buildRequestDevice() - }; - - // GDPR Consent Params - if (bidderRequest.gdprConsent) { - deepSetValue(openrtbRequest, 'user.ext.consent', bidderRequest.gdprConsent.consentString); - deepSetValue(openrtbRequest, 'regs.ext.gdpr', (bidderRequest.gdprConsent.gdprApplies ? 1 : 0)); - } + let gdpr = 0; + const gdprApplies = gdprConsent.gdprApplies; + if (typeof gdprApplies === 'boolean') { + gdpr = Number(gdprApplies) + } - // CCPA - if (bidderRequest.uspConsent) { - deepSetValue(openrtbRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); + return `&gdpr=${gdpr}&gdpr_consent=${consentString}`; } - if (bidRequest.schain) { - deepSetValue(openrtbRequest, 'source.schain', bidRequest.schain); - } + function buildRequest(bidRequest, bidderRequest) { + const openrtbRequest = { + id: bidRequest.bidId, + imp: getRequestImpressions(bidRequest, bidderRequest), + site: getRequestSite(bidRequest, bidderRequest), + device: getRequestDevice() + }; - openrtbRequest.tmax = bidderRequest.timeout || 200; + // GDPR Consent Params + if (bidderRequest.gdprConsent) { + deepSetValue(openrtbRequest, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + deepSetValue(openrtbRequest, 'regs.ext.gdpr', (bidderRequest.gdprConsent.gdprApplies ? 1 : 0)); + } - return JSON.stringify(openrtbRequest); -} + // CCPA + if (bidderRequest.uspConsent) { + deepSetValue(openrtbRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); + } -function buildRequestImpression(bidRequest) { - const impressionObject = { - id: bidRequest.adUnitCode, - }; + if (bidRequest.schain) { + deepSetValue(openrtbRequest, 'source.schain', bidRequest.schain); + } - impressionObject.video = buildImpressionVideo(bidRequest); + openrtbRequest.tmax = bidderRequest.timeout || 200; - const bidFloorData = buildBidFloorData(bidRequest); - if (bidFloorData) { - impressionObject.bidfloor = bidFloorData.floor; - impressionObject.bidfloorcur = bidFloorData.currency; + return JSON.stringify(openrtbRequest); } - impressionObject.ext = buildImpressionExtension(bidRequest); + function getRequestImpressions(bidRequest) { + const impressionObject = { + id: bidRequest.adUnitCode, + }; - return [impressionObject]; -} + impressionObject.video = getImpressionVideo(bidRequest); -function buildImpressionVideo(bidRequest) { - const videoParams = deepAccess(bidRequest, 'mediaTypes.video', {}); + const bidFloorData = getBidFloorData(bidRequest); + if (bidFloorData) { + impressionObject.bidfloor = bidFloorData.floor; + impressionObject.bidfloorcur = bidFloorData.currency; + } - const video = {}; + impressionObject.ext = getImpressionExtension(bidRequest); - VIDEO_ORTB_PARAMS.forEach((param) => { - if (videoParams.hasOwnProperty(param)) { - video[param] = videoParams[param]; - } - }); + return [impressionObject]; + } - return video; -} + function getImpressionVideo(bidRequest) { + const videoParams = deepAccess(bidRequest, 'mediaTypes.video', {}); -function buildImpressionExtension(bidRequest) { - return { - prebid: { - bidder: { - jwplayer: { - placementId: bidRequest.params.placementId - } + const video = {}; + + VIDEO_ORTB_PARAMS.forEach((param) => { + if (videoParams.hasOwnProperty(param)) { + video[param] = videoParams[param]; } - } - }; -} + }); -function buildBidFloorData(bidRequest) { - const { params } = bidRequest; - const currency = params.currency || 'USD'; + return video; + } - let floorData; - if (isFn(bidRequest.getFloor)) { - const bidFloorRequest = { - currency: currency, - mediaType: VIDEO, - size: '*' + function getImpressionExtension(bidRequest) { + return { + prebid: { + bidder: { + jwplayer: { + placementId: bidRequest.params.placementId + } + } + } }; - floorData = bidRequest.getFloor(bidFloorRequest); - } else if (params.bidFloor) { - floorData = { floor: params.bidFloor, currency: currency }; } - return floorData; -} + function getBidFloorData(bidRequest) { + const { params } = bidRequest; + const currency = params.currency || 'USD'; + + let floorData; + if (isFn(bidRequest.getFloor)) { + const bidFloorRequest = { + currency: currency, + mediaType: VIDEO, + size: '*' + }; + floorData = bidRequest.getFloor(bidFloorRequest); + } else if (params.bidFloor) { + floorData = { floor: params.bidFloor, currency: currency }; + } -function buildRequestSite(bidRequest, bidderRequest) { - const site = bidderRequest.ortb2.site || {}; + return floorData; + } - site.domain = site.domain || config.publisherDomain || window.location.hostname; - site.page = site.page || config.pageUrl || window.location.href; + function getRequestSite(bidRequest, bidderRequest) { + const site = bidderRequest.ortb2.site || {}; - const referer = bidderRequest.refererInfo && bidderRequest.refererInfo.referer; - if (!site.ref && referer) { - site.ref = referer; - } + site.domain = site.domain || config.publisherDomain || window.location.hostname; + site.page = site.page || config.pageUrl || window.location.href; - const jwplayerPublisherExtChain = 'publisher.ext.jwplayer.'; + const referer = bidderRequest.refererInfo && bidderRequest.refererInfo.referer; + if (!site.ref && referer) { + site.ref = referer; + } - deepSetValue(site, jwplayerPublisherExtChain + 'publisherId', bidRequest.params.publisherId); - deepSetValue(site, jwplayerPublisherExtChain + 'siteId', bidRequest.params.siteId); + const jwplayerPublisherExtChain = 'publisher.ext.jwplayer.'; - return site; -} + deepSetValue(site, jwplayerPublisherExtChain + 'publisherId', bidRequest.params.publisherId); + deepSetValue(site, jwplayerPublisherExtChain + 'siteId', bidRequest.params.siteId); -function buildRequestDevice() { - const device = { - h: screen.height, - w: screen.width, - ua: navigator.userAgent, - dnt: getDNT() ? 1 : 0, - js: 1 - }; - - const language = getLanguage(); - if (language) { - device.language = language; + return site; } - return device; -} + function getRequestDevice() { + const device = { + h: screen.height, + w: screen.width, + ua: navigator.userAgent, + dnt: getDNT() ? 1 : 0, + js: 1 + }; -function getLanguage() { - const navigatorLanguage = navigator.language; - if (!navigatorLanguage) { - return; - } + const language = getLanguage(); + if (language) { + device.language = language; + } - const languageCodeSegments = navigatorLanguage.split('-'); - if (!languageCodeSegments.length) { - return; + return device; } - return languageCodeSegments[0]; -} + function getLanguage() { + const navigatorLanguage = navigator.language; + if (!navigatorLanguage) { + return; + } -function hasContentUrl(ortb2) { - const site = ortb2.site; - const content = site && site.content; - return !!(content && content.url); -} + const languageCodeSegments = navigatorLanguage.split('-'); + if (!languageCodeSegments.length) { + return; + } -function getWarnings(bidderRequest) { - const content = bidderRequest.ortb2.site.content; - const contentChain = 'ortb2.site.content.'; - const warnings = []; - if (!content.id) { - warnings.push(getMissingFieldMessage(contentChain + 'id')); + return languageCodeSegments[0]; } - if (!content.title) { - warnings.push(getMissingFieldMessage(contentChain + 'title')); + function hasContentUrl(ortb2) { + const site = ortb2.site; + const content = site && site.content; + return !!(content && content.url); } - if (!content.ext || !content.ext.description) { - warnings.push(getMissingFieldMessage(contentChain + 'ext.description')); + function getWarnings(bidderRequest) { + const content = bidderRequest.ortb2.site.content; + const contentChain = 'ortb2.site.content.'; + const warnings = []; + if (!content.id) { + warnings.push(getMissingFieldMessage(contentChain + 'id')); + } + + if (!content.title) { + warnings.push(getMissingFieldMessage(contentChain + 'title')); + } + + if (!content.ext || !content.ext.description) { + warnings.push(getMissingFieldMessage(contentChain + 'ext.description')); + } + + return warnings; } - return warnings; + function getMissingFieldMessage(fieldName) { + return `Optional field ${fieldName} is not populated; we recommend populating for maximum performance.` + } } -function getMissingFieldMessage(fieldName) { - return `Optional field ${fieldName} is not populated; we recommend populating for maximum performance.` -} +export const spec = getBidAdapter(); registerBidder(spec); diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js index fd4abaa29b8..b46b69f9e90 100644 --- a/test/spec/modules/jwplayerBidAdapter_spec.js +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -75,7 +75,7 @@ describe('jwplayer bid adapter tests', function() { }); }); - describe('buildRequests for video', function() { + describe('buildRequests', function() { it('should include proper ortb params in requests', function() { const bidRequests = [ { @@ -208,11 +208,10 @@ describe('jwplayer bid adapter tests', function() { expect(openrtbRequest.tmax).to.equal(1000); }); - }); }); - describe('interpretResponse for video', function() { + describe('interpretResponse', function() { const bidResponse = { id: 'testId', impid: 'test-imp-id', @@ -256,5 +255,109 @@ describe('jwplayer bid adapter tests', function() { expect(bidResponses[0].meta.advertiserDomains[0]).to.equal('prebid.com'); }); - describe('user sync handler', function() {}); + describe('getUserSyncs', function() { + const consentString = 'test_consent_string'; + const baseGdprConsent = { + gdprApplies: true, + vendorData: { + purpose: { + consents: { + 1: true + } + } + } + }; + + const expectedBaseUrl = 'https://ib.adnxs.com/getuid?https://vpb-server.jwplayer.com/setuid?bidder=jwplayer&uid=$UID&f=i'; + + it('should return empty when Purpose 1 consent is not granted', function() { + expect(spec.getUserSyncs({}, {})).to.be.empty; + expect(spec.getUserSyncs({}, {}, {})).to.be.empty; + expect(spec.getUserSyncs({}, {}, { gdprApplies: false })).to.be.empty; + expect(spec.getUserSyncs({}, {}, { + gdprApplies: true, + vendorData: { + purpose: { + consents: { + 1: false + } + } + } + })).to.be.empty; + }); + + it('should return iframe when enabled', function () { + const userSyncs = spec.getUserSyncs({ iframeEnabled: true }, {}, baseGdprConsent); + expect(userSyncs.length).to.equal(1); + const sync = userSyncs[0]; + expect(sync.type).to.equal('iframe'); + expect(sync.url).to.equal(expectedBaseUrl); + }); + + it('should return image when enabled', function () { + const userSyncs = spec.getUserSyncs({ pixelEnabled: true }, {}, baseGdprConsent); + expect(userSyncs.length).to.equal(1); + const sync = userSyncs[0]; + expect(sync.type).to.equal('image'); + expect(sync.url).to.equal(expectedBaseUrl); + }); + + it('should return both iframe and image when enabled', function () { + const userSyncs = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, {}, baseGdprConsent); + expect(userSyncs.length).to.equal(2); + + const iframeSync = userSyncs[0]; + expect(iframeSync.type).to.equal('iframe'); + expect(iframeSync.url).to.equal(expectedBaseUrl); + + const imageSync = userSyncs[1]; + expect(imageSync.type).to.equal('image'); + expect(imageSync.url).to.equal(expectedBaseUrl); + }); + + it('should include gdpr consent query params in sync redirect url', function () { + const expectedUrl = expectedBaseUrl + '&gdpr=1&gdpr_consent=' + consentString; + const gdprConsent = Object.assign({ }, baseGdprConsent, { consentString }); + const userSyncs = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, {}, gdprConsent); + expect(userSyncs.length).to.equal(2); + + const iframeSync = userSyncs[0]; + expect(iframeSync.type).to.equal('iframe'); + expect(iframeSync.url).to.equal(expectedUrl); + + const imageSync = userSyncs[1]; + expect(imageSync.type).to.equal('image'); + expect(imageSync.url).to.equal(expectedUrl); + }); + + it('should include gdpr 0 in consent query params when gdprApplies is false', function () { + const expectedUrl = expectedBaseUrl + '&gdpr=0&gdpr_consent=' + consentString; + const gdprConsent = Object.assign({ }, baseGdprConsent, { consentString, gdprApplies: false }); + const userSyncs = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, {}, gdprConsent); + expect(userSyncs.length).to.equal(2); + + const iframeSync = userSyncs[0]; + expect(iframeSync.type).to.equal('iframe'); + expect(iframeSync.url).to.equal(expectedUrl); + + const imageSync = userSyncs[1]; + expect(imageSync.type).to.equal('image'); + expect(imageSync.url).to.equal(expectedUrl); + }); + + it('should include gdpr 0 in consent query params when gdprApplies is not a bool', function () { + const expectedUrl = expectedBaseUrl + '&gdpr=0&gdpr_consent=' + consentString; + const gdprConsent = Object.assign({ }, baseGdprConsent, { consentString, gdprApplies: 1 }); + const userSyncs = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, {}, gdprConsent); + expect(userSyncs.length).to.equal(2); + + const iframeSync = userSyncs[0]; + expect(iframeSync.type).to.equal('iframe'); + expect(iframeSync.url).to.equal(expectedUrl); + + const imageSync = userSyncs[1]; + expect(imageSync.type).to.equal('image'); + expect(imageSync.url).to.equal(expectedUrl); + }); + }); }); From 8ac50d97e05254ce95abd80a7a50826c11068eb8 Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Fri, 28 Jul 2023 17:41:18 -0400 Subject: [PATCH 38/48] uses boost email --- modules/jwplayerBidAdapter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jwplayerBidAdapter.md b/modules/jwplayerBidAdapter.md index e9be2c70885..6fe0e4ae584 100644 --- a/modules/jwplayerBidAdapter.md +++ b/modules/jwplayerBidAdapter.md @@ -3,7 +3,7 @@ ``` Module Name: JWPlayer Bid Adapter Module Type: Bidder Adapter -Maintainer: jrocha@jwplayer.com +Maintainer: boost-engineering@jwplayer.com ``` # Description From 86414d38365394a2f470508c16c71ccc856f36ac Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Wed, 3 Apr 2024 23:17:10 -0300 Subject: [PATCH 39/48] improves ortb reqs --- modules/jwplayerBidAdapter.js | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index fec667c5652..2ff3041971f 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -168,7 +168,8 @@ function getBidAdapter() { id: bidRequest.bidId, imp: getRequestImpressions(bidRequest, bidderRequest), site: getRequestSite(bidRequest, bidderRequest), - device: getRequestDevice() + device: getRequestDevice(bidderRequest.ortb2), + user: getRequestUser(bidderRequest.ortb2), }; // GDPR Consent Params @@ -220,6 +221,10 @@ function getBidAdapter() { } }); + if (!videoParams.plcmt) { + logWarn(`${BIDDER_CODE}: Please set a value to mediaTypes.video.plcmt`); + } + return video; } @@ -273,17 +278,17 @@ function getBidAdapter() { return site; } - function getRequestDevice() { - const device = { + function getRequestDevice(ortb2) { + const device = Object.assign({ h: screen.height, w: screen.width, ua: navigator.userAgent, dnt: getDNT() ? 1 : 0, js: 1 - }; + }, ortb2.device || {}) const language = getLanguage(); - if (language) { + if (!device.language && language) { device.language = language; } @@ -304,6 +309,15 @@ function getBidAdapter() { return languageCodeSegments[0]; } + function getRequestUser(ortb2) { + const user = ortb2.user || {}; + if (config.getConfig('coppa') === true) { + user.coppa = true; + } + + return user; + } + function hasContentUrl(ortb2) { const site = ortb2.site; const content = site && site.content; From be73c65173eeeaf8eae5c4ead99c89da1e2c8c73 Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Thu, 4 Apr 2024 18:56:59 -0300 Subject: [PATCH 40/48] interprets response --- modules/jwplayerBidAdapter.js | 79 ++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 29 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 2ff3041971f..0794195aaa3 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -69,35 +69,43 @@ function getBidAdapter() { }); } - function interpretResponse(serverResponse) { + function interpretResponse(serverResponse, bidRequest) { const bidResponses = []; const serverResponseBody = serverResponse.body; - const bidId = serverResponse.bidid; + logResponseWarnings(serverResponseBody); + + const seatBids = serverResponseBody && serverResponseBody.seatbid; + if (!isArray(seatBids)) { + return bidResponses; + } + const cur = serverResponse.cur; - if (serverResponseBody && isArray(serverResponseBody.seatbid)) { - serverResponseBody.seatbid.forEach(seatBids => { - seatBids.bid.forEach(bid => { - const bidResponse = { - requestId: bidId, - cpm: bid.price, - currency: cur, - width: bid.w, - height: bid.h, - creativeId: bid.adid, - vastXml: bid.adm, - netRevenue: true, - ttl: 500, - ad: bid.adm, - meta: { - advertiserDomains: bid.adomain - } - }; - bidResponses.push(bidResponse); - }); + seatBids.forEach(seatBid => { + seatBid.bid.forEach(bid => { + const bidResponse = { + requestId: serverResponse.id, + cpm: bid.price, + currency: cur, + width: bid.w, + height: bid.h, + ad: bid.adm, + vastXml: bid.adm, + ttl: bid.ttl || 3600, + netRevenue: false, + creativeId: bid.adid, + dealId: bid.dealid, + meta: { + advertiserDomains: bid.adomain, + mediaType: VIDEO, + primaryCatId: bid.cat, + } + }; + + bidResponses.push(bidResponse); }); - } + }); return bidResponses; } @@ -138,12 +146,6 @@ function getBidAdapter() { getUserSyncs } - // Optional? - // onTimeout: function(timeoutData) {}, - // onBidWon: function(bid) {}, - // onSetTargeting: function(bid) {}, - // onBidderError: function({ error, bidderRequest }) {} - function getUserSyncConsentQueryParams(gdprConsent) { if (!gdprConsent) { return ''; @@ -346,6 +348,25 @@ function getBidAdapter() { function getMissingFieldMessage(fieldName) { return `Optional field ${fieldName} is not populated; we recommend populating for maximum performance.` } + + function logResponseWarnings(serverResponseBody) { + const warningPayload = deepAccess(serverResponseBody, 'ext.warnings'); + if (!warningPayload) { + return; + } + + const warningCategories = Object.keys(warningPayload); + warningCategories.forEach(category => { + const warnings = warningPayload[category]; + if (!isArray(warnings)) { + return; + } + + warnings.forEach(warning => { + logWarn(`${BIDDER_CODE}: [Bid Response][Warning Code: ${warning.code}] ${warning.message}`); + }); + }); + } } export const spec = getBidAdapter(); From 96f4527777add05041b1797b6435baa27fad7751 Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Fri, 5 Apr 2024 16:27:02 -0300 Subject: [PATCH 41/48] updates tests --- modules/jwplayerBidAdapter.js | 14 +- test/spec/modules/jwplayerBidAdapter_spec.js | 144 ++++++++++++------- 2 files changed, 97 insertions(+), 61 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 0794195aaa3..9f22012ff7f 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -69,23 +69,23 @@ function getBidAdapter() { }); } - function interpretResponse(serverResponse, bidRequest) { - const bidResponses = []; + function interpretResponse(serverResponse) { + const outgoingBidResponses = []; const serverResponseBody = serverResponse.body; logResponseWarnings(serverResponseBody); const seatBids = serverResponseBody && serverResponseBody.seatbid; if (!isArray(seatBids)) { - return bidResponses; + return outgoingBidResponses; } - const cur = serverResponse.cur; + const cur = serverResponseBody.cur; seatBids.forEach(seatBid => { seatBid.bid.forEach(bid => { const bidResponse = { - requestId: serverResponse.id, + requestId: serverResponseBody.id, cpm: bid.price, currency: cur, width: bid.w, @@ -103,11 +103,11 @@ function getBidAdapter() { } }; - bidResponses.push(bidResponse); + outgoingBidResponses.push(bidResponse); }); }); - return bidResponses; + return outgoingBidResponses; } function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) { diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js index b46b69f9e90..f969d657006 100644 --- a/test/spec/modules/jwplayerBidAdapter_spec.js +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -1,8 +1,7 @@ import { expect, assert } from 'chai'; import { spec } from 'modules/jwplayerBidAdapter.js'; -import { config } from 'src/config.js'; -describe('jwplayer bid adapter tests', function() { +describe('jwplayerBidAdapter', function() { beforeEach(function() { this.defaultBidderRequest = { gdprConsent: { @@ -45,39 +44,74 @@ describe('jwplayer bid adapter tests', function() { } }); + it('should use jwplayer bidder code', function () { + expect(spec.code).to.equal('jwplayer'); + }); + + it('should use jwplayer GVLID code', function () { + expect(spec.gvlid).to.equal(1046); + }); + + it('should support VIDEO media type only', function () { + expect(spec.supportedMediaTypes).to.have.length(1); + expect(spec.supportedMediaTypes[0]).to.equal('video'); + }); + describe('isBidRequestValid', function() { - it('passes when the bid includes a placement ID, a publisher ID and a site ID', function() { - assert(spec.isBidRequestValid({params: {placementId: 'foo', publisherId: 'bar', siteId: 'siteId '}}) === true); + it('should be invalid when bidRequest is undefined', function() { + assert(spec.isBidRequestValid() === false); + }); + + it('should be invalid when bidRequest is null', function() { + assert(spec.isBidRequestValid(null) === false); + }); + + it('should be invalid when the bidRequest has no params', function() { + assert(spec.isBidRequestValid({}) === false); }); - it('fails when the bid request only includes a publisher ID', function() { + it('should be invalid when the bid request only includes a publisher ID', function() { assert(spec.isBidRequestValid({params: {publisherId: 'foo'}}) === false); }); - it('fails when the bid request only includes a placement ID', function() { + it('should be invalid when the bid request only includes a placement ID', function() { assert(spec.isBidRequestValid({params: {placementId: 'foo'}}) === false); }); - it('fails when the bid request only includes a site ID', function() { + it('should be invalid when the bid request only includes a site ID', function() { assert(spec.isBidRequestValid({params: {siteId: 'foo'}}) === false); }); - it('fails when bid is undefined', function() { - assert(spec.isBidRequestValid() === false); + it('should be valid when the bid includes a placement ID, a publisher ID and a site ID', function() { + assert(spec.isBidRequestValid({params: {placementId: 'foo', publisherId: 'bar', siteId: 'siteId '}}) === true); }); + }); - it('fails when bid is null', function() { - assert(spec.isBidRequestValid(null) === false); + describe('buildRequests', function() { + it('should return undefined when bidRequests is undefined', function () { + expect(spec.buildRequests()).to.be.undefined; }); - it('fails when the bid has no params', function() { - assert(spec.isBidRequestValid({}) === false); + it('should return undefined when bidRequests is null', function () { + expect(spec.buildRequests(null)).to.be.undefined; }); - }); - describe('buildRequests', function() { - it('should include proper ortb params in requests', function() { - const bidRequests = [ + it('should return undefined when ortb site.content.url is absent', function () { + const request = spec.buildRequests({}, { + ortb2: { + site: { + content: { + url: null, + } + } + } + }); + + expect(request).to.be.undefined; + }); + + it('should build a valid request when bid request is complete', function() { + const incomingBidRequests = [ { bidder: 'jwplayer', params: { @@ -130,9 +164,9 @@ describe('jwplayer bid adapter tests', function() { } ]; - const serverRequests = spec.buildRequests(bidRequests, this.defaultBidderRequest); + const outgoingBidRequests = spec.buildRequests(incomingBidRequests, this.defaultBidderRequest); - serverRequests.forEach(serverRequest => { + outgoingBidRequests.forEach(serverRequest => { expect(serverRequest.url).to.equal('https://vpb-server.jwplayer.com/openrtb2/auction'); expect(serverRequest.method).to.equal('POST'); @@ -212,47 +246,49 @@ describe('jwplayer bid adapter tests', function() { }); describe('interpretResponse', function() { - const bidResponse = { - id: 'testId', - impid: 'test-imp-id', - price: 1.000000, - adid: '97517771', - adm: 'some-test-ad', - adomain: ['prebid.com'], - w: 1, - h: 1, - } - const serverResponse = { body: { id: 'test-request-id', - seatbid: [ - { - bid: [ bidResponse ], - seat: 1000 - } - ] - }, - bidid: '123456789', - cur: 'USD' - } + cur: 'USD', + seatbid: [{ + bid: [{ + id: 'testId', + impid: 'test-imp-id', + price: 5.000, + adid: 'test-creative-id', + adm: 'test-ad-xml', + adomain: ['prebid.com'], + cat: ['test-iab-category'], + w: 200, + h: 150, + }], + seat: 1000 + }] + } + }; const bidResponses = spec.interpretResponse(serverResponse); - expect(bidResponses[0]).to.not.equal(null); - expect(bidResponses[0].requestId).to.equal('123456789'); - expect(bidResponses[0].cpm).to.equal(1); - expect(bidResponses[0].currency).to.equal('USD'); - expect(bidResponses[0].width).to.equal(1); - expect(bidResponses[0].height).to.equal(1); - expect(bidResponses[0].creativeId).to.equal('97517771'); - expect(bidResponses[0].vastXml).to.equal('some-test-ad'); - expect(bidResponses[0].netRevenue).to.equal(true); - expect(bidResponses[0].ttl).to.equal(500); - expect(bidResponses[0].ad).to.equal('some-test-ad'); - expect(bidResponses[0].meta).to.not.equal(null); - expect(bidResponses[0].meta.advertiserDomains).to.not.equal(null); - expect(bidResponses[0].meta.advertiserDomains[0]).to.equal('prebid.com'); + expect(bidResponses).to.have.length(1); + const bid = bidResponses[0]; + expect(bid.requestId).to.equal('test-request-id'); + expect(bid.cpm).to.equal(5); + expect(bid.currency).to.equal('USD'); + expect(bid.width).to.equal(200); + expect(bid.height).to.equal(150); + expect(bid.creativeId).to.equal('test-creative-id'); + expect(bid.vastXml).to.equal('test-ad-xml'); + expect(bid.netRevenue).to.equal(false); + expect(bid.ttl).to.equal(3600); + expect(bid.ad).to.equal('test-ad-xml'); + + expect(bid.meta).to.not.be.undefined; + + expect(bid.meta.advertiserDomains).to.have.length(1); + expect(bid.meta.advertiserDomains[0]).to.equal('prebid.com'); + + expect(bid.meta.primaryCatId).to.have.length(1); + expect(bid.meta.primaryCatId[0]).to.equal('test-iab-category'); }); describe('getUserSyncs', function() { From 1551e885bc0c9551160a4a1b16639920c835601b Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Wed, 10 Apr 2024 18:31:01 -0300 Subject: [PATCH 42/48] updates tests --- modules/jwplayerBidAdapter.js | 38 +++++++++++ test/spec/modules/jwplayerBidAdapter_spec.js | 72 +++++++++++++------- 2 files changed, 86 insertions(+), 24 deletions(-) diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 9f22012ff7f..151d08bf3a6 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -223,6 +223,8 @@ function getBidAdapter() { } }); + setPlayerSize(video, videoParams); + if (!videoParams.plcmt) { logWarn(`${BIDDER_CODE}: Please set a value to mediaTypes.video.plcmt`); } @@ -242,6 +244,42 @@ function getBidAdapter() { }; } + function setPlayerSize(videoImp, videoParams) { + if (videoImp.w !== undefined && videoImp.h !== undefined) { + return; + } + + const playerSize = getNormalizedPlayerSize(videoParams.playerSize); + if (!playerSize.length) { + logWarn(logWarn(`${BIDDER_CODE}: Video size has not been set. Please set values in video.h and video.w`)); + return; + } + + if (videoImp.w === undefined) { + videoImp.w = playerSize[0]; + } + + if (videoImp.h === undefined) { + videoImp.h = playerSize[1]; + } + } + + function getNormalizedPlayerSize(playerSize) { + if (!Array.isArray(playerSize)) { + return []; + } + + if (Array.isArray(playerSize[0])) { + playerSize = playerSize[0]; + } + + if (playerSize.length < 2) { + return []; + } + + return playerSize; + } + function getBidFloorData(bidRequest) { const { params } = bidRequest; const currency = params.currency || 'USD'; diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js index f969d657006..5a517d0ac75 100644 --- a/test/spec/modules/jwplayerBidAdapter_spec.js +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -174,6 +174,32 @@ describe('jwplayerBidAdapter', function() { expect(openrtbRequest.id).to.equal('testBidId'); + expect(openrtbRequest.imp[0].id).to.equal('testAdUnitCode'); + expect(openrtbRequest.imp[0].video.w).to.equal(640); + expect(openrtbRequest.imp[0].video.h).to.equal(480); + expect(openrtbRequest.imp[0].video.mimes).to.deep.equal(['video/mp4', 'application/javascript']); + expect(openrtbRequest.imp[0].video.protocols).to.deep.equal([2, 3, 5, 6]); + expect(openrtbRequest.imp[0].video.api).to.deep.equal([2]); + expect(openrtbRequest.imp[0].video.startdelay).to.equal(0); + expect(openrtbRequest.imp[0].video.placement).to.equal(1); + expect(openrtbRequest.imp[0].video.plcmt).to.equal(1); + expect(openrtbRequest.imp[0].video.pos).to.equal(3); + expect(openrtbRequest.imp[0].video.minduration).to.equal(3); + expect(openrtbRequest.imp[0].video.maxduration).to.equal(60); + expect(openrtbRequest.imp[0].video.skip).to.equal(1); + expect(openrtbRequest.imp[0].video.skipafter).to.equal(4); + expect(openrtbRequest.imp[0].video.minbitrate).to.equal(500); + expect(openrtbRequest.imp[0].video.maxbitrate).to.equal(1000); + expect(openrtbRequest.imp[0].video.delivery).to.deep.equal([2]); + expect(openrtbRequest.imp[0].video.playbackmethod).to.deep.equal([1]); + expect(openrtbRequest.imp[0].video.playbackend).to.equal(2); + expect(openrtbRequest.imp[0].video.linearity).to.equal(1); + + expect(openrtbRequest.imp[0].bidfloor).to.equal(10); + expect(openrtbRequest.imp[0].bidfloorcur).to.equal('EUR'); + + expect(openrtbRequest.imp[0].ext.prebid.bidder.jwplayer.placementId).to.equal('testPlacementId'); + expect(openrtbRequest.site.domain).to.equal('page.example.com'); expect(openrtbRequest.site.page).to.equal('https://examplepage.com'); expect(openrtbRequest.site.ref).to.equal('https://example.com'); @@ -197,31 +223,13 @@ describe('jwplayerBidAdapter', function() { }]); expect(datum.ext.segtax).to.equal(502); expect(datum.ext.cids).to.deep.equal(['testMediaId', 'externalTestId']); - expect(openrtbRequest.device.ua).to.equal(navigator.userAgent); - expect(openrtbRequest.imp[0].id).to.equal('testAdUnitCode'); - expect(openrtbRequest.imp[0].video.mimes).to.deep.equal(['video/mp4', 'application/javascript']); - expect(openrtbRequest.imp[0].video.protocols).to.deep.equal([2, 3, 5, 6]); - expect(openrtbRequest.imp[0].video.api).to.deep.equal([2]); - expect(openrtbRequest.imp[0].video.startdelay).to.equal(0); - expect(openrtbRequest.imp[0].video.placement).to.equal(1); - expect(openrtbRequest.imp[0].video.plcmt).to.equal(1); - expect(openrtbRequest.imp[0].video.pos).to.equal(3); - expect(openrtbRequest.imp[0].video.minduration).to.equal(3); - expect(openrtbRequest.imp[0].video.maxduration).to.equal(60); - expect(openrtbRequest.imp[0].video.skip).to.equal(1); - expect(openrtbRequest.imp[0].video.skipafter).to.equal(4); - expect(openrtbRequest.imp[0].video.minbitrate).to.equal(500); - expect(openrtbRequest.imp[0].video.maxbitrate).to.equal(1000); - expect(openrtbRequest.imp[0].video.delivery).to.deep.equal([2]); - expect(openrtbRequest.imp[0].video.playbackmethod).to.deep.equal([1]); - expect(openrtbRequest.imp[0].video.playbackend).to.equal(2); - expect(openrtbRequest.imp[0].video.linearity).to.equal(1); - - expect(openrtbRequest.imp[0].bidfloor).to.equal(10); - expect(openrtbRequest.imp[0].bidfloorcur).to.equal('EUR'); - - expect(openrtbRequest.imp[0].ext.prebid.bidder.jwplayer.placementId).to.equal('testPlacementId'); + expect(openrtbRequest.device.ua).to.equal(navigator.userAgent); + expect(openrtbRequest.device.w).to.not.be.undefined; + expect(openrtbRequest.device.h).to.not.be.undefined; + expect(openrtbRequest.device.dnt).to.not.be.undefined; + expect(openrtbRequest.device.js).to.equal(1); + expect(openrtbRequest.device.language).to.not.be.undefined; expect(openrtbRequest.user.ext.consent).to.equal('testConsentString'); @@ -242,6 +250,18 @@ describe('jwplayerBidAdapter', function() { expect(openrtbRequest.tmax).to.equal(1000); }); + + it('should set w and h from playerSize', function () { + + }); + + it('should normalize playerSize nested array', function () { + + }); + + it('should set user coppa to true when configured', function () { + + }); }); }); @@ -261,6 +281,7 @@ describe('jwplayerBidAdapter', function() { cat: ['test-iab-category'], w: 200, h: 150, + dealid: 'test-deal-id' }], seat: 1000 }] @@ -281,12 +302,15 @@ describe('jwplayerBidAdapter', function() { expect(bid.netRevenue).to.equal(false); expect(bid.ttl).to.equal(3600); expect(bid.ad).to.equal('test-ad-xml'); + expect(bid.dealId).to.equal('test-deal-id'); expect(bid.meta).to.not.be.undefined; expect(bid.meta.advertiserDomains).to.have.length(1); expect(bid.meta.advertiserDomains[0]).to.equal('prebid.com'); + expect(bid.meta.mediaType).to.equal('video'); + expect(bid.meta.primaryCatId).to.have.length(1); expect(bid.meta.primaryCatId[0]).to.equal('test-iab-category'); }); From 92430c90c7829a0668f159ac8cee78d21645248d Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Wed, 10 Apr 2024 18:34:40 -0300 Subject: [PATCH 43/48] removes playerSize official support --- test/spec/modules/jwplayerBidAdapter_spec.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js index 5a517d0ac75..e19790a9670 100644 --- a/test/spec/modules/jwplayerBidAdapter_spec.js +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -124,7 +124,8 @@ describe('jwplayerBidAdapter', function() { mediaTypes: { video: { pos: 3, - playerSize: [640, 480], + w: 640, + h: 480, context: 'instream', mimes: [ 'video/mp4', @@ -250,18 +251,6 @@ describe('jwplayerBidAdapter', function() { expect(openrtbRequest.tmax).to.equal(1000); }); - - it('should set w and h from playerSize', function () { - - }); - - it('should normalize playerSize nested array', function () { - - }); - - it('should set user coppa to true when configured', function () { - - }); }); }); From 432702f6ddb8739d9e51ea6d8335f14982ed363a Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Fri, 12 Apr 2024 18:27:01 -0300 Subject: [PATCH 44/48] adds md --- modules/jwplayerBidAdapter.md | 57 ++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/modules/jwplayerBidAdapter.md b/modules/jwplayerBidAdapter.md index 6fe0e4ae584..620f8657e50 100644 --- a/modules/jwplayerBidAdapter.md +++ b/modules/jwplayerBidAdapter.md @@ -12,6 +12,61 @@ Connects to JWPlayer's demand sources. JWPlayer bid adapter supports Video (instream and outstream). -# Test Parameters +# Sample Ad Unit + +```markdown +const adUnit = { + code: 'test-ad-unit', + mediaTypes: { + video: { + pos: 0, + w: 640, + h: 480, + mimes : ['video/x-ms-wmv', 'video/mp4'], + minduration : 0, + maxduration: 60, + protocols : [2,3,7,5,6,8], + startdelay: 0, + placement: 1, + plcmt: 1, + skip: 1, + skipafter: 10, + playbackmethod: [3], + api: [2], + linearity: 1 + } + }, + bids: [{ + bidder: 'jwplayer', + params: { + publisherId: 'test-publisher-id', + siteId: 'test-site-id', + placementId: 'test-placement-id' + } + }] +}; ``` + +# Sample ortb2 config + +```markdown +pbjs.setConfig({ + ortb2: { + site: { + publisher: { + id: 'test-publisher-id' + }, + content: { + id: 'test-media-id', + url: 'test.mp4', + title: 'title of my media', + ext: { + description: 'description of my media' + } + }, + domain: 'test-domain.com', + page: 'https://www.test-domain.com/test.html', + } + } +} ``` From 10f3226cb617d2231153c7b9138c078bf27041cc Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Fri, 12 Apr 2024 18:35:15 -0300 Subject: [PATCH 45/48] updates demo --- .../noadserver/jwplayerBidAdapter_test.html | 64 ++++ .../noadserver/jwplayer_test.html | 333 ------------------ 2 files changed, 64 insertions(+), 333 deletions(-) create mode 100644 integrationExamples/noadserver/jwplayerBidAdapter_test.html delete mode 100644 integrationExamples/noadserver/jwplayer_test.html diff --git a/integrationExamples/noadserver/jwplayerBidAdapter_test.html b/integrationExamples/noadserver/jwplayerBidAdapter_test.html new file mode 100644 index 00000000000..46227f971ef --- /dev/null +++ b/integrationExamples/noadserver/jwplayerBidAdapter_test.html @@ -0,0 +1,64 @@ + + + + + + + + + + + diff --git a/integrationExamples/noadserver/jwplayer_test.html b/integrationExamples/noadserver/jwplayer_test.html deleted file mode 100644 index f544b9b55c6..00000000000 --- a/integrationExamples/noadserver/jwplayer_test.html +++ /dev/null @@ -1,333 +0,0 @@ - - - - - - - - - - - - - - - From 491dc05858942a21b0d383c4ade8635031480642 Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Fri, 12 Apr 2024 19:06:38 -0300 Subject: [PATCH 46/48] adds content url --- .../noadserver/jwplayerBidAdapter_test.html | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/integrationExamples/noadserver/jwplayerBidAdapter_test.html b/integrationExamples/noadserver/jwplayerBidAdapter_test.html index 46227f971ef..0e2df3ac416 100644 --- a/integrationExamples/noadserver/jwplayerBidAdapter_test.html +++ b/integrationExamples/noadserver/jwplayerBidAdapter_test.html @@ -49,6 +49,13 @@ mediaIDs: ['test-media-id'] } }] + }, + ortb2: { + site: { + content: { + url: 'test.mp4' + } + } } }); pbjs.addAdUnits([adUnit]); From 81e5dfc0fe26e0f668ce53cbadd2fdefd829b2a7 Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Fri, 12 Apr 2024 19:17:00 -0300 Subject: [PATCH 47/48] adds jwplayer ssp --- .../noadserver/jwplayerBidAdapter_test.html | 8 ++++++-- .../realTimeData/jwplayerRtdProvider_example.html | 7 +++++++ .../videoModule/jwplayer/bidMarkedAsUsed.html | 7 +++++++ .../videoModule/jwplayer/bidRequestScheduling.html | 13 +++++++++---- .../jwplayer/bidsBackHandlerOverride.html | 7 +++++++ .../videoModule/jwplayer/eventListeners.html | 7 +++++++ .../videoModule/jwplayer/eventsUI.html | 7 +++++++ .../videoModule/jwplayer/gamAdServerMediation.html | 7 +++++++ .../videoModule/jwplayer/mediaMetadata.html | 7 +++++++ .../videoModule/jwplayer/playlist.html | 7 +++++++ .../videoModule/videojs/bidMarkedAsUsed.html | 7 +++++++ .../videoModule/videojs/bidRequestScheduling.html | 7 +++++++ .../videojs/bidsBackHandlerOverride.html | 7 +++++++ .../videoModule/videojs/eventListeners.html | 7 +++++++ .../videoModule/videojs/eventsUI.html | 7 +++++++ .../videoModule/videojs/gamAdServerMediation.html | 7 +++++++ .../videoModule/videojs/mediaMetadata.html | 7 +++++++ .../videoModule/videojs/playlist.html | 7 +++++++ 18 files changed, 127 insertions(+), 6 deletions(-) diff --git a/integrationExamples/noadserver/jwplayerBidAdapter_test.html b/integrationExamples/noadserver/jwplayerBidAdapter_test.html index 0e2df3ac416..f8b15af64a2 100644 --- a/integrationExamples/noadserver/jwplayerBidAdapter_test.html +++ b/integrationExamples/noadserver/jwplayerBidAdapter_test.html @@ -11,7 +11,7 @@ pos: 0, w: 640, h: 480, - mimes : ['video/x-ms-wmv', 'video/mp4'], + mimes : ['application/vnd.apple.mpegurl', 'video/mp4'], minduration : 0, maxduration: 60, protocols : [2,3,7,5,6,8], @@ -46,7 +46,11 @@ name: "jwplayer", waitForIt: true, params: { - mediaIDs: ['test-media-id'] + mediaIDs: ['test-media-id'], + overrideContentUrl: 'always', + overrideContentId: 'always', + overrideContentTitle: 'always', + overrideContentDescription: 'always' } }] }, diff --git a/integrationExamples/realTimeData/jwplayerRtdProvider_example.html b/integrationExamples/realTimeData/jwplayerRtdProvider_example.html index 4f29ef1c406..f3f0c64fb1a 100644 --- a/integrationExamples/realTimeData/jwplayerRtdProvider_example.html +++ b/integrationExamples/realTimeData/jwplayerRtdProvider_example.html @@ -35,6 +35,13 @@ }, // Replace this object to test a new Adapter! bids: [{ + bidder: 'jwplayer', + params: { + publisherId: 'test-publisher-id', + siteId: 'test-site-id', + placementId: 'test-placement-id' + } + }, { bidder: 'ix', params: { siteId: '300', diff --git a/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html b/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html index 80ea81d09b6..d0b261043e4 100644 --- a/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html +++ b/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html @@ -20,6 +20,13 @@ }, bids: [{ + bidder: 'jwplayer', + params: { + publisherId: 'test-publisher-id', + siteId: 'test-site-id', + placementId: 'test-placement-id' + } + }, { bidder: 'ix', params: { siteId: '300', diff --git a/integrationExamples/videoModule/jwplayer/bidRequestScheduling.html b/integrationExamples/videoModule/jwplayer/bidRequestScheduling.html index 663765317b0..c40af32cac2 100644 --- a/integrationExamples/videoModule/jwplayer/bidRequestScheduling.html +++ b/integrationExamples/videoModule/jwplayer/bidRequestScheduling.html @@ -50,12 +50,17 @@ mediationLayerAdServer: "dfp", bidTimeout: 2000 }, - bidders: [ - { + bidders: [{ + bidder: 'jwplayer', + params: { + publisherId: 'test-publisher-id', + siteId: 'test-site-id', + placementId: 'test-placement-id' + } + }, { name: "ix", siteId: "300" - } - ] + }] } } } diff --git a/integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html b/integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html index 66eaff26090..75a72ba3501 100644 --- a/integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html +++ b/integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html @@ -19,6 +19,13 @@ }, bids: [{ + bidder: 'jwplayer', + params: { + publisherId: 'test-publisher-id', + siteId: 'test-site-id', + placementId: 'test-placement-id' + } + }, { bidder: 'ix', params: { siteId: '300', diff --git a/integrationExamples/videoModule/jwplayer/eventListeners.html b/integrationExamples/videoModule/jwplayer/eventListeners.html index 39acb086107..6f04f37264b 100644 --- a/integrationExamples/videoModule/jwplayer/eventListeners.html +++ b/integrationExamples/videoModule/jwplayer/eventListeners.html @@ -23,6 +23,13 @@ // Replace this object to test a new Adapter! bids: [{ + bidder: 'jwplayer', + params: { + publisherId: 'test-publisher-id', + siteId: 'test-site-id', + placementId: 'test-placement-id' + } + }, { bidder: 'ix', params: { siteId: '300', diff --git a/integrationExamples/videoModule/jwplayer/eventsUI.html b/integrationExamples/videoModule/jwplayer/eventsUI.html index 78d72a6153d..cfd1efe7624 100644 --- a/integrationExamples/videoModule/jwplayer/eventsUI.html +++ b/integrationExamples/videoModule/jwplayer/eventsUI.html @@ -22,6 +22,13 @@ }, bids: [{ + bidder: 'jwplayer', + params: { + publisherId: 'test-publisher-id', + siteId: 'test-site-id', + placementId: 'test-placement-id' + } + }, { bidder: 'ix', params: { siteId: '300', diff --git a/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html b/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html index 018c8eba8b2..1f4331785ea 100644 --- a/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html +++ b/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html @@ -19,6 +19,13 @@ divId: 'player', // required to indicate which player is being used to render this ad unit. }, bids: [{ + bidder: 'jwplayer', + params: { + publisherId: 'test-publisher-id', + siteId: 'test-site-id', + placementId: 'test-placement-id' + } + }, { bidder: 'ix', params: { siteId: '300', diff --git a/integrationExamples/videoModule/jwplayer/mediaMetadata.html b/integrationExamples/videoModule/jwplayer/mediaMetadata.html index 7581af571d3..63e62aa4b82 100644 --- a/integrationExamples/videoModule/jwplayer/mediaMetadata.html +++ b/integrationExamples/videoModule/jwplayer/mediaMetadata.html @@ -20,6 +20,13 @@ }, bids: [{ + bidder: 'jwplayer', + params: { + publisherId: 'test-publisher-id', + siteId: 'test-site-id', + placementId: 'test-placement-id' + } + }, { bidder: 'ix', params: { siteId: '300', diff --git a/integrationExamples/videoModule/jwplayer/playlist.html b/integrationExamples/videoModule/jwplayer/playlist.html index 89efaea3d5c..9e89f606f23 100644 --- a/integrationExamples/videoModule/jwplayer/playlist.html +++ b/integrationExamples/videoModule/jwplayer/playlist.html @@ -20,6 +20,13 @@ }, bids: [{ + bidder: 'jwplayer', + params: { + publisherId: 'test-publisher-id', + siteId: 'test-site-id', + placementId: 'test-placement-id' + } + }, { bidder: 'ix', params: { siteId: '300', diff --git a/integrationExamples/videoModule/videojs/bidMarkedAsUsed.html b/integrationExamples/videoModule/videojs/bidMarkedAsUsed.html index d6656bc0c93..35745ab281f 100644 --- a/integrationExamples/videoModule/videojs/bidMarkedAsUsed.html +++ b/integrationExamples/videoModule/videojs/bidMarkedAsUsed.html @@ -35,6 +35,13 @@ }, bids: [{ + bidder: 'jwplayer', + params: { + publisherId: 'test-publisher-id', + siteId: 'test-site-id', + placementId: 'test-placement-id' + } + }, { bidder: 'ix', params: { siteId: '300', diff --git a/integrationExamples/videoModule/videojs/bidRequestScheduling.html b/integrationExamples/videoModule/videojs/bidRequestScheduling.html index eb10fda89a2..da6499ca4cc 100644 --- a/integrationExamples/videoModule/videojs/bidRequestScheduling.html +++ b/integrationExamples/videoModule/videojs/bidRequestScheduling.html @@ -37,6 +37,13 @@ }, bids: [{ + bidder: 'jwplayer', + params: { + publisherId: 'test-publisher-id', + siteId: 'test-site-id', + placementId: 'test-placement-id' + } + }, { bidder: 'ix', params: { siteId: '300', diff --git a/integrationExamples/videoModule/videojs/bidsBackHandlerOverride.html b/integrationExamples/videoModule/videojs/bidsBackHandlerOverride.html index ac8f4163e76..74217ecee2c 100644 --- a/integrationExamples/videoModule/videojs/bidsBackHandlerOverride.html +++ b/integrationExamples/videoModule/videojs/bidsBackHandlerOverride.html @@ -34,6 +34,13 @@ }, bids: [{ + bidder: 'jwplayer', + params: { + publisherId: 'test-publisher-id', + siteId: 'test-site-id', + placementId: 'test-placement-id' + } + }, { bidder: 'ix', params: { siteId: '300', diff --git a/integrationExamples/videoModule/videojs/eventListeners.html b/integrationExamples/videoModule/videojs/eventListeners.html index 1966f134e02..3fc2ef7137c 100644 --- a/integrationExamples/videoModule/videojs/eventListeners.html +++ b/integrationExamples/videoModule/videojs/eventListeners.html @@ -35,6 +35,13 @@ }, bids: [{ + bidder: 'jwplayer', + params: { + publisherId: 'test-publisher-id', + siteId: 'test-site-id', + placementId: 'test-placement-id' + } + }, { bidder: 'ix', params: { siteId: '300', diff --git a/integrationExamples/videoModule/videojs/eventsUI.html b/integrationExamples/videoModule/videojs/eventsUI.html index 9eba09f7a52..215b2de4d25 100644 --- a/integrationExamples/videoModule/videojs/eventsUI.html +++ b/integrationExamples/videoModule/videojs/eventsUI.html @@ -37,6 +37,13 @@ }, bids: [{ + bidder: 'jwplayer', + params: { + publisherId: 'test-publisher-id', + siteId: 'test-site-id', + placementId: 'test-placement-id' + } + }, { bidder: 'ix', params: { siteId: '300', diff --git a/integrationExamples/videoModule/videojs/gamAdServerMediation.html b/integrationExamples/videoModule/videojs/gamAdServerMediation.html index 6ffc1a67c03..d6603abbf8f 100644 --- a/integrationExamples/videoModule/videojs/gamAdServerMediation.html +++ b/integrationExamples/videoModule/videojs/gamAdServerMediation.html @@ -34,6 +34,13 @@ divId: 'player', // required to indicate which player is being used to render this ad unit. }, bids: [{ + bidder: 'jwplayer', + params: { + publisherId: 'test-publisher-id', + siteId: 'test-site-id', + placementId: 'test-placement-id' + } + }, { bidder: 'ix', params: { siteId: '300', diff --git a/integrationExamples/videoModule/videojs/mediaMetadata.html b/integrationExamples/videoModule/videojs/mediaMetadata.html index ede076fd814..084c597cddd 100644 --- a/integrationExamples/videoModule/videojs/mediaMetadata.html +++ b/integrationExamples/videoModule/videojs/mediaMetadata.html @@ -35,6 +35,13 @@ }, bids: [{ + bidder: 'jwplayer', + params: { + publisherId: 'test-publisher-id', + siteId: 'test-site-id', + placementId: 'test-placement-id' + } + }, { bidder: 'ix', params: { siteId: '300', diff --git a/integrationExamples/videoModule/videojs/playlist.html b/integrationExamples/videoModule/videojs/playlist.html index eb813f095f7..2563717df41 100644 --- a/integrationExamples/videoModule/videojs/playlist.html +++ b/integrationExamples/videoModule/videojs/playlist.html @@ -36,6 +36,13 @@ }, bids: [{ + bidder: 'jwplayer', + params: { + publisherId: 'test-publisher-id', + siteId: 'test-site-id', + placementId: 'test-placement-id' + } + }, { bidder: 'ix', params: { siteId: '300', From 1815c71746ab654fb83de9fb0deb138d715bc1ae Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Mon, 15 Apr 2024 18:19:42 -0300 Subject: [PATCH 48/48] renames demo --- ...wplayerBidAdapter_test.html => jwplayerBidAdapter_sample.html} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename integrationExamples/noadserver/{jwplayerBidAdapter_test.html => jwplayerBidAdapter_sample.html} (100%) diff --git a/integrationExamples/noadserver/jwplayerBidAdapter_test.html b/integrationExamples/noadserver/jwplayerBidAdapter_sample.html similarity index 100% rename from integrationExamples/noadserver/jwplayerBidAdapter_test.html rename to integrationExamples/noadserver/jwplayerBidAdapter_sample.html