From 8d635ff86ca3c3bde767bca6acee8b89d5b1f2ca Mon Sep 17 00:00:00 2001 From: nakamoto Date: Fri, 15 Feb 2019 17:28:34 +0900 Subject: [PATCH 01/25] Add microadBidAdapter --- modules/microadBidAdapter.js | 151 ++++++++ modules/microadBidAdapter.md | 28 ++ test/spec/modules/microadBidAdapter_spec.js | 381 ++++++++++++++++++++ 3 files changed, 560 insertions(+) create mode 100644 modules/microadBidAdapter.js create mode 100644 modules/microadBidAdapter.md create mode 100644 test/spec/modules/microadBidAdapter_spec.js diff --git a/modules/microadBidAdapter.js b/modules/microadBidAdapter.js new file mode 100644 index 00000000000..7b5c8f08099 --- /dev/null +++ b/modules/microadBidAdapter.js @@ -0,0 +1,151 @@ +import { registerBidder } from 'src/adapters/bidderFactory'; +import { BANNER } from 'src/mediaTypes'; + +const BIDDER_CODE = 'microad'; + +const ENDPOINT_URLS = { + 'production': '//s-rtb.send.microad.jp/prebid', + 'test': 'https://rtbtest.send.microad.jp/prebid' +}; +export let ENVIRONMENT = 'production'; + +/* eslint-disable no-template-curly-in-string */ +const EXT_URL_STRING = '${COMPASS_EXT_URL}'; +const EXT_REF_STRING = '${COMPASS_EXT_REF}'; +const EXT_IFA_STRING = '${COMPASS_EXT_IFA}'; +const EXT_APPID_STRING = '${COMPASS_EXT_APPID}'; +const EXT_GEO_STRING = '${COMPASS_EXT_GEO}'; +/* eslint-enable no-template-curly-in-string */ + +const BANNER_CODE = 1; +const NATIVE_CODE = 2; +const VIDEO_CODE = 4; + +function createCBT() { + const randomValue = Math.floor(Math.random() * Math.pow(10, 18)).toString(16); + const date = new Date().getTime().toString(16); + return randomValue + date; +} + +function createBitSequenceFromMediaType(hi, code) { + return (hi ? -1 : 0) & code; +} + +function convertMediaTypes(bid) { + return createBitSequenceFromMediaType(bid.mediaTypes.banner, BANNER_CODE) | + createBitSequenceFromMediaType(bid.mediaTypes.native, NATIVE_CODE) | + createBitSequenceFromMediaType(bid.mediaTypes.video, VIDEO_CODE); +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + isBidRequestValid: function(bid) { + return !!(bid && bid.params && bid.params.spot && bid.mediaTypes && (bid.mediaTypes.banner || bid.mediaTypes.native || bid.mediaTypes.video)); + }, + buildRequests: function(validBidRequests, bidderRequest) { + const requests = []; + + validBidRequests.forEach(bid => { + const bidParams = bid.params; + const params = { + spot: encodeURIComponent(bidParams.spot), + url: encodeURIComponent(bidderRequest.refererInfo.canonicalUrl || window.location.href), + referrer: encodeURIComponent(bidderRequest.refererInfo.referer), + bid_id: bid.bidId, + transaction_id: bid.transactionId, + media_types: convertMediaTypes(bid), + cbt: createCBT() + }; + + if (bidParams.url) { + params['url_macro'] = bidParams.url.replace(EXT_URL_STRING, ''); + } + + if (bidParams.referrer) { + params['referrer_macro'] = bidParams.referrer.replace(EXT_REF_STRING, ''); + } + + if (bidParams.ifa) { + params['ifa'] = bidParams.ifa.replace(EXT_IFA_STRING, ''); + } + + if (bidParams.appid) { + params['appid'] = bidParams.appid.replace(EXT_APPID_STRING, ''); + } + + if (bidParams.geo) { + const geo = bidParams.geo.replace(EXT_GEO_STRING, ''); + if (/^[0-9.\-]+,[0-9.\-]+$/.test(geo)) { + params['geo'] = geo; + } + } + + requests.push({ + method: 'GET', + url: ENDPOINT_URLS[ENVIRONMENT], + data: params, + options: { Accept: 'application/json' } + }); + }); + return requests; + }, + interpretResponse: function(serverResponse) { + const body = serverResponse.body; + const bidResponses = []; + + if (body.cpm && body.cpm > 0) { + const bidResponse = { + requestId: body.requestId, + cpm: body.cpm, + width: body.width, + height: body.height, + ad: body.ad, + ttl: body.ttl, + creativeId: body.creativeId, + netRevenue: body.netRevenue, + currency: body.currency, + }; + + if (body.dealId) { + bidResponse['dealId'] = body.dealId; + } + + bidResponses.push(bidResponse); + } + + return bidResponses; + }, + getUserSyncs: function(syncOptions, serverResponses) { + const syncs = []; + + if (!syncOptions.iframeEnabled && !syncOptions.pixelEnabled) { + return syncs; + } + + serverResponses.forEach(resp => { + const syncIframeUrls = resp.body.syncUrls.iframe; + const syncImageUrls = resp.body.syncUrls.image; + if (syncOptions.iframeEnabled && syncIframeUrls) { + syncIframeUrls.forEach(syncIframeUrl => { + syncs.push({ + type: 'iframe', + url: syncIframeUrl + }); + }); + } + if (syncOptions.pixelEnabled && syncImageUrls) { + syncImageUrls.forEach(syncImageUrl => { + syncs.push({ + type: 'image', + url: syncImageUrl + }); + }); + } + }); + + return syncs; + } +}; + +registerBidder(spec); diff --git a/modules/microadBidAdapter.md b/modules/microadBidAdapter.md new file mode 100644 index 00000000000..c805e5cf6fb --- /dev/null +++ b/modules/microadBidAdapter.md @@ -0,0 +1,28 @@ +# Overview + +Module Name: MicroAd SSP Bidder Adapter +Module Type: Bidder Adapter +Maintainer: prebid@microad.co.jp + +# Description + +Module that connects to MicroAd SSP demand sources. + +# Test Parameters + +```javascript + var adUnits = [ + code: '209e56872ae8b0442a60477ae0c58be9', + mediaTypes: { + banner: { + sizes: [[200, 200]] + } + }, + bids: [{ + bidder: 'microad', + params: { + spot: '209e56872ae8b0442a60477ae0c58be9' + } + }] + ]; +``` diff --git a/test/spec/modules/microadBidAdapter_spec.js b/test/spec/modules/microadBidAdapter_spec.js new file mode 100644 index 00000000000..3089f9f4039 --- /dev/null +++ b/test/spec/modules/microadBidAdapter_spec.js @@ -0,0 +1,381 @@ +import { expect } from 'chai'; +import { spec } from 'modules/microadBidAdapter'; +import * as utils from 'src/utils'; + +describe('microadBidAdapter', () => { + const bidRequestTemplate = { + bidder: 'microad', + mediaTypes: { + banner: {} + }, + params: { + spot: 'spot-code' + }, + bidId: 'bid-id', + transactionId: 'transaction-id' + }; + + describe('isBidRequestValid', () => { + it('should return true when required parameters are set', () => { + const validBids = [ + bidRequestTemplate, + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + native: {} + } + }), + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + video: {} + } + }) + ]; + validBids.forEach(validBid => { + expect(spec.isBidRequestValid(validBid)).to.equal(true); + }); + }); + + it('should return false when required parameters are not set', () => { + const bidWithoutParams = utils.deepClone(bidRequestTemplate); + delete bidWithoutParams.params; + const bidWithoutSpot = utils.deepClone(bidRequestTemplate); + delete bidWithoutSpot.params.spot; + const bidWithoutMediaTypes = utils.deepClone(bidRequestTemplate); + delete bidWithoutMediaTypes.mediaTypes; + + const invalidBids = [ + {}, + bidWithoutParams, + bidWithoutSpot, + bidWithoutMediaTypes, + Object.assign({}, bidRequestTemplate, { + mediaTypes: {} + }) + ]; + invalidBids.forEach(invalidBid => { + expect(spec.isBidRequestValid(invalidBid)).to.equal(false); + }); + }); + }); + + describe('buildRequests', () => { + const bidderRequest = { + refererInfo: { + canonicalUrl: 'http://example.com/to', + referer: 'http://example.com/from' + } + }; + const expectedResultTemplate = { + spot: 'spot-code', + url: encodeURIComponent('http://example.com/to'), + referrer: encodeURIComponent('http://example.com/from'), + bid_id: 'bid-id', + transaction_id: 'transaction-id', + media_types: 1 + }; + + it('should generate valid media_types', () => { + const bidRequests = [ + bidRequestTemplate, + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + banner: {}, native: {} + } + }), + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + banner: {}, native: {}, video: {} + } + }), + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + native: {} + } + }), + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + native: {}, video: {} + } + }), + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + video: {} + } + }), + Object.assign({}, bidRequestTemplate, { + mediaTypes: { + banner: {}, video: {} + } + }) + ]; + + const results = bidRequests.map(bid => { + const requests = spec.buildRequests([bid], bidderRequest); + return requests[0].data.media_types; + }); + expect(results).to.deep.equal([ + 1, // BANNER + 3, // BANNER + NATIVE + 7, // BANNER + NATIVE + VIDEO + 2, // NATIVE + 6, // NATIVE + VIDEO + 4, // VIDEO + 5 // BANNER + VIDEO + ]); + }); + + it('should use window.location.href if there is no canonicalUrl', () => { + const bidderRequestWithoutCanonicalUrl = { + refererInfo: { + referer: 'http://example.com/from' + } + }; + const requests = spec.buildRequests([bidRequestTemplate], bidderRequestWithoutCanonicalUrl); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + url: encodeURIComponent(window.location.href) + }) + ); + }); + }); + + it('should generate valid request with no optional parameters', () => { + const requests = spec.buildRequests([bidRequestTemplate], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt + }) + ); + }); + }); + + it('should add url_macro parameter to response if request parameters contain url', () => { + const bidRequestWithUrl = Object.assign({}, bidRequestTemplate, { + params: { + spot: 'spot-code', + url: '${COMPASS_EXT_URL}url-macro' + } + }); + const requests = spec.buildRequests([bidRequestWithUrl], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + url_macro: 'url-macro' + }) + ); + }); + }); + + it('should add referrer_macro parameter to response if request parameters contain referrer', () => { + const bidRequestWithReferrer = Object.assign({}, bidRequestTemplate, { + params: { + spot: 'spot-code', + referrer: '${COMPASS_EXT_REF}referrer-macro' + } + }); + const requests = spec.buildRequests([bidRequestWithReferrer], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + referrer_macro: 'referrer-macro' + }) + ); + }); + }); + + it('should add ifa parameter to response if request parameters contain ifa', () => { + const bidRequestWithIfa = Object.assign({}, bidRequestTemplate, { + params: { + spot: 'spot-code', + ifa: '${COMPASS_EXT_IFA}ifa' + } + }); + const requests = spec.buildRequests([bidRequestWithIfa], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + ifa: 'ifa' + }) + ); + }); + }); + + it('should add appid parameter to response if request parameters contain appid', () => { + const bidRequestWithAppid = Object.assign({}, bidRequestTemplate, { + params: { + spot: 'spot-code', + appid: '${COMPASS_EXT_APPID}appid' + } + }); + const requests = spec.buildRequests([bidRequestWithAppid], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + appid: 'appid' + }) + ); + }); + }); + + it('should add geo parameter to response if request parameters contain geo', () => { + const bidRequestWithGeo = Object.assign({}, bidRequestTemplate, { + params: { + spot: 'spot-code', + geo: '${COMPASS_EXT_GEO}35.655275,139.693771' + } + }); + const requests = spec.buildRequests([bidRequestWithGeo], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt, + geo: '35.655275,139.693771' + }) + ); + }); + }); + + it('should not add geo parameter to response if request parameters contain invalid geo', () => { + const bidRequestWithGeo = Object.assign({}, bidRequestTemplate, { + params: { + spot: 'spot-code', + geo: '${COMPASS_EXT_GEO}invalid format geo' + } + }); + const requests = spec.buildRequests([bidRequestWithGeo], bidderRequest); + requests.forEach(request => { + expect(request.data).to.deep.equal( + Object.assign({}, expectedResultTemplate, { + cbt: request.data.cbt + }) + ); + }); + }); + }); + + describe('interpretResponse', () => { + const serverResponseTemplate = { + body: { + requestId: 'request-id', + cpm: 0.1, + width: 200, + height: 100, + ad: '
test
', + ttl: 10, + creativeId: 'creative-id', + netRevenue: true, + currency: 'JPY' + } + }; + const expectedBidResponseTemplate = { + requestId: 'request-id', + cpm: 0.1, + width: 200, + height: 100, + ad: '
test
', + ttl: 10, + creativeId: 'creative-id', + netRevenue: true, + currency: 'JPY' + }; + + it('should return nothing if server response body does not contain cpm', () => { + const emptyResponse = { + body: {} + }; + + expect(spec.interpretResponse(emptyResponse)).to.deep.equal([]); + }); + + it('should return nothing if returned cpm is zero', () => { + const serverResponse = { + body: { + cpm: 0 + } + }; + + expect(spec.interpretResponse(serverResponse)).to.deep.equal([]); + }); + + it('should return a valid bidResponse without deal id if serverResponse is valid, has a nonzero cpm and no deal id', () => { + expect(spec.interpretResponse(serverResponseTemplate)).to.deep.equal([expectedBidResponseTemplate]); + }); + + it('should return a valid bidResponse with deal id if serverResponse is valid, has a nonzero cpm and a deal id', () => { + const serverResponseWithDealId = Object.assign({}, utils.deepClone(serverResponseTemplate)); + serverResponseWithDealId.body['dealId'] = 10001; + const expectedBidResponse = Object.assign({}, expectedBidResponseTemplate, { + dealId: 10001 + }); + + expect(spec.interpretResponse(serverResponseWithDealId)).to.deep.equal([expectedBidResponse]); + }); + }); + + describe('getUserSyncs', () => { + const BOTH_ENABLED = { + iframeEnabled: true, pixelEnabled: true + }; + const IFRAME_ENABLED = { + iframeEnabled: true, pixelEnabled: false + }; + const PIXEL_ENABLED = { + iframeEnabled: false, pixelEnabled: true + }; + const BOTH_DISABLED = { + iframeEnabled: false, pixelEnabled: false + }; + const serverResponseTemplate = { + body: { + syncUrls: { + iframe: ['https://www.exmaple.com/iframe1', 'https://www.exmaple.com/iframe2'], + image: ['https://www.exmaple.com/image1', 'https://www.exmaple.com/image2'] + } + } + }; + const expectedIframeSyncs = [ + {type: 'iframe', url: 'https://www.exmaple.com/iframe1'}, + {type: 'iframe', url: 'https://www.exmaple.com/iframe2'} + ]; + const expectedImageSyncs = [ + {type: 'image', url: 'https://www.exmaple.com/image1'}, + {type: 'image', url: 'https://www.exmaple.com/image2'} + ]; + + it('should return nothing if no sync urls are set', () => { + const serverResponse = utils.deepClone(serverResponseTemplate); + serverResponse.body.syncUrls.iframe = []; + serverResponse.body.syncUrls.image = []; + + const syncs = spec.getUserSyncs(BOTH_ENABLED, [serverResponse]); + expect(syncs).to.deep.equal([]); + }); + + it('should return nothing if sync is disabled', () => { + const syncs = spec.getUserSyncs(BOTH_DISABLED, [serverResponseTemplate]); + expect(syncs).to.deep.equal([]); + }); + + it('should register iframe and image sync urls if sync is enabled', () => { + const syncs = spec.getUserSyncs(BOTH_ENABLED, [serverResponseTemplate]); + expect(syncs).to.deep.equal(expectedIframeSyncs.concat(expectedImageSyncs)); + }); + + it('should register iframe sync urls if iframe is enabled', () => { + const syncs = spec.getUserSyncs(IFRAME_ENABLED, [serverResponseTemplate]); + expect(syncs).to.deep.equal(expectedIframeSyncs); + }); + + it('should register image sync urls if image is enabled', () => { + const syncs = spec.getUserSyncs(PIXEL_ENABLED, [serverResponseTemplate]); + expect(syncs).to.deep.equal(expectedImageSyncs); + }); + }); +}); From 3055b90bd38c02c76696c98824f648d59135eef3 Mon Sep 17 00:00:00 2001 From: nakamoto Date: Tue, 26 Feb 2019 15:35:24 +0900 Subject: [PATCH 02/25] Remove unnecessary encodeURIComponent from microadBidAdapter --- modules/microadBidAdapter.js | 6 +++--- test/spec/modules/microadBidAdapter_spec.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/microadBidAdapter.js b/modules/microadBidAdapter.js index 7b5c8f08099..786469764bf 100644 --- a/modules/microadBidAdapter.js +++ b/modules/microadBidAdapter.js @@ -49,9 +49,9 @@ export const spec = { validBidRequests.forEach(bid => { const bidParams = bid.params; const params = { - spot: encodeURIComponent(bidParams.spot), - url: encodeURIComponent(bidderRequest.refererInfo.canonicalUrl || window.location.href), - referrer: encodeURIComponent(bidderRequest.refererInfo.referer), + spot: bidParams.spot, + url: bidderRequest.refererInfo.canonicalUrl || window.location.href, + referrer: bidderRequest.refererInfo.referer, bid_id: bid.bidId, transaction_id: bid.transactionId, media_types: convertMediaTypes(bid), diff --git a/test/spec/modules/microadBidAdapter_spec.js b/test/spec/modules/microadBidAdapter_spec.js index 3089f9f4039..a6d1aa1d266 100644 --- a/test/spec/modules/microadBidAdapter_spec.js +++ b/test/spec/modules/microadBidAdapter_spec.js @@ -67,8 +67,8 @@ describe('microadBidAdapter', () => { }; const expectedResultTemplate = { spot: 'spot-code', - url: encodeURIComponent('http://example.com/to'), - referrer: encodeURIComponent('http://example.com/from'), + url: 'http://example.com/to', + referrer: 'http://example.com/from', bid_id: 'bid-id', transaction_id: 'transaction-id', media_types: 1 @@ -135,7 +135,7 @@ describe('microadBidAdapter', () => { expect(request.data).to.deep.equal( Object.assign({}, expectedResultTemplate, { cbt: request.data.cbt, - url: encodeURIComponent(window.location.href) + url: window.location.href }) ); }); From 67fb91bf6e2d11c6cf4d3d74b42362928f06b6de Mon Sep 17 00:00:00 2001 From: Chandra Prakash Date: Tue, 26 Feb 2019 13:50:33 -0800 Subject: [PATCH 03/25] Submit Advangelists Prebid Adapter --- modules/advangelistsBidAdapter.js | 443 +++++++++++++++++++++++ modules/advangelistsBidAdapter.md | 65 ++++ test/spec/advangelistsBidAdapter_spec.js | 141 ++++++++ 3 files changed, 649 insertions(+) create mode 100644 modules/advangelistsBidAdapter.js create mode 100644 modules/advangelistsBidAdapter.md create mode 100644 test/spec/advangelistsBidAdapter_spec.js diff --git a/modules/advangelistsBidAdapter.js b/modules/advangelistsBidAdapter.js new file mode 100644 index 00000000000..d0298db4e1c --- /dev/null +++ b/modules/advangelistsBidAdapter.js @@ -0,0 +1,443 @@ +import * as utils from '../src/utils'; +import { parse as parseUrl } from '../src/url'; +import { config } from '../src/config'; +import { registerBidder } from '../src/adapters/bidderFactory'; +// import { Renderer } from '../src/Renderer'; +import { VIDEO, BANNER } from '../src/mediaTypes'; +import find from 'core-js/library/fn/array/find'; +import includes from 'core-js/library/fn/array/includes'; + +const ADAPTER_VERSION = '1.0'; +const BIDDER_CODE = 'avng'; +// const OUTSTREAM = 'outstream'; + +export const VIDEO_ENDPOINT = '//nep.advangelists.com/xp/get?pubid=';// 0cf8d6d643e13d86a5b6374148a4afac'; +export const BANNER_ENDPOINT = '//nep.advangelists.com/xp/get?pubid=';// 0cf8d6d643e13d86a5b6374148a4afac'; +export const OUTSTREAM_SRC = '//player-cdn.beachfrontmedia.com/playerapi/loader/outstream.js'; + +export const VIDEO_TARGETING = ['mimes', 'playbackmethod', 'maxduration', 'skip']; +export const DEFAULT_MIMES = ['video/mp4', 'application/javascript']; + +let pubid = ''; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO], + + isBidRequestValid(bidRequest) { + if (typeof bidRequest != 'undefined') { + if (bidRequest.bidder !== BIDDER_CODE && typeof bidRequest.params === 'undefined') { return false; } + if (bidRequest === '' || bidRequest.params.placement === '' || bidRequest.params.pubid === '') { return false; } + return true; + } else { return false; } + }, + + buildRequests(bids, bidderRequest) { + let requests = []; + let videoBids = bids.filter(bid => isVideoBidValid(bid)); + let bannerBids = bids.filter(bid => isBannerBidValid(bid)); + videoBids.forEach(bid => { + pubid = getVideoBidParam(bid, 'pubid'); + requests.push({ + method: 'POST', + url: VIDEO_ENDPOINT + pubid, + data: createVideoRequestData(bid, bidderRequest), + bidRequest: bid + }); + }); + + bannerBids.forEach(bid => { + pubid = getBannerBidParam(bid, 'pubid'); + requests.push({ + method: 'POST', + url: BANNER_ENDPOINT + pubid, + data: createBannerRequestData(bid, bidderRequest), + bidRequest: bid + }); + }); + + console.log(requests); + return requests; + }, + + interpretResponse(serverResponse, {bidRequest}) { + let response = serverResponse.body; + if (response !== null && utils.isEmpty(response) == false) { + if (isVideoBid(bidRequest)) { + console.log('About to create final response for video') + let bidResponse = { + requestId: response.id, + bidderCode: BIDDER_CODE, + cpm: response.seatbid[0].bid[0].price, + width: response.seatbid[0].bid[0].w, + height: response.seatbid[0].bid[0].h, + ttl: response.seatbid[0].bid[0].ttl || 60, + creativeId: response.seatbid[0].bid[0].crid, + currency: response.cur, + mediaType: VIDEO, + netRevenue: true + } + + if (response.seatbid[0].bid[0].adm) { + bidResponse.vastXml = response.seatbid[0].bid[0].adm; + bidResponse.adResponse = { + content: response.seatbid[0].bid[0].adm + }; + } else { + bidResponse.vastUrl = response.seatbid[0].bid[0].nurl; + } + + return bidResponse; + } else { + console.log('About to create final respsone for banner') + return { + requestId: response.id, + bidderCode: BIDDER_CODE, + cpm: response.seatbid[0].bid[0].price, + width: response.seatbid[0].bid[0].w, + height: response.seatbid[0].bid[0].h, + ad: response.seatbid[0].bid[0].adm, + ttl: response.seatbid[0].bid[0].ttl || 60, + creativeId: response.seatbid[0].bid[0].crid, + currency: response.cur, + mediaType: BANNER, + netRevenue: true + } + } + } + } +/*, + + getUserSyncs(syncOptions, serverResponses = [], gdprConsent = {}) { + let syncs = []; + let { gdprApplies, consentString } = gdprConsent; + let bannerResponse = find(serverResponses, (res) => utils.isArray(res.body)); + + if (bannerResponse) { + if (syncOptions.iframeEnabled) { + bannerResponse.body + .filter(bid => bid.sync) + .forEach(bid => { + syncs.push({ + type: 'iframe', + url: bid.sync + }); + }); + } + } else if (syncOptions.iframeEnabled) { + syncs.push({ + type: 'iframe', + url: `//sync.bfmio.com/sync_iframe?ifg=1&id=${appId}&gdpr=${gdprApplies ? 1 : 0}&gc=${consentString || ''}&gce=1` + }); + } else if (syncOptions.pixelEnabled) { + syncs.push({ + type: 'image', + url: `//sync.bfmio.com/syncb?pid=144&id=${appId}&gdpr=${gdprApplies ? 1 : 0}&gc=${consentString || ''}&gce=1` + }); + } + + return syncs; + } */ +}; + +function isBannerBid(bid) { + return utils.deepAccess(bid, 'mediaTypes.banner') || !isVideoBid(bid); +} + +function isVideoBid(bid) { + return utils.deepAccess(bid, 'mediaTypes.video'); +} + +function isVideoBidValid(bid) { + return isVideoBid(bid) && getVideoBidParam(bid, 'pubid') && getVideoBidParam(bid, 'placement'); +} + +function isBannerBidValid(bid) { + return isBannerBid(bid) && getBannerBidParam(bid, 'pubid') && getBannerBidParam(bid, 'placement'); +} + +function getVideoBidParam(bid, key) { + return utils.deepAccess(bid, 'params.video.' + key) || utils.deepAccess(bid, 'params.' + key); +} + +function getBannerBidParam(bid, key) { + return utils.deepAccess(bid, 'params.banner.' + key) || utils.deepAccess(bid, 'params.' + key); +} + +function isMobile() { + return (/(ios|ipod|ipad|iphone|android)/i).test(navigator.userAgent); +} + +function isConnectedTV() { + return (/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(navigator.userAgent); +} + +function getDoNotTrack() { + return navigator.doNotTrack === '1' || window.doNotTrack === '1' || navigator.msDoNoTrack === '1' || navigator.doNotTrack === 'yes'; +} + +function findAndFillParam(o, key, value) { + try { + if (typeof value === 'function') { + o[key] = value(); + } else { + o[key] = value; + } + } catch (ex) {} +} + +/* +function createRenderer(bidRequest) { + const renderer = Renderer.install({ + id: bidRequest.bidId, + url: OUTSTREAM_SRC, + loaded: false + }); + + renderer.setRender(outstreamRender); + + return renderer; +} + +function outstreamRender(bid) { + bid.renderer.push(() => { + window.Beachfront.Player(bid.adUnitCode, { + ad_tag_url: bid.vastUrl, + width: bid.width, + height: bid.height, + expand_in_view: false, + collapse_on_complete: true + }); + }); +} +*/ +function getOsVersion() { + let clientStrings = [ + { s: 'Android', r: /Android/ }, + { s: 'iOS', r: /(iPhone|iPad|iPod)/ }, + { s: 'Mac OS X', r: /Mac OS X/ }, + { s: 'Mac OS', r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ }, + { s: 'Linux', r: /(Linux|X11)/ }, + { s: 'Windows 10', r: /(Windows 10.0|Windows NT 10.0)/ }, + { s: 'Windows 8.1', r: /(Windows 8.1|Windows NT 6.3)/ }, + { s: 'Windows 8', r: /(Windows 8|Windows NT 6.2)/ }, + { s: 'Windows 7', r: /(Windows 7|Windows NT 6.1)/ }, + { s: 'Windows Vista', r: /Windows NT 6.0/ }, + { s: 'Windows Server 2003', r: /Windows NT 5.2/ }, + { s: 'Windows XP', r: /(Windows NT 5.1|Windows XP)/ }, + { s: 'UNIX', r: /UNIX/ }, + { s: 'Search Bot', r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/ } + ]; + let cs = find(clientStrings, cs => cs.r.test(navigator.userAgent)); + return cs ? cs.s : 'unknown'; +} + +function getFirstSize(sizes) { + return (sizes && sizes.length) ? sizes[0] : { w: undefined, h: undefined }; +} + +function parseSizes(sizes) { + return utils.parseSizesInput(sizes).map(size => { + let [ width, height ] = size.split('x'); + return { + w: parseInt(width, 10) || undefined, + h: parseInt(height, 10) || undefined + }; + }); +} + +function getVideoSizes(bid) { + return parseSizes(utils.deepAccess(bid, 'mediaTypes.video.playerSize') || bid.sizes); +} + +function getBannerSizes(bid) { + return parseSizes(utils.deepAccess(bid, 'mediaTypes.banner.sizes') || bid.sizes); +} + +function getTopWindowReferrer() { + try { + return window.top.document.referrer; + } catch (e) { + return ''; + } +} + +function getVideoTargetingParams(bid) { + return Object.keys(Object(bid.params.video)) + .filter(param => includes(VIDEO_TARGETING, param)) + .reduce((obj, param) => { + obj[ param ] = bid.params.video[ param ]; + return obj; + }, {}); +} + +function createVideoRequestData(bid, bidderRequest) { + let topLocation = getTopWindowLocation(bidderRequest); + let topReferrer = getTopWindowReferrer(); + + let sizes = getVideoSizes(bid); + let firstSize = getFirstSize(sizes); + + let video = getVideoTargetingParams(bid); + var o = { + 'device': { + 'langauge': (global.navigator.language).split('-')[0], + 'dnt': (global.navigator.doNotTrack === 1 ? 1 : 0), + 'devicetype': isMobile() ? 4 : isConnectedTV() ? 3 : 2, + 'js': 1, + 'os': getOsVersion() + }, + 'at': 2, + 'site': {}, + 'tmax': 3000, + 'cur': ['USD'], + 'id': bid.bidId, + 'imp': [], + 'regs': { + 'ext': { + } + }, + 'user': { + 'ext': { + } + } + }; + + o.site['page'] = topLocation.href; + o.site['domain'] = topLocation.hostname; + o.site['search'] = topLocation.search; + o.site['domain'] = topLocation.hostname; + o.site['ref'] = topReferrer; + o.site['mobile'] = isMobile() ? 1 : 0; + var secure = topLocation.protocol.indexOf('https') === 0 ? 1 : 0; + + o.device['dnt'] = getDoNotTrack() ? 1 : 0; + + findAndFillParam(o.site, 'name', function() { + return global.top.document.title; + }); + + findAndFillParam(o.device, 'h', function() { + return global.screen.height; + }); + findAndFillParam(o.device, 'w', function() { + return global.screen.width; + }); + + let placement = getVideoBidParam(bid, 'placement'); + + for (let j = 0; j < sizes.length; j++) { + o.imp.push({ + 'id': '' + j, + 'displaymanager': '' + BIDDER_CODE, + 'displaymanagerver': '' + ADAPTER_VERSION, + 'tagId': placement, + 'bidfloor': 2.0, + 'bidfloorcur': 'USD', + 'secure': secure, + 'video': Object.assign({ + 'id': utils.generateUUID(), + 'pos': 0, + 'w': firstSize.w, + 'h': firstSize.h, + 'mimes': DEFAULT_MIMES + }, video) + + }); + } + + if (bidderRequest && bidderRequest.gdprConsent) { + let { gdprApplies, consentString } = bidderRequest.gdprConsent; + o.regs.ext = {'gdpr': gdprApplies ? 1 : 0}; + o.user.ext = {'consent': consentString}; + } + + return o; +} + +function getTopWindowLocation(bidderRequest) { + let url = bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer; + return parseUrl(config.getConfig('pageUrl') || url, { decodeSearchAsString: true }); +} + +function createBannerRequestData(bid, bidderRequest) { + let topLocation = getTopWindowLocation(bidderRequest); + let topReferrer = getTopWindowReferrer(); + + let sizes = getBannerSizes(bid); + + var o = { + 'device': { + 'langauge': (global.navigator.language).split('-')[0], + 'dnt': (global.navigator.doNotTrack === 1 ? 1 : 0), + 'devicetype': isMobile() ? 4 : isConnectedTV() ? 3 : 2, + 'js': 1 + }, + 'at': 2, + 'site': {}, + 'tmax': 3000, + 'cur': ['USD'], + 'id': bid.bidId, + 'imp': [], + 'regs': { + 'ext': { + } + }, + 'user': { + 'ext': { + } + } + }; + + o.site['page'] = topLocation.href; + o.site['domain'] = topLocation.hostname; + o.site['search'] = topLocation.search; + o.site['domain'] = topLocation.hostname; + o.site['ref'] = topReferrer; + o.site['mobile'] = isMobile() ? 1 : 0; + var secure = topLocation.protocol.indexOf('https') === 0 ? 1 : 0; + + o.device['dnt'] = getDoNotTrack() ? 1 : 0; + + findAndFillParam(o.site, 'name', function() { + return global.top.document.title; + }); + + findAndFillParam(o.device, 'h', function() { + return global.screen.height; + }); + findAndFillParam(o.device, 'w', function() { + return global.screen.width; + }); + + let placement = getBannerBidParam(bid, 'placement'); + for (let j = 0; j < sizes.length; j++) { + let size = sizes[j]; + + o.imp.push({ + 'id': '' + j, + 'displaymanager': '' + BIDDER_CODE, + 'displaymanagerver': '' + ADAPTER_VERSION, + 'tagId': placement, + 'bidfloor': 2.0, + 'bidfloorcur': 'USD', + 'secure': secure, + 'banner': { + 'id': utils.generateUUID(), + 'pos': 0, + 'w': size['w'], + 'h': size['h'] + } + }); + } + + if (bidderRequest && bidderRequest.gdprConsent) { + let { gdprApplies, consentString } = bidderRequest.gdprConsent; + o.regs.ext = {'gdpr': gdprApplies ? 1 : 0}; + o.user.ext = {'consent': consentString}; + } + + return o; +} + +registerBidder(spec); diff --git a/modules/advangelistsBidAdapter.md b/modules/advangelistsBidAdapter.md new file mode 100644 index 00000000000..14e2befd48f --- /dev/null +++ b/modules/advangelistsBidAdapter.md @@ -0,0 +1,65 @@ +# Overview + +``` +Module Name: Advangelists Bidder Adapter +Module Type: Bidder Adapter +Maintainer: lokesh@advangelists.com +``` + +# Description + +Connects to Advangelists exchange for bids. + +Advangelists bid adapter supports Banner and Video ads currently. + +For more informatio + +# Sample Display Ad Unit: For Publishers +```javascript +var displayAdUnit = [ +{ + code: 'display', + sizes: [ + [300, 250], + [320, 50] + ], + bids: [{ + bidder: 'avng', + params: { + pubid: '0cf8d6d643e13d86a5b6374148a4afac', + placement: 1234 + } + }] +}]; +``` + +# Sample Video Ad Unit: For Publishers +```javascript + +var videoAdUnit = { + code: 'video', + sizes: [320,480], + mediaTypes: { + video: { + playerSize : [[320, 480]], + context: 'instream' + } + }, + bids: [ + { + bidder: 'avng', + params: { + pubid: '8537f00948fc37cc03c5f0f88e198a76', + placement: 1234, + video: { + id: 123, + skip: 1, + mimes : ['video/mp4', 'application/javascript'], + playbackmethod : [2,6], + maxduration: 30 + } + } + } + ] + }; +``` \ No newline at end of file diff --git a/test/spec/advangelistsBidAdapter_spec.js b/test/spec/advangelistsBidAdapter_spec.js new file mode 100644 index 00000000000..39066cccb30 --- /dev/null +++ b/test/spec/advangelistsBidAdapter_spec.js @@ -0,0 +1,141 @@ +import { expect } from 'chai'; +import { spec } from 'modules/advangelistsBidAdapter'; +import { BANNER, VIDEO } from 'src/mediaTypes'; +import * as utils from 'src/utils'; + +describe('advangelistsBidAdapter', function () { + let bidRequests; + let bidRequestsVid; + + beforeEach(function () { + bidRequests = [{'bidder': 'avng', 'params': {'pubid': '0cf8d6d643e13d86a5b6374148a4afac', 'placement': 1234}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': 'f72931e6-2b0e-4e37-a2bc-1ea912141f81', 'sizes': [[300, 250]], 'bidId': '2aa73f571eaf29', 'bidderRequestId': '1bac84515a7af3', 'auctionId': '5dbc60fa-1aa1-41ce-9092-e6bbd4d478f7', 'src': 'client', 'bidRequestsCount': 1}]; + + bidRequestsVid = [{'bidder': 'avng', 'params': {'pubid': '8537f00948fc37cc03c5f0f88e198a76', 'placement': 1234, 'video': {'id': 123, 'skip': 1, 'mimes': ['video/mp4', 'application/javascript'], 'playbackmethod': [2, 6], 'maxduration': 30}}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'video': {'playerSize': [[320, 480]], 'context': 'instream'}}, 'adUnitCode': 'video1', 'transactionId': '8b060952-93f7-4863-af44-bb8796b97c42', 'sizes': [], 'bidId': '25c6ab92aa0e81', 'bidderRequestId': '1d420b73a013fc', 'auctionId': '9a69741c-34fb-474c-83e1-cfa003aaee17', 'src': 'client', 'bidRequestsCount': 1}]; + }); + + describe('spec.isBidRequestValid', function () { + it('should return true when the required params are passed for banner', function () { + const bidRequest = bidRequests[0]; + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + + it('should return true when the required params are passed for video', function () { + const bidRequests = bidRequestsVid[0]; + expect(spec.isBidRequestValid(bidRequests)).to.equal(true); + }); + + it('should return false when no pub id params are passed', function () { + const bidRequest = bidRequests[0]; + bidRequest.params.pubid = ''; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return false when no placement params are passed', function () { + const bidRequest = bidRequests[0]; + bidRequest.params.placement = ''; + expect(spec.isBidRequestValid(bidRequest)).to.equal(false); + }); + + it('should return false when a bid request is not passed', function () { + console.log('from spec - no bid request'); + expect(spec.isBidRequestValid()).to.equal(false); + console.log('from spec blank object'); + expect(spec.isBidRequestValid({})).to.equal(false); + }); + }); + + describe('spec.buildRequests', function () { + it('should create a POST request for each bid', function () { + const bidRequest = bidRequests[0]; + const requests = spec.buildRequests([ bidRequest ]); + expect(requests[0].method).to.equal('POST'); + }); + + it('should create a POST request for each bid in video request', function () { + const bidRequest = bidRequestsVid[0]; + const requests = spec.buildRequests([ bidRequest ]); + expect(requests[0].method).to.equal('POST'); + }); + + it('should have domain in request', function () { + const bidRequest = bidRequests[0]; + const requests = spec.buildRequests([ bidRequest ]); + expect(Object.keys(requests[0].data.site.domain).length !== 0); + }); + }); + + describe('spec.interpretResponse', function () { + describe('for banner bids', function () { + it('should return no bids if the response is not valid', function () { + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {} }; + const bidResponse = spec.interpretResponse({ body: null }, { bidRequest }); + + if (typeof bidResponse !== 'undefined') { + expect(bidResponse.length).to.equal(0); + } else { + expect(true).to.equal(true); + } + }); + + it('should return no bids if the response is empty', function () { + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {} }; + const bidResponse = spec.interpretResponse({ body: [] }, { bidRequest }); + if (typeof bidResponse !== 'undefined') { + expect(bidResponse.length).to.equal(0); + } else { expect(true).to.equal(true); } + }); + + it('should return valid video bid responses', function () { + let _mediaTypes = VIDEO; + const avngbidreqVid = {'bidRequest': {'mediaTypes': {'video': {'w': 320, 'h': 480}}}}; + const serverResponseVid = {'cur': 'USD', 'id': '25c6ab92aa0e81', 'seatbid': [{'seat': '3', 'bid': [{'crid': '1855', 'h': 480, 'protocol': 2, 'nurl': 'http://nep.advangelists.com/xp/evt?pp=1MO1wiaMhhq7wLRzZZwwwPkJxxKpYEnM5k5MH4qSGm1HR8rp3Nl7vDocvzZzSAvE4pnREL9mQ1kf5PDjk6E8em6DOk7vVrYUH1TYQyqCucd58PFpJNN7h30RXKHHFg3XaLuQ3PKfMuI1qZATBJ6WHcu875y0hqRdiewn0J4JsCYF53M27uwmcV0HnQxARQZZ72mPqrW95U6wgkZljziwKrICM3aBV07TU6YK5R5AyzJRuD6mtrQ2xtHlQ3jXVYKE5bvWFiUQd90t0jOGhPtYBNoOfP7uQ4ZZj4pyucxbr96orHe9PSOn9UpCSWArdx7s8lOfDpwOvbMuyGxynbStDWm38sDgd4bMHnIt762m5VMDNJfiUyX0vWzp05OsufJDVEaWhAM62i40lQZo7mWP4ipoOWLkmlaAzFIMsTcNaHAHiKKqGEOZLkCEhFNM0SLcvgN2HFRULOOIZvusq7TydOKxuXgCS91dLUDxDDDFUK83BFKlMkTxnCzkLbIR1bd9GKcr1TRryOrulyvRWAKAIhEsUzsc5QWFUhmI2dZ1eqnBQJ0c89TaPcnoaP2WipF68UgyiOstf2CBy0M34858tC5PmuQwQYwXscg6zyqDwR0i9MzGH4FkTyU5yeOlPcsA0ht6UcoCdFpHpumDrLUwAaxwGk1Nj8S6YlYYT5wNuTifDGbg22QKXzZBkUARiyVvgPn9nRtXnrd7WmiMYq596rya9RQj7LC0auQW8bHVQLEe49shsZDnAwZTWr4QuYKqgRGZcXteG7RVJe0ryBZezOq11ha9C0Lv0siNVBahOXE35Wzoq4c4BDaGpqvhaKN7pjeWLGlQR04ufWekwxiMWAvjmfgAfexBJ7HfbYNZpq__', 'adid': '61_1855', 'adomain': ['chevrolet.com.ar'], 'price': 2, 'w': 320, 'iurl': 'https://daf37cpxaja7f.cloudfront.net/c61/creative_url_14922301369663_1.png', 'cat': ['IAB2'], 'id': '7f570b40-aca1-4806-8ea8-818ea679c82b_0', 'attr': [], 'impid': '0', 'cid': '61'}]}], 'bidid': '7f570b40-aca1-4806-8ea8-818ea679c82b'} + const bidResponseVid = spec.interpretResponse({ body: serverResponseVid }, avngbidreqVid); + delete bidResponseVid['vastUrl']; + delete bidResponseVid['ad']; + // bidResponseVid['mediaType'] = 'video'; + expect(bidResponseVid).to.deep.equal({ + requestId: bidRequestsVid[0].bidId, + bidderCode: 'avng', + creativeId: serverResponseVid.seatbid[0].bid[0].crid, + cpm: serverResponseVid.seatbid[0].bid[0].price, + width: serverResponseVid.seatbid[0].bid[0].w, + height: serverResponseVid.seatbid[0].bid[0].h, + mediaType: 'video', + currency: 'USD', + netRevenue: true, + ttl: 60 + }); + }); + + it('should return valid banner bid responses', function () { + const avngbidreq = {bids: {}}; + bidRequests.forEach(bid => { + let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER); + avngbidreq.bids[bid.bidId] = {mediaTypes: _mediaTypes, + w: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][0] : bid.mediaTypes[_mediaTypes].playerSize[0], + h: _mediaTypes == BANNER ? bid.mediaTypes[_mediaTypes].sizes[0][1] : bid.mediaTypes[_mediaTypes].playerSize[1] + + }; + }); + const serverResponse = {'id': '2aa73f571eaf29', 'seatbid': [{'bid': [{'id': '2c5e8a1a84522d', 'impid': '2c5e8a1a84522d', 'price': 0.81, 'adid': 'abcde-12345', 'nurl': '', 'adm': '
', 'adomain': ['advertiserdomain.com'], 'iurl': '', 'cid': 'campaign1', 'crid': 'abcde-12345', 'w': 300, 'h': 250}], 'seat': '19513bcfca8006'}], 'bidid': '19513bcfca8006', 'cur': 'USD', 'w': 300, 'h': 250}; + + const bidResponse = spec.interpretResponse({ body: serverResponse }, avngbidreq); + expect(bidResponse).to.deep.equal({ + requestId: bidRequests[0].bidId, + ad: serverResponse.seatbid[0].bid[0].adm, + bidderCode: 'avng', + creativeId: serverResponse.seatbid[0].bid[0].crid, + cpm: serverResponse.seatbid[0].bid[0].price, + width: serverResponse.seatbid[0].bid[0].w, + height: serverResponse.seatbid[0].bid[0].h, + mediaType: 'banner', + currency: 'USD', + netRevenue: true, + ttl: 60 + }); + }); + }); + }); +}); From 3ebb9169391a6f5b0d298bc7ee71c85364f9a37c Mon Sep 17 00:00:00 2001 From: Chandra Prakash Date: Wed, 27 Feb 2019 13:11:08 -0800 Subject: [PATCH 04/25] Submit Advangelists Prebid Adapter 1.1 --- test/spec/{ => modules}/advangelistsBidAdapter_spec.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) rename test/spec/{ => modules}/advangelistsBidAdapter_spec.js (95%) diff --git a/test/spec/advangelistsBidAdapter_spec.js b/test/spec/modules/advangelistsBidAdapter_spec.js similarity index 95% rename from test/spec/advangelistsBidAdapter_spec.js rename to test/spec/modules/advangelistsBidAdapter_spec.js index 39066cccb30..d75f07f26b8 100644 --- a/test/spec/advangelistsBidAdapter_spec.js +++ b/test/spec/modules/advangelistsBidAdapter_spec.js @@ -8,9 +8,9 @@ describe('advangelistsBidAdapter', function () { let bidRequestsVid; beforeEach(function () { - bidRequests = [{'bidder': 'avng', 'params': {'pubid': '0cf8d6d643e13d86a5b6374148a4afac', 'placement': 1234}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': 'f72931e6-2b0e-4e37-a2bc-1ea912141f81', 'sizes': [[300, 250]], 'bidId': '2aa73f571eaf29', 'bidderRequestId': '1bac84515a7af3', 'auctionId': '5dbc60fa-1aa1-41ce-9092-e6bbd4d478f7', 'src': 'client', 'bidRequestsCount': 1}]; + bidRequests = [{'bidder': 'avng', 'params': {'pubid': '0cf8d6d643e13d86a5b6374148a4afac', 'placement': 1234}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': 'f72931e6-2b0e-4e37-a2bc-1ea912141f81', 'sizes': [[300, 250]], 'bidId': '2aa73f571eaf29', 'bidderRequestId': '1bac84515a7af3', 'auctionId': '5dbc60fa-1aa1-41ce-9092-e6bbd4d478f7', 'src': 'client', 'bidRequestsCount': 1, 'pageurl': 'http://google.com'}]; - bidRequestsVid = [{'bidder': 'avng', 'params': {'pubid': '8537f00948fc37cc03c5f0f88e198a76', 'placement': 1234, 'video': {'id': 123, 'skip': 1, 'mimes': ['video/mp4', 'application/javascript'], 'playbackmethod': [2, 6], 'maxduration': 30}}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'video': {'playerSize': [[320, 480]], 'context': 'instream'}}, 'adUnitCode': 'video1', 'transactionId': '8b060952-93f7-4863-af44-bb8796b97c42', 'sizes': [], 'bidId': '25c6ab92aa0e81', 'bidderRequestId': '1d420b73a013fc', 'auctionId': '9a69741c-34fb-474c-83e1-cfa003aaee17', 'src': 'client', 'bidRequestsCount': 1}]; + bidRequestsVid = [{'bidder': 'avng', 'params': {'pubid': '8537f00948fc37cc03c5f0f88e198a76', 'placement': 1234, 'video': {'id': 123, 'skip': 1, 'mimes': ['video/mp4', 'application/javascript'], 'playbackmethod': [2, 6], 'maxduration': 30}}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'video': {'playerSize': [[320, 480]], 'context': 'instream'}}, 'adUnitCode': 'video1', 'transactionId': '8b060952-93f7-4863-af44-bb8796b97c42', 'sizes': [], 'bidId': '25c6ab92aa0e81', 'bidderRequestId': '1d420b73a013fc', 'auctionId': '9a69741c-34fb-474c-83e1-cfa003aaee17', 'src': 'client', 'bidRequestsCount': 1, 'pageurl': 'http://google.com'}]; }); describe('spec.isBidRequestValid', function () { @@ -60,7 +60,9 @@ describe('advangelistsBidAdapter', function () { it('should have domain in request', function () { const bidRequest = bidRequests[0]; const requests = spec.buildRequests([ bidRequest ]); - expect(Object.keys(requests[0].data.site.domain).length !== 0); + console.log(requests[0].data.site.domain); + // expect(Object.keys(requests[0].data.site.domain).length !== 0); + expect(requests[0].data.site.domain).length !== 0; }); }); From 4f5c4511bde1dd60ef2ae75d9fb570adfb98107c Mon Sep 17 00:00:00 2001 From: nakamoto Date: Thu, 28 Feb 2019 11:24:22 +0900 Subject: [PATCH 05/25] Correct procudtion endpoint for prebid --- modules/microadBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/microadBidAdapter.js b/modules/microadBidAdapter.js index 786469764bf..d42e4053fda 100644 --- a/modules/microadBidAdapter.js +++ b/modules/microadBidAdapter.js @@ -4,7 +4,7 @@ import { BANNER } from 'src/mediaTypes'; const BIDDER_CODE = 'microad'; const ENDPOINT_URLS = { - 'production': '//s-rtb.send.microad.jp/prebid', + 'production': '//s-rtb-pb.send.microad.jp/prebid', 'test': 'https://rtbtest.send.microad.jp/prebid' }; export let ENVIRONMENT = 'production'; From 3cc4c67ae5cd67aaf95155a4f27e0737264268e0 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Thu, 11 Jul 2019 13:29:33 -0700 Subject: [PATCH 06/25] analytics update with wrapper name --- modules/rubiconAnalyticsAdapter.js | 12 +++++++++++- test/spec/modules/rubiconAnalyticsSchema.json | 3 +++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 6c4e1b88d8b..23726dcd23f 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -27,6 +27,15 @@ config.getConfig('s2sConfig', ({s2sConfig}) => { serverConfig = s2sConfig; }); +let wrapperName = 'na'; +try { + if (window.document.currentScript.src) { + wrapperName = window.document.currentScript.src.split('/').slice(-1)[0].split('.')[0]; + } +} catch (e) { + utils.logError('Wrapper name could not be set'); +} + export const SEND_TIMEOUT = 3000; const INTEGRATION = 'pbjs'; @@ -141,7 +150,8 @@ function sendMessage(auctionId, bidWonId) { eventTimeMillis: Date.now(), integration: INTEGRATION, version: '$prebid.version$', - referrerUri: referrer + referrerUri: referrer, + wrapperName }; let auctionCache = cache.auctions[auctionId]; if (auctionCache && !auctionCache.sent) { diff --git a/test/spec/modules/rubiconAnalyticsSchema.json b/test/spec/modules/rubiconAnalyticsSchema.json index b856bf584e9..686aced840f 100644 --- a/test/spec/modules/rubiconAnalyticsSchema.json +++ b/test/spec/modules/rubiconAnalyticsSchema.json @@ -191,6 +191,9 @@ } ] } + }, + "wrapperName": { + "type": "string" } }, "definitions": { From 28848ad5e6665e86d02f9df7656f494435533d29 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Tue, 16 Jul 2019 16:31:24 -0700 Subject: [PATCH 07/25] reverted error merge --- modules/rubiconAnalyticsAdapter.js | 12 +----------- test/spec/modules/rubiconAnalyticsSchema.json | 3 --- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 3019aec0613..a00c727d470 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -27,15 +27,6 @@ config.getConfig('s2sConfig', ({s2sConfig}) => { serverConfig = s2sConfig; }); -let wrapperName = 'na'; -try { - if (window.document.currentScript.src) { - wrapperName = window.document.currentScript.src.split('/').slice(-1)[0].split('.')[0]; - } -} catch (e) { - utils.logError('Wrapper name could not be set'); -} - export const SEND_TIMEOUT = 3000; const INTEGRATION = 'pbjs'; @@ -150,8 +141,7 @@ function sendMessage(auctionId, bidWonId) { eventTimeMillis: Date.now(), integration: INTEGRATION, version: '$prebid.version$', - referrerUri: referrer, - wrapperName + referrerUri: referrer }; let auctionCache = cache.auctions[auctionId]; if (auctionCache && !auctionCache.sent) { diff --git a/test/spec/modules/rubiconAnalyticsSchema.json b/test/spec/modules/rubiconAnalyticsSchema.json index 686aced840f..b856bf584e9 100644 --- a/test/spec/modules/rubiconAnalyticsSchema.json +++ b/test/spec/modules/rubiconAnalyticsSchema.json @@ -191,9 +191,6 @@ } ] } - }, - "wrapperName": { - "type": "string" } }, "definitions": { From ca139524f2f74417391d98ef698f345cfedf7b3c Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Fri, 27 Sep 2019 10:22:22 -0700 Subject: [PATCH 08/25] update changed default value of netRevenue to true --- modules/rubiconBidAdapter.js | 2 +- test/spec/modules/rubiconBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index a1cdfdf8fea..492e283596c 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -484,7 +484,7 @@ export const spec = { cpm: bid.price || 0, bidderCode: seatbid.seat, ttl: 300, - netRevenue: config.getConfig('rubicon.netRevenue') || false, + netRevenue: config.getConfig('rubicon.netRevenue') || true, width: bid.w || utils.deepAccess(bidRequest, 'mediaTypes.video.w') || utils.deepAccess(bidRequest, 'params.video.playerWidth'), height: bid.h || utils.deepAccess(bidRequest, 'mediaTypes.video.h') || utils.deepAccess(bidRequest, 'params.video.playerHeight'), }; diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 8d65e1e97b4..d31b83fd923 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -2039,7 +2039,7 @@ describe('the rubicon adapter', function () { expect(bids[0].creativeId).to.equal('4259970'); expect(bids[0].cpm).to.equal(2); expect(bids[0].ttl).to.equal(300); - expect(bids[0].netRevenue).to.equal(false); + expect(bids[0].netRevenue).to.equal(true); expect(bids[0].adserverTargeting).to.deep.equal({hb_uuid: '0c498f63-5111-4bed-98e2-9be7cb932a64'}); expect(bids[0].mediaType).to.equal('video'); expect(bids[0].bidderCode).to.equal('rubicon'); From e4b034444804aa06c3641fd3ab8bd6dbfa00b747 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Wed, 20 Nov 2019 11:46:18 -0800 Subject: [PATCH 09/25] add beforeRequestBids event --- src/constants.json | 1 + src/prebid.js | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/constants.json b/src/constants.json index 99e2bd792b9..f75b22aaa7e 100644 --- a/src/constants.json +++ b/src/constants.json @@ -33,6 +33,7 @@ "BID_WON": "bidWon", "BIDDER_DONE": "bidderDone", "SET_TARGETING": "setTargeting", + "BEFORE_REQUEST_BIDS": "beforeRequestBids", "REQUEST_BIDS": "requestBids", "ADD_AD_UNITS": "addAdUnits", "AD_RENDER_FAILED" : "adRenderFailed" diff --git a/src/prebid.js b/src/prebid.js index 44649d7d444..e97867f585c 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -23,7 +23,7 @@ const events = require('./events'); const { triggerUserSyncs } = userSync; /* private variables */ -const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, SET_TARGETING, AD_RENDER_FAILED } = CONSTANTS.EVENTS; +const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, BEFORE_REQUEST_BIDS, SET_TARGETING, AD_RENDER_FAILED } = CONSTANTS.EVENTS; const { PREVENT_WRITING_ON_MAIN_DOCUMENT, NO_AD, EXCEPTION, CANNOT_FIND_AD, MISSING_DOC_OR_ADID } = CONSTANTS.AD_RENDER_FAILED_REASON; const eventValidators = { @@ -473,6 +473,10 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo } return; } + + // https://github.com/prebid/Prebid.js/issues/4149 + // this event will be fired after conditional adunit processing but before adunits are copied to bidrequests. It will pass in the array of adunits and allow the page function to alter the contents. + events.emit(BEFORE_REQUEST_BIDS, adUnits); const auction = auctionManager.createAuction({adUnits, adUnitCodes, callback: bidsBackHandler, cbTimeout, labels, auctionId}); From e52714d2cd550f38913f5fd9b1195dc3683bd42f Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Thu, 21 Nov 2019 18:37:58 -0800 Subject: [PATCH 10/25] add pbAdSlot support to pbs adapter --- modules/prebidServerBidAdapter/index.js | 10 ++++++++++ src/prebid.js | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index a6061b4bad4..64655689197 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -630,6 +630,16 @@ const OPEN_RTB_PROTOCOL = { return acc; }, {}); + /** + * Prebid AdSlot is passed to the oRTB request at imp[].ext.context.data.adslot + * @type {(string|undefined)} + * @see {@link https://github.com/prebid/Prebid.js/issues/4149|Issue} + */ + const pbAdSlot = utils.deepAccess(adUnit, 'context.pbAdSlot'); + if (typeof pbAdSlot === 'string' && pbAdSlot) { + utils.deepSetValue(ext, 'context.data.adslot', pbAdSlot); + } + const imp = { id: adUnit.code, ext, secure: _s2sConfig.secure }; Object.assign(imp, mediaTypes); diff --git a/src/prebid.js b/src/prebid.js index e97867f585c..2c16f5cdb56 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -473,7 +473,7 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo } return; } - + // https://github.com/prebid/Prebid.js/issues/4149 // this event will be fired after conditional adunit processing but before adunits are copied to bidrequests. It will pass in the array of adunits and allow the page function to alter the contents. events.emit(BEFORE_REQUEST_BIDS, adUnits); From 887971ac62fe842f0cfb471caf8cf842c33f4e55 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Thu, 21 Nov 2019 18:51:37 -0800 Subject: [PATCH 11/25] update jsdoc comment for on before request bids event --- src/prebid.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/prebid.js b/src/prebid.js index 2c16f5cdb56..bd4ebb2d993 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -474,8 +474,10 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo return; } - // https://github.com/prebid/Prebid.js/issues/4149 - // this event will be fired after conditional adunit processing but before adunits are copied to bidrequests. It will pass in the array of adunits and allow the page function to alter the contents. + /** + * The event "BEFORE_REQUEST_BIDS" is fired after conditional adunit processing but before adunits are copied to bidrequests. Note the adunits array is passed to the event handler. + * @see {@link https://github.com/prebid/Prebid.js/issues/4149|Issue} + */ events.emit(BEFORE_REQUEST_BIDS, adUnits); const auction = auctionManager.createAuction({adUnits, adUnitCodes, callback: bidsBackHandler, cbTimeout, labels, auctionId}); From ff646dfd428f7718cb1ab95b676953c3a0fe8cb4 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Thu, 21 Nov 2019 20:50:36 -0800 Subject: [PATCH 12/25] add pbAdSlot support to rubicon adapter --- modules/rubiconBidAdapter.js | 10 ++++++ test/spec/modules/rubiconBidAdapter_spec.js | 34 +++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 13d41978027..271ab1bbe89 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -449,6 +449,16 @@ export const spec = { // For SRA we need to explicitly put empty semi colons so AE treats it as empty, instead of copying the latter value data['p_pos'] = (params.position === 'atf' || params.position === 'btf') ? params.position : ''; + /** + * Prebid AdSlot is passed as tg_i.dfp_ad_unit_code + * @type {(string|undefined)} + * @see {@link https://github.com/prebid/Prebid.js/issues/4149|Issue} + */ + const pbAdSlot = utils.deepAccess(bidRequest, 'context.pbAdSlot'); + if (typeof pbAdSlot === 'string' && pbAdSlot) { + data['tg_i.dfp_ad_unit_code'] = pbAdSlot.replace(/^\/+/, ''); + } + if (bidRequest.userId) { if (bidRequest.userId.tdid) { data['tpid_tdid'] = bidRequest.userId.tdid; diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 68abb7ffb77..11b749b974f 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1238,6 +1238,40 @@ describe('the rubicon adapter', function () { }); }); }) + + describe('Prebid AdSlot config', function () { + it('should not send pbAdSlot if undefined', function () { + const clonedBid = utils.deepClone(bidderRequest.bids[0]); + clonedBid.context = {}; + let [request] = spec.buildRequests([clonedBid], bidderRequest); + let data = parseQuery(request.data); + expect(typeof data['tg_i.dfp_ad_unit_code']).to.equal('undefined'); + }); + + it('should not send pbAdSlot if value is an empty string', function () { + const clonedBid = utils.deepClone(bidderRequest.bids[0]); + clonedBid.context = { pbAdSlot: '' }; + let [request] = spec.buildRequests([clonedBid], bidderRequest); + let data = parseQuery(request.data); + expect(typeof data['tg_i.dfp_ad_unit_code']).to.equal('undefined'); + }); + + it('should send pbAdSlot if value is a non-empty string', function () { + const clonedBid = utils.deepClone(bidderRequest.bids[0]); + clonedBid.context = { pbAdSlot: 'abc' }; + let [request] = spec.buildRequests([clonedBid], bidderRequest); + let data = parseQuery(request.data); + expect(data['tg_i.dfp_ad_unit_code']).to.equal('abc'); + }); + + it('should send pbAdSlot removing a leading slash', function () { + const clonedBid = utils.deepClone(bidderRequest.bids[0]); + clonedBid.context = { pbAdSlot: '/a/b/c' }; + let [request] = spec.buildRequests([clonedBid], bidderRequest); + let data = parseQuery(request.data); + expect(data['tg_i.dfp_ad_unit_code']).to.equal('a/b/c'); + }); + }); }); describe('for video requests', function () { From 5e4c07864b3264189dddb1d36e8a6f25761f7b8e Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Fri, 22 Nov 2019 04:29:27 -0800 Subject: [PATCH 13/25] added more tests --- modules/prebidServerBidAdapter/index.js | 9 +-- modules/rubiconBidAdapter.js | 19 +++-- .../modules/prebidServerBidAdapter_spec.js | 70 +++++++++++++++++ test/spec/modules/rubiconBidAdapter_spec.js | 75 +++++++++++++------ 4 files changed, 135 insertions(+), 38 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 64655689197..5d75044084d 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -630,18 +630,17 @@ const OPEN_RTB_PROTOCOL = { return acc; }, {}); + const imp = { id: adUnit.code, ext, secure: _s2sConfig.secure }; + /** - * Prebid AdSlot is passed to the oRTB request at imp[].ext.context.data.adslot + * Prebid AdSlot * @type {(string|undefined)} - * @see {@link https://github.com/prebid/Prebid.js/issues/4149|Issue} */ const pbAdSlot = utils.deepAccess(adUnit, 'context.pbAdSlot'); if (typeof pbAdSlot === 'string' && pbAdSlot) { - utils.deepSetValue(ext, 'context.data.adslot', pbAdSlot); + utils.deepSetValue(imp, 'ext.context.data.adslot', pbAdSlot); } - const imp = { id: adUnit.code, ext, secure: _s2sConfig.secure }; - Object.assign(imp, mediaTypes); if (imp.banner || imp.video || imp.native) { diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 271ab1bbe89..e40bf905d51 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -449,16 +449,6 @@ export const spec = { // For SRA we need to explicitly put empty semi colons so AE treats it as empty, instead of copying the latter value data['p_pos'] = (params.position === 'atf' || params.position === 'btf') ? params.position : ''; - /** - * Prebid AdSlot is passed as tg_i.dfp_ad_unit_code - * @type {(string|undefined)} - * @see {@link https://github.com/prebid/Prebid.js/issues/4149|Issue} - */ - const pbAdSlot = utils.deepAccess(bidRequest, 'context.pbAdSlot'); - if (typeof pbAdSlot === 'string' && pbAdSlot) { - data['tg_i.dfp_ad_unit_code'] = pbAdSlot.replace(/^\/+/, ''); - } - if (bidRequest.userId) { if (bidRequest.userId.tdid) { data['tpid_tdid'] = bidRequest.userId.tdid; @@ -499,6 +489,15 @@ export const spec = { }); } + /** + * Prebid AdSlot + * @type {(string|undefined)} + */ + const pbAdSlot = utils.deepAccess(bidRequest, 'context.pbAdSlot'); + if (typeof pbAdSlot === 'string' && pbAdSlot) { + data['tg_i.dfp_ad_unit_code'] = pbAdSlot.replace(/^\/+/, ''); + } + // digitrust properties const digitrustParams = _getDigiTrustQueryParams(bidRequest, 'FASTLANE'); Object.assign(data, digitrustParams); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 76860a09ee2..7d1b88b9a16 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1311,6 +1311,76 @@ describe('S2S Adapter', function () { const parsedRequestBody = JSON.parse(requests[0].requestBody); expect(parsedRequestBody.source.ext.schain).to.deep.equal(schainObject); }) + + describe('pbAdSlot config', function () { + it('should not send \"imp.ext.context.data.adslot\" if \"context\" is undefined', function () { + const ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + const consentConfig = { s2sConfig: ortb2Config }; + config.setConfig(consentConfig); + const bidRequest = utils.deepClone(REQUEST); + + adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); + const parsedRequestBody = JSON.parse(requests[0].requestBody); + + expect(parsedRequestBody.imp).to.be.a('array'); + expect(parsedRequestBody.imp[0]).to.be.a('object'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); + }); + + it('should not send \"imp.ext.context.data.adslot\" if \"context.pbAdSlot\" is undefined', function () { + const ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + const consentConfig = { s2sConfig: ortb2Config }; + config.setConfig(consentConfig); + const bidRequest = utils.deepClone(REQUEST); + bidRequest.ad_units[0].context = {}; + + adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); + const parsedRequestBody = JSON.parse(requests[0].requestBody); + + expect(parsedRequestBody.imp).to.be.a('array'); + expect(parsedRequestBody.imp[0]).to.be.a('object'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); + }); + + it('should not send \"imp.ext.context.data.adslot\" if \"context.pbAdSlot\" is empty string', function () { + const ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + const consentConfig = { s2sConfig: ortb2Config }; + config.setConfig(consentConfig); + const bidRequest = utils.deepClone(REQUEST); + bidRequest.ad_units[0].context = { + pbAdSlot: '' + }; + + adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); + const parsedRequestBody = JSON.parse(requests[0].requestBody); + + expect(parsedRequestBody.imp).to.be.a('array'); + expect(parsedRequestBody.imp[0]).to.be.a('object'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); + }); + + it('should send \"imp.ext.context.data.adslot\" if \"context.pbAdSlot\" value is a non-empty string', function () { + const ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + const consentConfig = { s2sConfig: ortb2Config }; + config.setConfig(consentConfig); + const bidRequest = utils.deepClone(REQUEST); + bidRequest.ad_units[0].context = { + pbAdSlot: '/a/b/c' + }; + + adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); + const parsedRequestBody = JSON.parse(requests[0].requestBody); + + expect(parsedRequestBody.imp).to.be.a('array'); + expect(parsedRequestBody.imp[0]).to.be.a('object'); + expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.context.data.adslot'); + expect(parsedRequestBody.imp[0].ext.context.data.adslot).to.equal('/a/b/c'); + }); + }); }); describe('response handler', function () { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 11b749b974f..086519e4253 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1239,36 +1239,65 @@ describe('the rubicon adapter', function () { }); }) - describe('Prebid AdSlot config', function () { - it('should not send pbAdSlot if undefined', function () { - const clonedBid = utils.deepClone(bidderRequest.bids[0]); - clonedBid.context = {}; - let [request] = spec.buildRequests([clonedBid], bidderRequest); - let data = parseQuery(request.data); - expect(typeof data['tg_i.dfp_ad_unit_code']).to.equal('undefined'); + describe('Prebid AdSlot', function () { + beforeEach(function () { + // enforce that the bid at 0 does not have a 'context' property + if (bidderRequest.bids[0].hasOwnProperty('context')) { + delete bidderRequest.bids[0].context; + } }); - it('should not send pbAdSlot if value is an empty string', function () { - const clonedBid = utils.deepClone(bidderRequest.bids[0]); - clonedBid.context = { pbAdSlot: '' }; - let [request] = spec.buildRequests([clonedBid], bidderRequest); - let data = parseQuery(request.data); - expect(typeof data['tg_i.dfp_ad_unit_code']).to.equal('undefined'); + it('should not send \"tg_i.dfp_ad_unit_code\" if \"context\" object is not valid', function () { + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const data = parseQuery(request.data); + + expect(data).to.be.an('Object'); + expect(data).to.not.have.property('tg_i.dfp_ad_unit_code'); }); - it('should send pbAdSlot if value is a non-empty string', function () { - const clonedBid = utils.deepClone(bidderRequest.bids[0]); - clonedBid.context = { pbAdSlot: 'abc' }; - let [request] = spec.buildRequests([clonedBid], bidderRequest); - let data = parseQuery(request.data); + it('should not send \"tg_i.dfp_ad_unit_code\" if \"context.pbAdSlot\" is undefined', function () { + bidderRequest.bids[0].context = {}; + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const data = parseQuery(request.data); + + expect(data).to.be.an('Object'); + expect(data).to.not.have.property('tg_i.dfp_ad_unit_code'); + }); + + it('should not send \"tg_i.dfp_ad_unit_code\" if \"context.pbAdSlot\" value is an empty string', function () { + bidderRequest.bids[0].context = { + pbAdSlot: '' + }; + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const data = parseQuery(request.data); + + expect(data).to.be.an('Object'); + expect(data).to.not.have.property('tg_i.dfp_ad_unit_code'); + }); + + it('should send \"tg_i.dfp_ad_unit_code\" if \"context.pbAdSlot\" value is a valid string', function () { + bidderRequest.bids[0].context = { + pbAdSlot: 'abc' + }; + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const data = parseQuery(request.data); + + expect(data).to.be.an('Object'); + expect(data).to.have.property('tg_i.dfp_ad_unit_code'); expect(data['tg_i.dfp_ad_unit_code']).to.equal('abc'); }); - it('should send pbAdSlot removing a leading slash', function () { - const clonedBid = utils.deepClone(bidderRequest.bids[0]); - clonedBid.context = { pbAdSlot: '/a/b/c' }; - let [request] = spec.buildRequests([clonedBid], bidderRequest); - let data = parseQuery(request.data); + it('should send \"tg_i.dfp_ad_unit_code\" if \"context.pbAdSlot\" value is a valid string, but all leading slash characters should be removed', function () { + bidderRequest.bids[0].context = { pbAdSlot: '/a/b/c' }; + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const data = parseQuery(request.data); + + expect(data).to.be.an('Object'); + expect(data).to.have.property('tg_i.dfp_ad_unit_code'); expect(data['tg_i.dfp_ad_unit_code']).to.equal('a/b/c'); }); }); From 6ca2d59f0dd2b48867162fb77fb6acbb4a008e2a Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Fri, 22 Nov 2019 04:43:12 -0800 Subject: [PATCH 14/25] update code comment --- src/prebid.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prebid.js b/src/prebid.js index bd4ebb2d993..11a354a5818 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -475,7 +475,7 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo } /** - * The event "BEFORE_REQUEST_BIDS" is fired after conditional adunit processing but before adunits are copied to bidrequests. Note the adunits array is passed to the event handler. + * emit and send adunits for modification from within the handler function * @see {@link https://github.com/prebid/Prebid.js/issues/4149|Issue} */ events.emit(BEFORE_REQUEST_BIDS, adUnits); From b97e2b9a7c99d92cf69118b93343b57303b83567 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Sun, 24 Nov 2019 10:49:05 -0800 Subject: [PATCH 15/25] add api test, update event to emit from adapterManager --- src/adapterManager.js | 7 ++ src/prebid.js | 8 +-- test/spec/unit/pbjs_api_spec.js | 113 ++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 7 deletions(-) diff --git a/src/adapterManager.js b/src/adapterManager.js index fa7b7cbb58d..31476ab58c3 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -65,6 +65,7 @@ function getBids({bidderCode, auctionId, bidderRequestId, adUnits, labels, src}) } bid = Object.assign({}, bid, getDefinedParams(adUnit, [ + 'context', 'mediaType', 'renderer' ])); @@ -161,6 +162,12 @@ export let gdprDataHandler = { }; adapterManager.makeBidRequests = function(adUnits, auctionStart, auctionId, cbTimeout, labels) { + /** + * emit and pass adunits for external modification + * @see {@link https://github.com/prebid/Prebid.js/issues/4149|Issue} + */ + events.emit(CONSTANTS.EVENTS.BEFORE_REQUEST_BIDS, adUnits); + let bidRequests = []; let bidderCodes = getBidderCodes(adUnits); diff --git a/src/prebid.js b/src/prebid.js index 11a354a5818..44649d7d444 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -23,7 +23,7 @@ const events = require('./events'); const { triggerUserSyncs } = userSync; /* private variables */ -const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, BEFORE_REQUEST_BIDS, SET_TARGETING, AD_RENDER_FAILED } = CONSTANTS.EVENTS; +const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, SET_TARGETING, AD_RENDER_FAILED } = CONSTANTS.EVENTS; const { PREVENT_WRITING_ON_MAIN_DOCUMENT, NO_AD, EXCEPTION, CANNOT_FIND_AD, MISSING_DOC_OR_ADID } = CONSTANTS.AD_RENDER_FAILED_REASON; const eventValidators = { @@ -474,12 +474,6 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo return; } - /** - * emit and send adunits for modification from within the handler function - * @see {@link https://github.com/prebid/Prebid.js/issues/4149|Issue} - */ - events.emit(BEFORE_REQUEST_BIDS, adUnits); - const auction = auctionManager.createAuction({adUnits, adUnitCodes, callback: bidsBackHandler, cbTimeout, labels, auctionId}); let adUnitsLen = adUnits.length; diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 485dd5cf077..7154bdd793d 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -1972,6 +1972,119 @@ describe('Unit: Prebid Module', function () { assert.ok(spyEventsOn.calledWith('bidWon', Function)); events.on.restore(); }); + + describe('beforeRequestBids', function () { + it('should allow creation of a context.pbAdSlot property on adUnits from inside the event handler', function () { + // verify adUnits passed to handler then alter the adUnits + const beforeRequestBidsHandler = function beforeRequestBidsHandler(beforeRequestBidsAdUnits) { + expect(beforeRequestBidsAdUnits).to.be.a('array'); + expect(beforeRequestBidsAdUnits).to.have.lengthOf(1); + expect(beforeRequestBidsAdUnits[0]).to.be.a('object'); + // adUnit should not contain a context property yet + expect(beforeRequestBidsAdUnits[0]).to.not.have.property('context') + // alter the adUnit by adding the property for context.pbAdSlot + beforeRequestBidsAdUnits[0].context = { + pbAdSlot: 'foobar' + }; + }; + const beforeRequestBidsSpy = sinon.spy(beforeRequestBidsHandler); + + // use this handler to verify if the adUnits alterations were applied successfully by the beforeRequestBids handler + const bidRequestedHandler = function bidRequestedHandler(bidRequest) { + expect(bidRequest).to.be.a('object'); + expect(bidRequest).to.have.property('bids'); + expect(bidRequest.bids).to.be.a('array'); + expect(bidRequest.bids).to.have.lengthOf(1); + const bid = bidRequest['bids'][0]; + expect(bid).to.be.a('object'); + expect(bid).to.have.property('context'); + expect(bid.context).to.be.a('object'); + expect(bid.context).to.have.property('pbAdSlot'); + expect(bid.context.pbAdSlot).to.equal('foobar'); + }; + const bidRequestedSpy = sinon.spy(bidRequestedHandler); + + const bidsBackHandler = function bidsBackHandler() {}; + const bidsBackSpy = sinon.spy(bidsBackHandler); + + googletag.pubads().setSlots(createSlotArrayScenario2()); + + $$PREBID_GLOBAL$$.onEvent('beforeRequestBids', beforeRequestBidsSpy); + $$PREBID_GLOBAL$$.onEvent('bidRequested', bidRequestedSpy); + $$PREBID_GLOBAL$$.requestBids({ + adUnits: [{ + code: '/19968336/header-bid-tag-0', + mediaTypes: { + banner: { + sizes: [[750, 350]] + } + }, + bids: [{ + bidder: 'appnexus', + params: { + placementId: 13122370 + } + }] + }], + bidsBackHandler: bidsBackSpy + }); + + sinon.assert.calledOnce(beforeRequestBidsSpy); + sinon.assert.calledOnce(bidRequestedSpy); + }); + + it('should not create a context property on adUnits if not added by handler', function () { + // verify adUnits passed to handler then alter the adUnits + const beforeRequestBidsHandler = function beforeRequestBidsHandler(beforeRequestBidsAdUnits) { + expect(beforeRequestBidsAdUnits).to.be.a('array'); + expect(beforeRequestBidsAdUnits).to.have.lengthOf(1); + expect(beforeRequestBidsAdUnits[0]).to.be.a('object'); + // adUnit should not contain a context property yet + expect(beforeRequestBidsAdUnits[0]).to.not.have.property('context') + }; + const beforeRequestBidsSpy = sinon.spy(beforeRequestBidsHandler); + + // use this handler to verify if the adUnits alterations were applied successfully by the beforeRequestBids handler + const bidRequestedHandler = function bidRequestedHandler(bidRequest) { + expect(bidRequest).to.be.a('object'); + expect(bidRequest).to.have.property('bids'); + expect(bidRequest.bids).to.be.a('array'); + expect(bidRequest.bids).to.have.lengthOf(1); + const bid = bidRequest['bids'][0]; + expect(bid).to.be.a('object'); + expect(bid).to.not.have.property('context'); + }; + const bidRequestedSpy = sinon.spy(bidRequestedHandler); + + const bidsBackHandler = function bidsBackHandler() {}; + const bidsBackSpy = sinon.spy(bidsBackHandler); + + googletag.pubads().setSlots(createSlotArrayScenario2()); + + $$PREBID_GLOBAL$$.onEvent('beforeRequestBids', beforeRequestBidsSpy); + $$PREBID_GLOBAL$$.onEvent('bidRequested', bidRequestedSpy); + $$PREBID_GLOBAL$$.requestBids({ + adUnits: [{ + code: '/19968336/header-bid-tag-0', + mediaTypes: { + banner: { + sizes: [[750, 350]] + } + }, + bids: [{ + bidder: 'appnexus', + params: { + placementId: 13122370 + } + }] + }], + bidsBackHandler: bidsBackSpy + }); + + sinon.assert.calledOnce(beforeRequestBidsSpy); + sinon.assert.calledOnce(bidRequestedSpy); + }); + }); }); describe('offEvent', function () { From 8a89f4d9441ebbb57345746dd5f6087e338abe0f Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Sun, 24 Nov 2019 11:47:37 -0800 Subject: [PATCH 16/25] add multi-bid api test --- test/spec/unit/pbjs_api_spec.js | 133 +++++++++++++++++++++++++++----- 1 file changed, 115 insertions(+), 18 deletions(-) diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 7154bdd793d..012a9863f27 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -1974,9 +1974,37 @@ describe('Unit: Prebid Module', function () { }); describe('beforeRequestBids', function () { + let bidRequestedHandler; + let beforeRequestBidsHandler; + let bidsBackHandler = function bidsBackHandler() {}; + + let bidsBackSpy; + let bidRequestedSpy; + let beforeRequestBidsSpy; + + beforeEach(function () { + resetAuction(); + bidsBackSpy = sinon.spy(bidsBackHandler); + googletag.pubads().setSlots(createSlotArrayScenario2()); + }); + + afterEach(function () { + bidsBackSpy.resetHistory(); + + if (bidRequestedSpy) { + $$PREBID_GLOBAL$$.offEvent('bidRequested', bidRequestedSpy); + bidRequestedSpy.resetHistory(); + } + + if (beforeRequestBidsSpy) { + $$PREBID_GLOBAL$$.offEvent('beforeRequestBids', beforeRequestBidsSpy); + beforeRequestBidsSpy.resetHistory(); + } + }); + it('should allow creation of a context.pbAdSlot property on adUnits from inside the event handler', function () { // verify adUnits passed to handler then alter the adUnits - const beforeRequestBidsHandler = function beforeRequestBidsHandler(beforeRequestBidsAdUnits) { + beforeRequestBidsHandler = function beforeRequestBidsHandler(beforeRequestBidsAdUnits) { expect(beforeRequestBidsAdUnits).to.be.a('array'); expect(beforeRequestBidsAdUnits).to.have.lengthOf(1); expect(beforeRequestBidsAdUnits[0]).to.be.a('object'); @@ -1984,13 +2012,13 @@ describe('Unit: Prebid Module', function () { expect(beforeRequestBidsAdUnits[0]).to.not.have.property('context') // alter the adUnit by adding the property for context.pbAdSlot beforeRequestBidsAdUnits[0].context = { - pbAdSlot: 'foobar' + pbAdSlot: '/19968336/header-bid-tag-pbadslot-0' }; }; - const beforeRequestBidsSpy = sinon.spy(beforeRequestBidsHandler); + beforeRequestBidsSpy = sinon.spy(beforeRequestBidsHandler); // use this handler to verify if the adUnits alterations were applied successfully by the beforeRequestBids handler - const bidRequestedHandler = function bidRequestedHandler(bidRequest) { + bidRequestedHandler = function bidRequestedHandler(bidRequest) { expect(bidRequest).to.be.a('object'); expect(bidRequest).to.have.property('bids'); expect(bidRequest.bids).to.be.a('array'); @@ -2000,14 +2028,75 @@ describe('Unit: Prebid Module', function () { expect(bid).to.have.property('context'); expect(bid.context).to.be.a('object'); expect(bid.context).to.have.property('pbAdSlot'); - expect(bid.context.pbAdSlot).to.equal('foobar'); + expect(bid.context.pbAdSlot).to.equal('/19968336/header-bid-tag-pbadslot-0'); }; - const bidRequestedSpy = sinon.spy(bidRequestedHandler); + bidRequestedSpy = sinon.spy(bidRequestedHandler); - const bidsBackHandler = function bidsBackHandler() {}; - const bidsBackSpy = sinon.spy(bidsBackHandler); + $$PREBID_GLOBAL$$.onEvent('beforeRequestBids', beforeRequestBidsSpy); + $$PREBID_GLOBAL$$.onEvent('bidRequested', bidRequestedSpy); + $$PREBID_GLOBAL$$.requestBids({ + adUnits: [{ + code: '/19968336/header-bid-tag-0', + mediaTypes: { + banner: { + sizes: [[750, 350]] + } + }, + bids: [{ + bidder: 'appnexus', + params: { + placementId: 13122370 + } + }] + }], + bidsBackHandler: bidsBackSpy + }); - googletag.pubads().setSlots(createSlotArrayScenario2()); + sinon.assert.calledOnce(beforeRequestBidsSpy); + sinon.assert.calledOnce(bidRequestedSpy); + }); + + it('should allow creation of a context.pbAdSlot property on adUnits from inside the event handler', function () { + // verify adUnits passed to handler then alter the adUnits + beforeRequestBidsHandler = function beforeRequestBidsHandler(beforeRequestBidsAdUnits) { + expect(beforeRequestBidsAdUnits).to.be.a('array'); + expect(beforeRequestBidsAdUnits).to.have.lengthOf(2); + expect(beforeRequestBidsAdUnits[0]).to.be.a('object'); + expect(beforeRequestBidsAdUnits[1]).to.be.a('object'); + // adUnit should not contain a context property yet + expect(beforeRequestBidsAdUnits[0]).to.not.have.property('context') + expect(beforeRequestBidsAdUnits[1]).to.not.have.property('context') + // alter the adUnit by adding the property for context.pbAdSlot + beforeRequestBidsAdUnits[0].context = { + pbAdSlot: '/19968336/header-bid-tag-pbadslot-0' + }; + beforeRequestBidsAdUnits[1].context = { + pbAdSlot: '/19968336/header-bid-tag-pbadslot-1' + }; + }; + beforeRequestBidsSpy = sinon.spy(beforeRequestBidsHandler); + + // use this handler to verify if the adUnits alterations were applied successfully by the beforeRequestBids handler + bidRequestedHandler = function bidRequestedHandler(bidRequest) { + expect(bidRequest).to.be.a('object'); + expect(bidRequest).to.have.property('bids'); + expect(bidRequest.bids).to.be.a('array'); + expect(bidRequest.bids).to.have.lengthOf(2); + const bid0 = bidRequest['bids'][0]; + expect(bid0).to.be.a('object'); + expect(bid0).to.have.property('context'); + expect(bid0.context).to.be.a('object'); + expect(bid0.context).to.have.property('pbAdSlot'); + expect(bid0.context.pbAdSlot).to.equal('/19968336/header-bid-tag-pbadslot-0'); + + const bid1 = bidRequest['bids'][1]; + expect(bid1).to.be.a('object'); + expect(bid1).to.have.property('context'); + expect(bid1.context).to.be.a('object'); + expect(bid1.context).to.have.property('pbAdSlot'); + expect(bid1.context.pbAdSlot).to.equal('/19968336/header-bid-tag-pbadslot-1'); + }; + bidRequestedSpy = sinon.spy(bidRequestedHandler); $$PREBID_GLOBAL$$.onEvent('beforeRequestBids', beforeRequestBidsSpy); $$PREBID_GLOBAL$$.onEvent('bidRequested', bidRequestedSpy); @@ -2025,6 +2114,19 @@ describe('Unit: Prebid Module', function () { placementId: 13122370 } }] + }, { + code: '/19968336/header-bid-tag-1', + mediaTypes: { + banner: { + sizes: [[750, 350]] + } + }, + bids: [{ + bidder: 'appnexus', + params: { + placementId: 14122380 + } + }] }], bidsBackHandler: bidsBackSpy }); @@ -2035,17 +2137,17 @@ describe('Unit: Prebid Module', function () { it('should not create a context property on adUnits if not added by handler', function () { // verify adUnits passed to handler then alter the adUnits - const beforeRequestBidsHandler = function beforeRequestBidsHandler(beforeRequestBidsAdUnits) { + beforeRequestBidsHandler = function beforeRequestBidsHandler(beforeRequestBidsAdUnits) { expect(beforeRequestBidsAdUnits).to.be.a('array'); expect(beforeRequestBidsAdUnits).to.have.lengthOf(1); expect(beforeRequestBidsAdUnits[0]).to.be.a('object'); // adUnit should not contain a context property yet expect(beforeRequestBidsAdUnits[0]).to.not.have.property('context') }; - const beforeRequestBidsSpy = sinon.spy(beforeRequestBidsHandler); + beforeRequestBidsSpy = sinon.spy(beforeRequestBidsHandler); // use this handler to verify if the adUnits alterations were applied successfully by the beforeRequestBids handler - const bidRequestedHandler = function bidRequestedHandler(bidRequest) { + bidRequestedHandler = function bidRequestedHandler(bidRequest) { expect(bidRequest).to.be.a('object'); expect(bidRequest).to.have.property('bids'); expect(bidRequest.bids).to.be.a('array'); @@ -2054,12 +2156,7 @@ describe('Unit: Prebid Module', function () { expect(bid).to.be.a('object'); expect(bid).to.not.have.property('context'); }; - const bidRequestedSpy = sinon.spy(bidRequestedHandler); - - const bidsBackHandler = function bidsBackHandler() {}; - const bidsBackSpy = sinon.spy(bidsBackHandler); - - googletag.pubads().setSlots(createSlotArrayScenario2()); + bidRequestedSpy = sinon.spy(bidRequestedHandler); $$PREBID_GLOBAL$$.onEvent('beforeRequestBids', beforeRequestBidsSpy); $$PREBID_GLOBAL$$.onEvent('bidRequested', bidRequestedSpy); From 5375b6f03af69ebd7d49cb42021ccf48d483bdc2 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Fri, 13 Dec 2019 14:53:56 -0800 Subject: [PATCH 17/25] added pbAdSlot to rubicon bid adapter video bid request --- modules/rubiconBidAdapter.js | 9 +++++++++ test/spec/modules/rubiconBidAdapter_spec.js | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 4ad5cf44e8d..fe962976f4e 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -267,6 +267,15 @@ export const spec = { utils.deepSetValue(data, 'source.ext.schain', bidRequest.schain); } + /** + * Prebid AdSlot + * @type {(string|undefined)} + */ + const pbAdSlot = utils.deepAccess(bidRequest, 'context.pbAdSlot'); + if (typeof pbAdSlot === 'string' && pbAdSlot) { + utils.deepSetValue(data.imp[0], 'context.data.adslot', pbAdSlot); + } + return { method: 'POST', url: VIDEO_ENDPOINT, diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index ad75c0cdc66..c419a43e91c 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1631,6 +1631,20 @@ describe('the rubicon adapter', function () { const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(request.data.regs.coppa).to.equal(1); }); + + it('should include pbAdSlot in bid request', function () { + createVideoBidderRequest(); + bidderRequest.bids[0].context = { + pbAdSlot: '1234567890' + } + + sandbox.stub(Date, 'now').callsFake(() => + bidderRequest.auctionStart + 100 + ); + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.imp[0].context.data.adslot).to.equal('1234567890'); + }); }); describe('combineSlotUrlParams', function () { From 727f517ffa1a2f7bf16b9d7a7a45db311c2ea284 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Fri, 13 Dec 2019 15:44:45 -0800 Subject: [PATCH 18/25] added ext to server video path in rp adapter pbAdSlot --- modules/rubiconBidAdapter.js | 2 +- test/spec/modules/rubiconBidAdapter_spec.js | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index fe962976f4e..f8062965af6 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -273,7 +273,7 @@ export const spec = { */ const pbAdSlot = utils.deepAccess(bidRequest, 'context.pbAdSlot'); if (typeof pbAdSlot === 'string' && pbAdSlot) { - utils.deepSetValue(data.imp[0], 'context.data.adslot', pbAdSlot); + utils.deepSetValue(data.imp[0], 'ext.context.data.adslot', pbAdSlot); } return { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index c419a43e91c..d48cff78170 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1634,10 +1634,7 @@ describe('the rubicon adapter', function () { it('should include pbAdSlot in bid request', function () { createVideoBidderRequest(); - bidderRequest.bids[0].context = { - pbAdSlot: '1234567890' - } - + utils.deepSetValue(bidderRequest.bids[0], 'ext.context.pbAdSlot', '1234567890 '); sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 ); From 3abfe1a07985dae00c5a8b277f7d020c2ae28c18 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Fri, 13 Dec 2019 15:55:56 -0800 Subject: [PATCH 19/25] fix for pb video ortb path --- modules/rubiconBidAdapter.js | 2 +- test/spec/modules/rubiconBidAdapter_spec.js | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index f8062965af6..b0fa51460e5 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -273,7 +273,7 @@ export const spec = { */ const pbAdSlot = utils.deepAccess(bidRequest, 'context.pbAdSlot'); if (typeof pbAdSlot === 'string' && pbAdSlot) { - utils.deepSetValue(data.imp[0], 'ext.context.data.adslot', pbAdSlot); + utils.deepSetValue(data.imp[0].ext, 'context.data.adslot', pbAdSlot.replace(/^\/+/, '')); } return { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index d48cff78170..ed0e60ef22c 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1634,13 +1634,15 @@ describe('the rubicon adapter', function () { it('should include pbAdSlot in bid request', function () { createVideoBidderRequest(); - utils.deepSetValue(bidderRequest.bids[0], 'ext.context.pbAdSlot', '1234567890 '); + bidderRequest.bids[0].context = { + pbAdSlot: '/1234567890' + } sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 ); const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - expect(request.data.imp[0].context.data.adslot).to.equal('1234567890'); + expect(request.data.imp[0].ext.context.data.adslot).to.equal('1234567890'); }); }); From 0c1b352c2b9388b1cb812af16d1f29e2b44088db Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Fri, 13 Dec 2019 16:02:51 -0800 Subject: [PATCH 20/25] fix for consistency with pb server --- modules/rubiconBidAdapter.js | 2 +- test/spec/modules/rubiconBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index b0fa51460e5..d76af9ded08 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -273,7 +273,7 @@ export const spec = { */ const pbAdSlot = utils.deepAccess(bidRequest, 'context.pbAdSlot'); if (typeof pbAdSlot === 'string' && pbAdSlot) { - utils.deepSetValue(data.imp[0].ext, 'context.data.adslot', pbAdSlot.replace(/^\/+/, '')); + utils.deepSetValue(data.imp[0].ext, 'context.data.adslot', pbAdSlot); } return { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index ed0e60ef22c..ebff2c4ff8a 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1635,7 +1635,7 @@ describe('the rubicon adapter', function () { it('should include pbAdSlot in bid request', function () { createVideoBidderRequest(); bidderRequest.bids[0].context = { - pbAdSlot: '/1234567890' + pbAdSlot: '1234567890' } sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 From c526a5856e68b0e205808db24b42801220e6204a Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Mon, 6 Jan 2020 12:27:39 -0800 Subject: [PATCH 21/25] update obj path to fpd.context.pbAdSlot --- modules/prebidServerBidAdapter/index.js | 2 +- modules/rubiconBidAdapter.js | 4 +- src/adapterManager.js | 2 +- .../modules/prebidServerBidAdapter_spec.js | 22 ++++--- test/spec/modules/rubiconBidAdapter_spec.js | 43 +++++++++----- test/spec/unit/pbjs_api_spec.js | 58 +++++++++++-------- 6 files changed, 79 insertions(+), 52 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 72f9a5d3ec8..a0a5f3cbfd8 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -485,7 +485,7 @@ const OPEN_RTB_PROTOCOL = { * Prebid AdSlot * @type {(string|undefined)} */ - const pbAdSlot = utils.deepAccess(adUnit, 'context.pbAdSlot'); + const pbAdSlot = utils.deepAccess(adUnit, 'fpd.context.pbAdSlot'); if (typeof pbAdSlot === 'string' && pbAdSlot) { utils.deepSetValue(imp, 'ext.context.data.adslot', pbAdSlot); } diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 9ef80dea40d..b76ca9e375d 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -271,7 +271,7 @@ export const spec = { * Prebid AdSlot * @type {(string|undefined)} */ - const pbAdSlot = utils.deepAccess(bidRequest, 'context.pbAdSlot'); + const pbAdSlot = utils.deepAccess(bidRequest, 'fpd.context.pbAdSlot'); if (typeof pbAdSlot === 'string' && pbAdSlot) { utils.deepSetValue(data.imp[0].ext, 'context.data.adslot', pbAdSlot); } @@ -502,7 +502,7 @@ export const spec = { * Prebid AdSlot * @type {(string|undefined)} */ - const pbAdSlot = utils.deepAccess(bidRequest, 'context.pbAdSlot'); + const pbAdSlot = utils.deepAccess(bidRequest, 'fpd.context.pbAdSlot'); if (typeof pbAdSlot === 'string' && pbAdSlot) { data['tg_i.dfp_ad_unit_code'] = pbAdSlot.replace(/^\/+/, ''); } diff --git a/src/adapterManager.js b/src/adapterManager.js index 07df42f0493..c9802e0f903 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -66,7 +66,7 @@ function getBids({bidderCode, auctionId, bidderRequestId, adUnits, labels, src}) } bid = Object.assign({}, bid, getDefinedParams(adUnit, [ - 'context', + 'fpd', 'mediaType', 'renderer' ])); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 24796b0aa28..5bde9800fb3 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1295,7 +1295,7 @@ describe('S2S Adapter', function () { }) describe('pbAdSlot config', function () { - it('should not send \"imp.ext.context.data.adslot\" if \"context\" is undefined', function () { + it('should not send \"imp.ext.context.data.adslot\" if \"fpd.context\" is undefined', function () { const ortb2Config = utils.deepClone(CONFIG); ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; const consentConfig = { s2sConfig: ortb2Config }; @@ -1310,13 +1310,13 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); }); - it('should not send \"imp.ext.context.data.adslot\" if \"context.pbAdSlot\" is undefined', function () { + it('should not send \"imp.ext.context.data.adslot\" if \"fpd.context.pbAdSlot\" is undefined', function () { const ortb2Config = utils.deepClone(CONFIG); ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; const consentConfig = { s2sConfig: ortb2Config }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); - bidRequest.ad_units[0].context = {}; + bidRequest.ad_units[0].fpd = {}; adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); const parsedRequestBody = JSON.parse(requests[0].requestBody); @@ -1326,14 +1326,16 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); }); - it('should not send \"imp.ext.context.data.adslot\" if \"context.pbAdSlot\" is empty string', function () { + it('should not send \"imp.ext.context.data.adslot\" if \"fpd.context.pbAdSlot\" is empty string', function () { const ortb2Config = utils.deepClone(CONFIG); ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; const consentConfig = { s2sConfig: ortb2Config }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); - bidRequest.ad_units[0].context = { - pbAdSlot: '' + bidRequest.ad_units[0].fpd = { + context: { + pbAdSlot: '' + } }; adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); @@ -1344,14 +1346,16 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); }); - it('should send \"imp.ext.context.data.adslot\" if \"context.pbAdSlot\" value is a non-empty string', function () { + it('should send \"imp.ext.context.data.adslot\" if \"fpd.context.pbAdSlot\" value is a non-empty string', function () { const ortb2Config = utils.deepClone(CONFIG); ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; const consentConfig = { s2sConfig: ortb2Config }; config.setConfig(consentConfig); const bidRequest = utils.deepClone(REQUEST); - bidRequest.ad_units[0].context = { - pbAdSlot: '/a/b/c' + bidRequest.ad_units[0].fpd = { + context: { + pbAdSlot: '/a/b/c' + } }; adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 009ff230545..144713fd3d4 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1265,11 +1265,11 @@ describe('the rubicon adapter', function () { beforeEach(function () { // enforce that the bid at 0 does not have a 'context' property if (bidderRequest.bids[0].hasOwnProperty('context')) { - delete bidderRequest.bids[0].context; + delete bidderRequest.bids[0].fpd; } }); - it('should not send \"tg_i.dfp_ad_unit_code\" if \"context\" object is not valid', function () { + it('should not send \"tg_i.dfp_ad_unit_code\" if \"fpd.context\" object is not valid', function () { const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const data = parseQuery(request.data); @@ -1277,8 +1277,8 @@ describe('the rubicon adapter', function () { expect(data).to.not.have.property('tg_i.dfp_ad_unit_code'); }); - it('should not send \"tg_i.dfp_ad_unit_code\" if \"context.pbAdSlot\" is undefined', function () { - bidderRequest.bids[0].context = {}; + it('should not send \"tg_i.dfp_ad_unit_code\" if \"fpd.context.pbAdSlot\" is undefined', function () { + bidderRequest.bids[0].fpd = {}; const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const data = parseQuery(request.data); @@ -1287,9 +1287,11 @@ describe('the rubicon adapter', function () { expect(data).to.not.have.property('tg_i.dfp_ad_unit_code'); }); - it('should not send \"tg_i.dfp_ad_unit_code\" if \"context.pbAdSlot\" value is an empty string', function () { - bidderRequest.bids[0].context = { - pbAdSlot: '' + it('should not send \"tg_i.dfp_ad_unit_code\" if \"fpd.context.pbAdSlot\" value is an empty string', function () { + bidderRequest.bids[0].fpd = { + context: { + pbAdSlot: '' + } }; const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); @@ -1299,10 +1301,12 @@ describe('the rubicon adapter', function () { expect(data).to.not.have.property('tg_i.dfp_ad_unit_code'); }); - it('should send \"tg_i.dfp_ad_unit_code\" if \"context.pbAdSlot\" value is a valid string', function () { - bidderRequest.bids[0].context = { - pbAdSlot: 'abc' - }; + it('should send \"tg_i.dfp_ad_unit_code\" if \"fpd.context.pbAdSlot\" value is a valid string', function () { + bidderRequest.bids[0].fpd = { + context: { + pbAdSlot: 'abc' + } + } const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const data = parseQuery(request.data); @@ -1312,8 +1316,12 @@ describe('the rubicon adapter', function () { expect(data['tg_i.dfp_ad_unit_code']).to.equal('abc'); }); - it('should send \"tg_i.dfp_ad_unit_code\" if \"context.pbAdSlot\" value is a valid string, but all leading slash characters should be removed', function () { - bidderRequest.bids[0].context = { pbAdSlot: '/a/b/c' }; + it('should send \"tg_i.dfp_ad_unit_code\" if \"fpd.context.pbAdSlot\" value is a valid string, but all leading slash characters should be removed', function () { + bidderRequest.bids[0].fpd = { + context: { + pbAdSlot: '/a/b/c' + } + }; const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); const data = parseQuery(request.data); @@ -1634,9 +1642,12 @@ describe('the rubicon adapter', function () { it('should include pbAdSlot in bid request', function () { createVideoBidderRequest(); - bidderRequest.bids[0].context = { - pbAdSlot: '1234567890' - } + bidderRequest.bids[0].fpd = { + context: { + pbAdSlot: '1234567890' + } + }; + sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 ); diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index da7cec5151c..f0b5f3adc6e 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -2011,17 +2011,19 @@ describe('Unit: Prebid Module', function () { } }); - it('should allow creation of a context.pbAdSlot property on adUnits from inside the event handler', function () { + it('should allow creation of a fpd.context.pbAdSlot property on adUnits from inside the event handler', function () { // verify adUnits passed to handler then alter the adUnits beforeRequestBidsHandler = function beforeRequestBidsHandler(beforeRequestBidsAdUnits) { expect(beforeRequestBidsAdUnits).to.be.a('array'); expect(beforeRequestBidsAdUnits).to.have.lengthOf(1); expect(beforeRequestBidsAdUnits[0]).to.be.a('object'); // adUnit should not contain a context property yet - expect(beforeRequestBidsAdUnits[0]).to.not.have.property('context') + expect(beforeRequestBidsAdUnits[0]).to.not.have.property('fpd') // alter the adUnit by adding the property for context.pbAdSlot - beforeRequestBidsAdUnits[0].context = { - pbAdSlot: '/19968336/header-bid-tag-pbadslot-0' + beforeRequestBidsAdUnits[0].fpd = { + context: { + pbAdSlot: '/19968336/header-bid-tag-pbadslot-0' + } }; }; beforeRequestBidsSpy = sinon.spy(beforeRequestBidsHandler); @@ -2034,10 +2036,12 @@ describe('Unit: Prebid Module', function () { expect(bidRequest.bids).to.have.lengthOf(1); const bid = bidRequest['bids'][0]; expect(bid).to.be.a('object'); - expect(bid).to.have.property('context'); - expect(bid.context).to.be.a('object'); - expect(bid.context).to.have.property('pbAdSlot'); - expect(bid.context.pbAdSlot).to.equal('/19968336/header-bid-tag-pbadslot-0'); + expect(bid).to.have.property('fpd'); + expect(bid.fpd).to.be.a('object'); + expect(bid.fpd).to.have.property('context'); + expect(bid.fpd.context).to.be.a('object'); + expect(bid.fpd.context).to.have.property('pbAdSlot'); + expect(bid.fpd.context.pbAdSlot).to.equal('/19968336/header-bid-tag-pbadslot-0'); }; bidRequestedSpy = sinon.spy(bidRequestedHandler); @@ -2065,7 +2069,7 @@ describe('Unit: Prebid Module', function () { sinon.assert.calledOnce(bidRequestedSpy); }); - it('should allow creation of a context.pbAdSlot property on adUnits from inside the event handler', function () { + it('should allow creation of a fpd.context.pbAdSlot property on adUnits from inside the event handler', function () { // verify adUnits passed to handler then alter the adUnits beforeRequestBidsHandler = function beforeRequestBidsHandler(beforeRequestBidsAdUnits) { expect(beforeRequestBidsAdUnits).to.be.a('array'); @@ -2073,14 +2077,18 @@ describe('Unit: Prebid Module', function () { expect(beforeRequestBidsAdUnits[0]).to.be.a('object'); expect(beforeRequestBidsAdUnits[1]).to.be.a('object'); // adUnit should not contain a context property yet - expect(beforeRequestBidsAdUnits[0]).to.not.have.property('context') - expect(beforeRequestBidsAdUnits[1]).to.not.have.property('context') + expect(beforeRequestBidsAdUnits[0]).to.not.have.property('fpd'); + expect(beforeRequestBidsAdUnits[1]).to.not.have.property('fpd'); // alter the adUnit by adding the property for context.pbAdSlot - beforeRequestBidsAdUnits[0].context = { - pbAdSlot: '/19968336/header-bid-tag-pbadslot-0' + beforeRequestBidsAdUnits[0].fpd = { + context: { + pbAdSlot: '/19968336/header-bid-tag-pbadslot-0' + } }; - beforeRequestBidsAdUnits[1].context = { - pbAdSlot: '/19968336/header-bid-tag-pbadslot-1' + beforeRequestBidsAdUnits[1].fpd = { + context: { + pbAdSlot: '/19968336/header-bid-tag-pbadslot-1' + } }; }; beforeRequestBidsSpy = sinon.spy(beforeRequestBidsHandler); @@ -2093,17 +2101,21 @@ describe('Unit: Prebid Module', function () { expect(bidRequest.bids).to.have.lengthOf(2); const bid0 = bidRequest['bids'][0]; expect(bid0).to.be.a('object'); - expect(bid0).to.have.property('context'); - expect(bid0.context).to.be.a('object'); - expect(bid0.context).to.have.property('pbAdSlot'); - expect(bid0.context.pbAdSlot).to.equal('/19968336/header-bid-tag-pbadslot-0'); + expect(bid0).to.have.property('fpd'); + expect(bid0.fpd).to.be.a('object'); + expect(bid0.fpd).to.have.property('context'); + expect(bid0.fpd.context).to.be.a('object'); + expect(bid0.fpd.context).to.have.property('pbAdSlot'); + expect(bid0.fpd.context.pbAdSlot).to.equal('/19968336/header-bid-tag-pbadslot-0'); const bid1 = bidRequest['bids'][1]; expect(bid1).to.be.a('object'); - expect(bid1).to.have.property('context'); - expect(bid1.context).to.be.a('object'); - expect(bid1.context).to.have.property('pbAdSlot'); - expect(bid1.context.pbAdSlot).to.equal('/19968336/header-bid-tag-pbadslot-1'); + expect(bid1).to.have.property('fpd'); + expect(bid1.fpd).to.be.a('object'); + expect(bid1.fpd).to.have.property('context'); + expect(bid1.fpd.context).to.be.a('object'); + expect(bid1.fpd.context).to.have.property('pbAdSlot'); + expect(bid1.fpd.context.pbAdSlot).to.equal('/19968336/header-bid-tag-pbadslot-1'); }; bidRequestedSpy = sinon.spy(bidRequestedHandler); From 431a8b8be4e5357aa9c1ac0071debe4383329529 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Mon, 13 Jan 2020 11:04:50 -0800 Subject: [PATCH 22/25] add leading slash removal to all outputs of pbAslot --- modules/prebidServerBidAdapter/index.js | 2 +- modules/rubiconBidAdapter.js | 2 +- test/spec/modules/prebidServerBidAdapter_spec.js | 4 ++-- test/spec/modules/rubiconBidAdapter_spec.js | 16 ++++++++++++++++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index a0a5f3cbfd8..9af6186731b 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -487,7 +487,7 @@ const OPEN_RTB_PROTOCOL = { */ const pbAdSlot = utils.deepAccess(adUnit, 'fpd.context.pbAdSlot'); if (typeof pbAdSlot === 'string' && pbAdSlot) { - utils.deepSetValue(imp, 'ext.context.data.adslot', pbAdSlot); + utils.deepSetValue(imp, 'ext.context.data.adslot', pbAdSlot.replace(/^\/+/, '')); } Object.assign(imp, mediaTypes); diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index b76ca9e375d..c03188191e8 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -273,7 +273,7 @@ export const spec = { */ const pbAdSlot = utils.deepAccess(bidRequest, 'fpd.context.pbAdSlot'); if (typeof pbAdSlot === 'string' && pbAdSlot) { - utils.deepSetValue(data.imp[0].ext, 'context.data.adslot', pbAdSlot); + utils.deepSetValue(data.imp[0].ext, 'context.data.adslot', pbAdSlot.replace(/^\/+/, '')); } return { diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 5bde9800fb3..272d33e25bd 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1354,7 +1354,7 @@ describe('S2S Adapter', function () { const bidRequest = utils.deepClone(REQUEST); bidRequest.ad_units[0].fpd = { context: { - pbAdSlot: '/a/b/c' + pbAdSlot: '/abc' } }; @@ -1364,7 +1364,7 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.context.data.adslot'); - expect(parsedRequestBody.imp[0].ext.context.data.adslot).to.equal('/a/b/c'); + expect(parsedRequestBody.imp[0].ext.context.data.adslot).to.equal('abc'); }); }); }); diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 144713fd3d4..c98d9ce0e03 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1657,6 +1657,22 @@ describe('the rubicon adapter', function () { }); }); + it('should include pbAdSlot in bid request but should remove leading slash', function () { + createVideoBidderRequest(); + bidderRequest.bids[0].fpd = { + context: { + pbAdSlot: '/1234567890' + } + }; + + sandbox.stub(Date, 'now').callsFake(() => + bidderRequest.auctionStart + 100 + ); + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.imp[0].ext.context.data.adslot).to.equal('1234567890'); + }); + describe('combineSlotUrlParams', function () { it('should combine an array of slot url params', function () { expect(spec.combineSlotUrlParams([])).to.deep.equal({}); From 55d6e90cf9ffe808e6dfb6cc845d1c72db32a98d Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Mon, 13 Jan 2020 16:04:31 -0800 Subject: [PATCH 23/25] revert to previous branch reviewed --- modules/prebidServerBidAdapter/index.js | 9 + modules/rubiconBidAdapter.js | 18 ++ src/adapterManager.js | 7 + src/constants.json | 1 + .../modules/prebidServerBidAdapter_spec.js | 74 ++++++ test/spec/modules/rubiconBidAdapter_spec.js | 103 ++++++++ test/spec/unit/pbjs_api_spec.js | 222 ++++++++++++++++++ 7 files changed, 434 insertions(+) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index edb0b4c5f4a..9af6186731b 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -481,6 +481,15 @@ const OPEN_RTB_PROTOCOL = { const imp = { id: adUnit.code, ext, secure: _s2sConfig.secure }; + /** + * Prebid AdSlot + * @type {(string|undefined)} + */ + const pbAdSlot = utils.deepAccess(adUnit, 'fpd.context.pbAdSlot'); + if (typeof pbAdSlot === 'string' && pbAdSlot) { + utils.deepSetValue(imp, 'ext.context.data.adslot', pbAdSlot.replace(/^\/+/, '')); + } + Object.assign(imp, mediaTypes); if (imp.banner || imp.video || imp.native) { diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index f42ed0be5b6..c03188191e8 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -267,6 +267,15 @@ export const spec = { utils.deepSetValue(data, 'source.ext.schain', bidRequest.schain); } + /** + * Prebid AdSlot + * @type {(string|undefined)} + */ + const pbAdSlot = utils.deepAccess(bidRequest, 'fpd.context.pbAdSlot'); + if (typeof pbAdSlot === 'string' && pbAdSlot) { + utils.deepSetValue(data.imp[0].ext, 'context.data.adslot', pbAdSlot.replace(/^\/+/, '')); + } + return { method: 'POST', url: VIDEO_ENDPOINT, @@ -489,6 +498,15 @@ export const spec = { }); } + /** + * Prebid AdSlot + * @type {(string|undefined)} + */ + const pbAdSlot = utils.deepAccess(bidRequest, 'fpd.context.pbAdSlot'); + if (typeof pbAdSlot === 'string' && pbAdSlot) { + data['tg_i.dfp_ad_unit_code'] = pbAdSlot.replace(/^\/+/, ''); + } + // digitrust properties const digitrustParams = _getDigiTrustQueryParams(bidRequest, 'FASTLANE'); Object.assign(data, digitrustParams); diff --git a/src/adapterManager.js b/src/adapterManager.js index f93d6cf5072..c9802e0f903 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -66,6 +66,7 @@ function getBids({bidderCode, auctionId, bidderRequestId, adUnits, labels, src}) } bid = Object.assign({}, bid, getDefinedParams(adUnit, [ + 'fpd', 'mediaType', 'renderer' ])); @@ -174,6 +175,12 @@ export let uspDataHandler = { }; adapterManager.makeBidRequests = hook('sync', function (adUnits, auctionStart, auctionId, cbTimeout, labels) { + /** + * emit and pass adunits for external modification + * @see {@link https://github.com/prebid/Prebid.js/issues/4149|Issue} + */ + events.emit(CONSTANTS.EVENTS.BEFORE_REQUEST_BIDS, adUnits); + let bidRequests = []; let bidderCodes = getBidderCodes(adUnits); diff --git a/src/constants.json b/src/constants.json index d993a6531ad..0b6b144a336 100644 --- a/src/constants.json +++ b/src/constants.json @@ -33,6 +33,7 @@ "BID_WON": "bidWon", "BIDDER_DONE": "bidderDone", "SET_TARGETING": "setTargeting", + "BEFORE_REQUEST_BIDS": "beforeRequestBids", "REQUEST_BIDS": "requestBids", "ADD_AD_UNITS": "addAdUnits", "AD_RENDER_FAILED" : "adRenderFailed" diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 8e99600871c..127fdf4d1a0 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1288,6 +1288,80 @@ describe('S2S Adapter', function () { const parsedRequestBody = JSON.parse(server.requests[0].requestBody); expect(parsedRequestBody.source.ext.schain).to.deep.equal(schainObject); }) + + describe('pbAdSlot config', function () { + it('should not send \"imp.ext.context.data.adslot\" if \"fpd.context\" is undefined', function () { + const ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + const consentConfig = { s2sConfig: ortb2Config }; + config.setConfig(consentConfig); + const bidRequest = utils.deepClone(REQUEST); + + adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); + const parsedRequestBody = JSON.parse(requests[0].requestBody); + + expect(parsedRequestBody.imp).to.be.a('array'); + expect(parsedRequestBody.imp[0]).to.be.a('object'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); + }); + + it('should not send \"imp.ext.context.data.adslot\" if \"fpd.context.pbAdSlot\" is undefined', function () { + const ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + const consentConfig = { s2sConfig: ortb2Config }; + config.setConfig(consentConfig); + const bidRequest = utils.deepClone(REQUEST); + bidRequest.ad_units[0].fpd = {}; + + adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); + const parsedRequestBody = JSON.parse(requests[0].requestBody); + + expect(parsedRequestBody.imp).to.be.a('array'); + expect(parsedRequestBody.imp[0]).to.be.a('object'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); + }); + + it('should not send \"imp.ext.context.data.adslot\" if \"fpd.context.pbAdSlot\" is empty string', function () { + const ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + const consentConfig = { s2sConfig: ortb2Config }; + config.setConfig(consentConfig); + const bidRequest = utils.deepClone(REQUEST); + bidRequest.ad_units[0].fpd = { + context: { + pbAdSlot: '' + } + }; + + adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); + const parsedRequestBody = JSON.parse(requests[0].requestBody); + + expect(parsedRequestBody.imp).to.be.a('array'); + expect(parsedRequestBody.imp[0]).to.be.a('object'); + expect(parsedRequestBody.imp[0]).to.not.have.deep.nested.property('ext.context.data.adslot'); + }); + + it('should send \"imp.ext.context.data.adslot\" if \"fpd.context.pbAdSlot\" value is a non-empty string', function () { + const ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + const consentConfig = { s2sConfig: ortb2Config }; + config.setConfig(consentConfig); + const bidRequest = utils.deepClone(REQUEST); + bidRequest.ad_units[0].fpd = { + context: { + pbAdSlot: '/abc' + } + }; + + adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); + const parsedRequestBody = JSON.parse(requests[0].requestBody); + + expect(parsedRequestBody.imp).to.be.a('array'); + expect(parsedRequestBody.imp[0]).to.be.a('object'); + expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.context.data.adslot'); + expect(parsedRequestBody.imp[0].ext.context.data.adslot).to.equal('abc'); + }); + }); }); describe('response handler', function () { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index d0c0773ff45..c98d9ce0e03 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1260,6 +1260,77 @@ describe('the rubicon adapter', function () { }); }); }) + + describe('Prebid AdSlot', function () { + beforeEach(function () { + // enforce that the bid at 0 does not have a 'context' property + if (bidderRequest.bids[0].hasOwnProperty('context')) { + delete bidderRequest.bids[0].fpd; + } + }); + + it('should not send \"tg_i.dfp_ad_unit_code\" if \"fpd.context\" object is not valid', function () { + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const data = parseQuery(request.data); + + expect(data).to.be.an('Object'); + expect(data).to.not.have.property('tg_i.dfp_ad_unit_code'); + }); + + it('should not send \"tg_i.dfp_ad_unit_code\" if \"fpd.context.pbAdSlot\" is undefined', function () { + bidderRequest.bids[0].fpd = {}; + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const data = parseQuery(request.data); + + expect(data).to.be.an('Object'); + expect(data).to.not.have.property('tg_i.dfp_ad_unit_code'); + }); + + it('should not send \"tg_i.dfp_ad_unit_code\" if \"fpd.context.pbAdSlot\" value is an empty string', function () { + bidderRequest.bids[0].fpd = { + context: { + pbAdSlot: '' + } + }; + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const data = parseQuery(request.data); + + expect(data).to.be.an('Object'); + expect(data).to.not.have.property('tg_i.dfp_ad_unit_code'); + }); + + it('should send \"tg_i.dfp_ad_unit_code\" if \"fpd.context.pbAdSlot\" value is a valid string', function () { + bidderRequest.bids[0].fpd = { + context: { + pbAdSlot: 'abc' + } + } + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const data = parseQuery(request.data); + + expect(data).to.be.an('Object'); + expect(data).to.have.property('tg_i.dfp_ad_unit_code'); + expect(data['tg_i.dfp_ad_unit_code']).to.equal('abc'); + }); + + it('should send \"tg_i.dfp_ad_unit_code\" if \"fpd.context.pbAdSlot\" value is a valid string, but all leading slash characters should be removed', function () { + bidderRequest.bids[0].fpd = { + context: { + pbAdSlot: '/a/b/c' + } + }; + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const data = parseQuery(request.data); + + expect(data).to.be.an('Object'); + expect(data).to.have.property('tg_i.dfp_ad_unit_code'); + expect(data['tg_i.dfp_ad_unit_code']).to.equal('a/b/c'); + }); + }); }); describe('for video requests', function () { @@ -1568,6 +1639,38 @@ describe('the rubicon adapter', function () { const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(request.data.regs.coppa).to.equal(1); }); + + it('should include pbAdSlot in bid request', function () { + createVideoBidderRequest(); + bidderRequest.bids[0].fpd = { + context: { + pbAdSlot: '1234567890' + } + }; + + sandbox.stub(Date, 'now').callsFake(() => + bidderRequest.auctionStart + 100 + ); + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.imp[0].ext.context.data.adslot).to.equal('1234567890'); + }); + }); + + it('should include pbAdSlot in bid request but should remove leading slash', function () { + createVideoBidderRequest(); + bidderRequest.bids[0].fpd = { + context: { + pbAdSlot: '/1234567890' + } + }; + + sandbox.stub(Date, 'now').callsFake(() => + bidderRequest.auctionStart + 100 + ); + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.imp[0].ext.context.data.adslot).to.equal('1234567890'); }); describe('combineSlotUrlParams', function () { diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 094220ea521..7a048c3f967 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -1966,6 +1966,228 @@ describe('Unit: Prebid Module', function () { assert.ok(spyEventsOn.calledWith('bidWon', Function)); events.on.restore(); }); + + describe('beforeRequestBids', function () { + let bidRequestedHandler; + let beforeRequestBidsHandler; + let bidsBackHandler = function bidsBackHandler() {}; + + let bidsBackSpy; + let bidRequestedSpy; + let beforeRequestBidsSpy; + + beforeEach(function () { + resetAuction(); + bidsBackSpy = sinon.spy(bidsBackHandler); + googletag.pubads().setSlots(createSlotArrayScenario2()); + }); + + afterEach(function () { + bidsBackSpy.resetHistory(); + + if (bidRequestedSpy) { + $$PREBID_GLOBAL$$.offEvent('bidRequested', bidRequestedSpy); + bidRequestedSpy.resetHistory(); + } + + if (beforeRequestBidsSpy) { + $$PREBID_GLOBAL$$.offEvent('beforeRequestBids', beforeRequestBidsSpy); + beforeRequestBidsSpy.resetHistory(); + } + }); + + it('should allow creation of a fpd.context.pbAdSlot property on adUnits from inside the event handler', function () { + // verify adUnits passed to handler then alter the adUnits + beforeRequestBidsHandler = function beforeRequestBidsHandler(beforeRequestBidsAdUnits) { + expect(beforeRequestBidsAdUnits).to.be.a('array'); + expect(beforeRequestBidsAdUnits).to.have.lengthOf(1); + expect(beforeRequestBidsAdUnits[0]).to.be.a('object'); + // adUnit should not contain a context property yet + expect(beforeRequestBidsAdUnits[0]).to.not.have.property('fpd') + // alter the adUnit by adding the property for context.pbAdSlot + beforeRequestBidsAdUnits[0].fpd = { + context: { + pbAdSlot: '/19968336/header-bid-tag-pbadslot-0' + } + }; + }; + beforeRequestBidsSpy = sinon.spy(beforeRequestBidsHandler); + + // use this handler to verify if the adUnits alterations were applied successfully by the beforeRequestBids handler + bidRequestedHandler = function bidRequestedHandler(bidRequest) { + expect(bidRequest).to.be.a('object'); + expect(bidRequest).to.have.property('bids'); + expect(bidRequest.bids).to.be.a('array'); + expect(bidRequest.bids).to.have.lengthOf(1); + const bid = bidRequest['bids'][0]; + expect(bid).to.be.a('object'); + expect(bid).to.have.property('fpd'); + expect(bid.fpd).to.be.a('object'); + expect(bid.fpd).to.have.property('context'); + expect(bid.fpd.context).to.be.a('object'); + expect(bid.fpd.context).to.have.property('pbAdSlot'); + expect(bid.fpd.context.pbAdSlot).to.equal('/19968336/header-bid-tag-pbadslot-0'); + }; + bidRequestedSpy = sinon.spy(bidRequestedHandler); + + $$PREBID_GLOBAL$$.onEvent('beforeRequestBids', beforeRequestBidsSpy); + $$PREBID_GLOBAL$$.onEvent('bidRequested', bidRequestedSpy); + $$PREBID_GLOBAL$$.requestBids({ + adUnits: [{ + code: '/19968336/header-bid-tag-0', + mediaTypes: { + banner: { + sizes: [[750, 350]] + } + }, + bids: [{ + bidder: 'appnexus', + params: { + placementId: 13122370 + } + }] + }], + bidsBackHandler: bidsBackSpy + }); + + sinon.assert.calledOnce(beforeRequestBidsSpy); + sinon.assert.calledOnce(bidRequestedSpy); + }); + + it('should allow creation of a fpd.context.pbAdSlot property on adUnits from inside the event handler', function () { + // verify adUnits passed to handler then alter the adUnits + beforeRequestBidsHandler = function beforeRequestBidsHandler(beforeRequestBidsAdUnits) { + expect(beforeRequestBidsAdUnits).to.be.a('array'); + expect(beforeRequestBidsAdUnits).to.have.lengthOf(2); + expect(beforeRequestBidsAdUnits[0]).to.be.a('object'); + expect(beforeRequestBidsAdUnits[1]).to.be.a('object'); + // adUnit should not contain a context property yet + expect(beforeRequestBidsAdUnits[0]).to.not.have.property('fpd'); + expect(beforeRequestBidsAdUnits[1]).to.not.have.property('fpd'); + // alter the adUnit by adding the property for context.pbAdSlot + beforeRequestBidsAdUnits[0].fpd = { + context: { + pbAdSlot: '/19968336/header-bid-tag-pbadslot-0' + } + }; + beforeRequestBidsAdUnits[1].fpd = { + context: { + pbAdSlot: '/19968336/header-bid-tag-pbadslot-1' + } + }; + }; + beforeRequestBidsSpy = sinon.spy(beforeRequestBidsHandler); + + // use this handler to verify if the adUnits alterations were applied successfully by the beforeRequestBids handler + bidRequestedHandler = function bidRequestedHandler(bidRequest) { + expect(bidRequest).to.be.a('object'); + expect(bidRequest).to.have.property('bids'); + expect(bidRequest.bids).to.be.a('array'); + expect(bidRequest.bids).to.have.lengthOf(2); + const bid0 = bidRequest['bids'][0]; + expect(bid0).to.be.a('object'); + expect(bid0).to.have.property('fpd'); + expect(bid0.fpd).to.be.a('object'); + expect(bid0.fpd).to.have.property('context'); + expect(bid0.fpd.context).to.be.a('object'); + expect(bid0.fpd.context).to.have.property('pbAdSlot'); + expect(bid0.fpd.context.pbAdSlot).to.equal('/19968336/header-bid-tag-pbadslot-0'); + + const bid1 = bidRequest['bids'][1]; + expect(bid1).to.be.a('object'); + expect(bid1).to.have.property('fpd'); + expect(bid1.fpd).to.be.a('object'); + expect(bid1.fpd).to.have.property('context'); + expect(bid1.fpd.context).to.be.a('object'); + expect(bid1.fpd.context).to.have.property('pbAdSlot'); + expect(bid1.fpd.context.pbAdSlot).to.equal('/19968336/header-bid-tag-pbadslot-1'); + }; + bidRequestedSpy = sinon.spy(bidRequestedHandler); + + $$PREBID_GLOBAL$$.onEvent('beforeRequestBids', beforeRequestBidsSpy); + $$PREBID_GLOBAL$$.onEvent('bidRequested', bidRequestedSpy); + $$PREBID_GLOBAL$$.requestBids({ + adUnits: [{ + code: '/19968336/header-bid-tag-0', + mediaTypes: { + banner: { + sizes: [[750, 350]] + } + }, + bids: [{ + bidder: 'appnexus', + params: { + placementId: 13122370 + } + }] + }, { + code: '/19968336/header-bid-tag-1', + mediaTypes: { + banner: { + sizes: [[750, 350]] + } + }, + bids: [{ + bidder: 'appnexus', + params: { + placementId: 14122380 + } + }] + }], + bidsBackHandler: bidsBackSpy + }); + + sinon.assert.calledOnce(beforeRequestBidsSpy); + sinon.assert.calledOnce(bidRequestedSpy); + }); + + it('should not create a context property on adUnits if not added by handler', function () { + // verify adUnits passed to handler then alter the adUnits + beforeRequestBidsHandler = function beforeRequestBidsHandler(beforeRequestBidsAdUnits) { + expect(beforeRequestBidsAdUnits).to.be.a('array'); + expect(beforeRequestBidsAdUnits).to.have.lengthOf(1); + expect(beforeRequestBidsAdUnits[0]).to.be.a('object'); + // adUnit should not contain a context property yet + expect(beforeRequestBidsAdUnits[0]).to.not.have.property('context') + }; + beforeRequestBidsSpy = sinon.spy(beforeRequestBidsHandler); + + // use this handler to verify if the adUnits alterations were applied successfully by the beforeRequestBids handler + bidRequestedHandler = function bidRequestedHandler(bidRequest) { + expect(bidRequest).to.be.a('object'); + expect(bidRequest).to.have.property('bids'); + expect(bidRequest.bids).to.be.a('array'); + expect(bidRequest.bids).to.have.lengthOf(1); + const bid = bidRequest['bids'][0]; + expect(bid).to.be.a('object'); + expect(bid).to.not.have.property('context'); + }; + bidRequestedSpy = sinon.spy(bidRequestedHandler); + + $$PREBID_GLOBAL$$.onEvent('beforeRequestBids', beforeRequestBidsSpy); + $$PREBID_GLOBAL$$.onEvent('bidRequested', bidRequestedSpy); + $$PREBID_GLOBAL$$.requestBids({ + adUnits: [{ + code: '/19968336/header-bid-tag-0', + mediaTypes: { + banner: { + sizes: [[750, 350]] + } + }, + bids: [{ + bidder: 'appnexus', + params: { + placementId: 13122370 + } + }] + }], + bidsBackHandler: bidsBackSpy + }); + + sinon.assert.calledOnce(beforeRequestBidsSpy); + sinon.assert.calledOnce(bidRequestedSpy); + }); + }); }); describe('offEvent', function () { From fe3fa594c163029fd2393e9ced3773fd1bc91a1e Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Mon, 13 Jan 2020 16:26:56 -0800 Subject: [PATCH 24/25] revert to previous branch reviewed --- modules/prebidServerBidAdapter/index.js | 2 +- modules/rubiconBidAdapter.js | 2 +- test/spec/modules/prebidServerBidAdapter_spec.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 9af6186731b..a0a5f3cbfd8 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -487,7 +487,7 @@ const OPEN_RTB_PROTOCOL = { */ const pbAdSlot = utils.deepAccess(adUnit, 'fpd.context.pbAdSlot'); if (typeof pbAdSlot === 'string' && pbAdSlot) { - utils.deepSetValue(imp, 'ext.context.data.adslot', pbAdSlot.replace(/^\/+/, '')); + utils.deepSetValue(imp, 'ext.context.data.adslot', pbAdSlot); } Object.assign(imp, mediaTypes); diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index c03188191e8..b76ca9e375d 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -273,7 +273,7 @@ export const spec = { */ const pbAdSlot = utils.deepAccess(bidRequest, 'fpd.context.pbAdSlot'); if (typeof pbAdSlot === 'string' && pbAdSlot) { - utils.deepSetValue(data.imp[0].ext, 'context.data.adslot', pbAdSlot.replace(/^\/+/, '')); + utils.deepSetValue(data.imp[0].ext, 'context.data.adslot', pbAdSlot); } return { diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 127fdf4d1a0..9d483672b2d 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1349,7 +1349,7 @@ describe('S2S Adapter', function () { const bidRequest = utils.deepClone(REQUEST); bidRequest.ad_units[0].fpd = { context: { - pbAdSlot: '/abc' + pbAdSlot: '/a/b/c' } }; @@ -1359,7 +1359,7 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); expect(parsedRequestBody.imp[0]).to.have.deep.nested.property('ext.context.data.adslot'); - expect(parsedRequestBody.imp[0].ext.context.data.adslot).to.equal('abc'); + expect(parsedRequestBody.imp[0].ext.context.data.adslot).to.equal('/a/b/c'); }); }); }); From 240e62156a74e0edccdd0f41e4756a4e5b5271e6 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Tue, 14 Jan 2020 10:07:24 -0800 Subject: [PATCH 25/25] fix merge errors --- test/spec/modules/prebidServerBidAdapter_spec.js | 8 ++++---- test/spec/modules/rubiconBidAdapter_spec.js | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index d50a60ac88d..538e1da9aeb 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1298,7 +1298,7 @@ describe('S2S Adapter', function () { const bidRequest = utils.deepClone(REQUEST); adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); - const parsedRequestBody = JSON.parse(requests[0].requestBody); + const parsedRequestBody = JSON.parse(server.requests[0].requestBody); expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); @@ -1314,7 +1314,7 @@ describe('S2S Adapter', function () { bidRequest.ad_units[0].fpd = {}; adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); - const parsedRequestBody = JSON.parse(requests[0].requestBody); + const parsedRequestBody = JSON.parse(server.requests[0].requestBody); expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); @@ -1334,7 +1334,7 @@ describe('S2S Adapter', function () { }; adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); - const parsedRequestBody = JSON.parse(requests[0].requestBody); + const parsedRequestBody = JSON.parse(server.requests[0].requestBody); expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); @@ -1354,7 +1354,7 @@ describe('S2S Adapter', function () { }; adapter.callBids(bidRequest, BID_REQUESTS, addBidResponse, done, ajax); - const parsedRequestBody = JSON.parse(requests[0].requestBody); + const parsedRequestBody = JSON.parse(server.requests[0].requestBody); expect(parsedRequestBody.imp).to.be.a('array'); expect(parsedRequestBody.imp[0]).to.be.a('object'); diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index c98d9ce0e03..5a9e2968309 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1264,7 +1264,7 @@ describe('the rubicon adapter', function () { describe('Prebid AdSlot', function () { beforeEach(function () { // enforce that the bid at 0 does not have a 'context' property - if (bidderRequest.bids[0].hasOwnProperty('context')) { + if (bidderRequest.bids[0].hasOwnProperty('fpd')) { delete bidderRequest.bids[0].fpd; } }); @@ -1657,11 +1657,11 @@ describe('the rubicon adapter', function () { }); }); - it('should include pbAdSlot in bid request but should remove leading slash', function () { + it('should include pbAdSlot in bid request', function () { createVideoBidderRequest(); bidderRequest.bids[0].fpd = { context: { - pbAdSlot: '/1234567890' + pbAdSlot: '1234567890' } };