diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index ab9c995aa7c..8f821e87911 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -5,6 +5,7 @@ import find from 'core-js-pure/features/array/find.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { INSTREAM, OUTSTREAM } from '../src/video.js'; import includes from 'core-js-pure/features/array/includes.js'; +import { Renderer } from '../src/Renderer.js'; const BIDDER_CODE = 'ix'; const ALIAS_BIDDER_CODE = 'roundel'; @@ -24,7 +25,7 @@ const PRICE_TO_DOLLAR_FACTOR = { JPY: 1 }; const USER_SYNC_URL = 'https://js-sec.indexww.com/um/ixmatch.html'; - +const RENDERER_URL = 'https://js-sec.indexww.com/htv/video-player.js'; const FLOOR_SOURCE = { PBJS: 'p', IX: 'x' }; // determines which eids we send and the rtiPartner field in ext @@ -267,6 +268,7 @@ function parseBid(rawBid, currency, bidRequest) { bid.width = bidRequest.video.w; bid.height = bidRequest.video.h; bid.mediaType = VIDEO; + bid.mediaTypes = bidRequest.mediaTypes; bid.ttl = isValidExpiry ? rawBid.exp : VIDEO_TIME_TO_LIVE; } else { bid.ad = rawBid.adm; @@ -385,17 +387,22 @@ function isValidBidFloorParams(bidFloor, bidFloorCur) { } /** - * Finds the impression with the associated id. + * Get bid request object with the associated id. * * @param {*} id Id of the impression. * @param {array} impressions List of impressions sent in the request. * @return {object} The impression with the associated id. */ -function getBidRequest(id, impressions) { +function getBidRequest(id, impressions, validBidRequests) { if (!id) { return; } - return find(impressions, imp => imp.id === id); + const bidRequest = { + ...find(validBidRequests, bid => bid.bidId === id), + ...find(impressions, imp => imp.id === id) + } + + return bidRequest; } /** @@ -718,7 +725,8 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { requests.push({ method: 'GET', url: baseUrl, - data: clonedPayload + data: clonedPayload, + validBidRequests }); currentRequestSize = baseRequestSize; @@ -930,6 +938,43 @@ function createMissingBannerImp(bid, imp, newSize) { return newImp; } +/** + * Initialize Outstream Renderer + * @param {Object} bid + */ +function outstreamRenderer (bid) { + bid.renderer.push(() => { + var config = { + width: bid.width, + height: bid.height, + timeout: 3000 + }; + + window.IXOutstreamPlayer(bid.vastUrl, bid.adUnitCode, config); + }); +} + +/** + * Create Outstream Renderer + * @param {string} id + * @returns {Renderer} + */ +function createRenderer (id) { + const renderer = Renderer.install({ + id: id, + url: RENDERER_URL, + loaded: false + }); + + try { + renderer.setRender(outstreamRenderer); + } catch (err) { + utils.logWarn('Prebid Error calling setRender on renderer', err); + } + + return renderer; +} + export const spec = { code: BIDDER_CODE, @@ -1108,8 +1153,13 @@ export const spec = { let requestBid = JSON.parse(bidderRequest.data.r); for (let j = 0; j < innerBids.length; j++) { - const bidRequest = getBidRequest(innerBids[j].impid, requestBid.imp); + const bidRequest = getBidRequest(innerBids[j].impid, requestBid.imp, bidderRequest.validBidRequests); bid = parseBid(innerBids[j], responseBody.cur, bidRequest); + + if (!utils.deepAccess(bid, 'mediaTypes.video.renderer') && utils.deepAccess(bid, 'mediaTypes.video.context') === 'outstream') { + bid.mediaTypes.video.renderer = createRenderer(innerBids[j].bidId); + } + bids.push(bid); } } diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 85236571383..6eff922536f 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -1233,7 +1233,7 @@ describe('IndexexchangeAdapter', function () { }); describe('First party data', function () { - afterEach(function() { + afterEach(function () { config.setConfig({ ortb2: {} }) @@ -2129,7 +2129,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const result = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); expect(result[0]).to.deep.equal(expectedParse[0]); }); @@ -2153,7 +2153,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE_WITHOUT_ADOMAIN }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const result = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE_WITHOUT_ADOMAIN }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); expect(result[0]).to.deep.equal(expectedParse[0]); }); @@ -2180,7 +2180,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); }); it('should set Japanese price correctly', function () { @@ -2206,7 +2206,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); expect(result[0]).to.deep.equal(expectedParse[0]); }); @@ -2235,7 +2235,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); expect(result[0].dealId).to.equal(expectedParse[0].dealId); }); @@ -2262,7 +2262,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); expect(result[0]).to.deep.equal(expectedParse[0]); }); @@ -2290,7 +2290,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); expect(result[0].dealId).to.deep.equal(expectedParse[0].dealId); }); @@ -2301,6 +2301,17 @@ describe('IndexexchangeAdapter', function () { cpm: 1.1, creativeId: '12346', mediaType: 'video', + mediaTypes: { + video: { + context: 'instream', + playerSize: [ + [ + 400, + 100 + ] + ] + } + }, width: 640, height: 480, currency: 'USD', @@ -2315,7 +2326,10 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: DEFAULT_VIDEO_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const result = spec.interpretResponse({ body: DEFAULT_VIDEO_BID_RESPONSE }, { + data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: ONE_VIDEO + }); + expect(result[0]).to.deep.equal(expectedParse[0]); }); @@ -2357,16 +2371,16 @@ describe('IndexexchangeAdapter', function () { const VIDEO_RESPONSE_WITH_EXP = utils.deepClone(DEFAULT_VIDEO_BID_RESPONSE); VIDEO_RESPONSE_WITH_EXP.seatbid[0].bid[0].exp = 200; BANNER_RESPONSE_WITH_EXP.seatbid[0].bid[0].exp = 100; - const bannerResult = spec.interpretResponse({ body: BANNER_RESPONSE_WITH_EXP }, { data: DEFAULT_BIDDER_REQUEST_DATA }); - const videoResult = spec.interpretResponse({ body: VIDEO_RESPONSE_WITH_EXP }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const bannerResult = spec.interpretResponse({ body: BANNER_RESPONSE_WITH_EXP }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); + const videoResult = spec.interpretResponse({ body: VIDEO_RESPONSE_WITH_EXP }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); expect(bannerResult[0].ttl).to.equal(100); expect(videoResult[0].ttl).to.equal(200); }); it('should default bid[].ttl if seat[].bid[].exp is not in the resposne', function () { - const bannerResult = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA }); - const videoResult = spec.interpretResponse({ body: DEFAULT_VIDEO_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + const bannerResult = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); + const videoResult = spec.interpretResponse({ body: DEFAULT_VIDEO_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: [] }); expect(bannerResult[0].ttl).to.equal(300); expect(videoResult[0].ttl).to.equal(3600);