From 8d635ff86ca3c3bde767bca6acee8b89d5b1f2ca Mon Sep 17 00:00:00 2001 From: nakamoto Date: Fri, 15 Feb 2019 17:28:34 +0900 Subject: [PATCH 01/12] 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/12] 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/12] 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/12] 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/12] 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/12] 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/12] 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/12] 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 8e81409a2105f1d1a9a07a5224634de55f42cd39 Mon Sep 17 00:00:00 2001 From: Mark Monday Date: Fri, 15 Nov 2019 15:09:50 -0500 Subject: [PATCH 09/12] Support user and context first party data in rubicon and prebid server adapters --- modules/prebidServerBidAdapter/index.js | 39 ++++++++++ modules/rubiconBidAdapter.js | 51 +++++++++---- .../modules/prebidServerBidAdapter_spec.js | 35 ++++++++- test/spec/modules/rubiconBidAdapter_spec.js | 71 +++++++++++++++++++ 4 files changed, 180 insertions(+), 16 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 151e880e985..373202256ad 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -294,6 +294,43 @@ function transformHeightWidth(adUnit) { return sizesObj; } +function addFirstPartyDataToRequest(request) { + const bidderConfig = config.getBidderConfig(); + let context = {}; + let user = {}; + const allowedBidders = []; + Object.keys(bidderConfig).forEach(bidder => { + const currBidderConfig = bidderConfig[bidder]; + let isAllowed = false; + + if (currBidderConfig.context) { + context = Object.assign({}, context, currBidderConfig.context); + isAllowed = true; + } + + if (currBidderConfig.user) { + user = Object.assign({}, user, currBidderConfig.user); + isAllowed = true; + } + + if (isAllowed) { + allowedBidders.push(bidder); + } + }); + + if (allowedBidders.length) { + utils.deepSetValue(request, 'ext.prebid.data.bidders', allowedBidders); + + if (!utils.isEmpty(context)) { + utils.deepSetValue(request, 'site.ext.data', context); + } + + if (!utils.isEmpty(user)) { + utils.deepSetValue(request, 'user.ext.data', user); + } + } +} + /* * Protocol spec for legacy endpoint * e.g., https:///v1/auction @@ -772,6 +809,8 @@ const OPEN_RTB_PROTOCOL = { utils.deepSetValue(request, 'regs.coppa', 1); } + addFirstPartyDataToRequest(request); + return request; }, diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 7e2b9147746..5acf70a3efc 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -272,6 +272,20 @@ export const spec = { utils.deepSetValue(data, 'source.ext.schain', bidRequest.schain); } + const siteData = Object.assign({}, bidRequest.params.inventory, config.getConfig('context')); + const userData = Object.assign({}, bidRequest.params.visitor, config.getConfig('user')); + if (!utils.isEmpty(siteData) || !utils.isEmpty(userData)) { + utils.deepSetValue(data, 'ext.prebid.data.bidders', [ bidderRequest.bidderCode ]); + + if (!utils.isEmpty(siteData)) { + utils.deepSetValue(data, 'site.ext.data', siteData); + } + + if (!utils.isEmpty(userData)) { + utils.deepSetValue(data, 'user.ext.data', userData); + } + } + return { method: 'POST', url: VIDEO_ENDPOINT, @@ -435,7 +449,6 @@ export const spec = { 'tk_flint': `${configIntType || DEFAULT_INTEGRATION}_v$prebid.version$`, 'x_source.tid': bidRequest.transactionId, 'p_screen_res': _getScreenResolution(), - 'kw': Array.isArray(params.keywords) ? params.keywords.join(',') : '', 'tk_user_key': params.userId, 'p_geo.latitude': isNaN(parseFloat(latitude)) ? undefined : parseFloat(latitude).toFixed(4), 'p_geo.longitude': isNaN(parseFloat(longitude)) ? undefined : parseFloat(longitude).toFixed(4), @@ -470,22 +483,30 @@ export const spec = { } // visitor properties - if (params.visitor !== null && typeof params.visitor === 'object') { - Object.keys(params.visitor).forEach((key) => { - if (params.visitor[key] != null) { - data[`tg_v.${key}`] = params.visitor[key].toString(); // initialize array; - } - }); - } + const visitorData = Object.assign({}, params.visitor, config.getConfig('user')); + Object.keys(visitorData).forEach((key) => { + if (visitorData[key] != null && key !== 'keywords') { + data[`tg_v.${key}`] = typeof visitorData[key] === 'object' && !Array.isArray(visitorData[key]) + ? JSON.stringify(visitorData[key]) + : visitorData[key].toString(); // initialize array; + } + }); // inventory properties - if (params.inventory !== null && typeof params.inventory === 'object') { - Object.keys(params.inventory).forEach((key) => { - if (params.inventory[key] != null) { - data[`tg_i.${key}`] = params.inventory[key].toString(); - } - }); - } + const inventoryData = Object.assign({}, params.inventory, config.getConfig('context')); + Object.keys(inventoryData).forEach((key) => { + if (inventoryData[key] != null && key !== 'keywords') { + data[`tg_i.${key}`] = typeof inventoryData[key] === 'object' && !Array.isArray(inventoryData[key]) + ? JSON.stringify(inventoryData[key]) + : inventoryData[key].toString(); + } + }); + + // keywords + const keywords = (params.keywords || []).concat( + utils.deepAccess(config.getConfig('user'), 'keywords') || [], + utils.deepAccess(config.getConfig('context'), 'keywords') || []); + data.kw = keywords.length ? keywords.join(',') : ''; // digitrust properties const digitrustParams = _getDigiTrustQueryParams(bidRequest, 'FASTLANE'); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 76860a09ee2..0de439fffc4 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1310,7 +1310,40 @@ describe('S2S Adapter', function () { adapter.callBids(REQUEST, bidRequests, addBidResponse, done, ajax); const parsedRequestBody = JSON.parse(requests[0].requestBody); expect(parsedRequestBody.source.ext.schain).to.deep.equal(schainObject); - }) + }); + + it('passes first party data in request', () => { + const s2sBidRequest = utils.deepClone(REQUEST); + const bidRequests = utils.deepClone(BID_REQUESTS); + + const context = { + keywords: ['power tools'], + search: 'drill', + content: { userrating: 4 }, + data: { + pageType: 'article', + category: 'tools' + } + }; + const user = { + keywords: ['a', 'b'], + gender: 'M', + yob: '1984', + geo: { country: 'ca' }, + data: { + registered: true, + interests: ['cars'] + } + }; + const allowedBidders = [ 'rubicon', 'appnexus' ]; + + config.setBidderConfig({ bidders: allowedBidders, config: { context, user } }); + adapter.callBids(s2sBidRequest, bidRequests, addBidResponse, done, ajax); + const parsedRequestBody = JSON.parse(requests[0].requestBody); + expect(parsedRequestBody.ext.prebid.data.bidders).to.deep.equal(allowedBidders); + expect(parsedRequestBody.site.ext.data).to.deep.equal(context); + expect(parsedRequestBody.user.ext.data).to.deep.equal(user); + }); }); describe('response handler', function () { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 3de1418b65c..a3c14d76466 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -946,6 +946,49 @@ describe('the rubicon adapter', function () { expect(data[key]).to.equal(value); }); }); + + it('should use protected data in bidder request over the bid params, if present', () => { + const context = { + keywords: ['e', 'f'], + rating: '4-star' + }; + const user = { + keywords: ['d'], + gender: 'M', + yob: '1984', + geo: { country: 'ca' } + }; + + sandbox.stub(config, 'getConfig').callsFake(key => { + const config = { + context, + user + }; + return config[key]; + }); + + const expectedQuery = { + 'kw': 'a,b,c,d,e,f', + 'tg_v.ucat': 'new', + 'tg_v.lastsearch': 'iphone', + 'tg_v.likes': 'sports,video games', + 'tg_v.gender': 'M', + 'tg_v.yob': '1984', + 'tg_v.geo': '{"country":"ca"}', + 'tg_i.rating': '4-star', + 'tg_i.prodtype': 'tech,mobile', + }; + + // get the built request + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + let data = parseQuery(request.data); + + // make sure that tg_v, tg_i, and kw values are correct + Object.keys(expectedQuery).forEach(key => { + let value = expectedQuery[key]; + expect(data[key]).to.deep.equal(value); + }); + }); }); describe('singleRequest config', function () { @@ -1534,6 +1577,34 @@ describe('the rubicon adapter', function () { const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(request.data.regs.coppa).to.equal(1); }); + + it('should include first party data', () => { + createVideoBidderRequest(); + + const context = { + keywords: ['e', 'f'], + rating: '4-star' + }; + const user = { + keywords: ['d'], + gender: 'M', + yob: '1984', + geo: { country: 'ca' } + }; + + sandbox.stub(config, 'getConfig').callsFake(key => { + const config = { + context, + user + }; + return config[key]; + }); + + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.ext.prebid.data.bidders).to.deep.equal([ 'rubicon' ]); + expect(request.data.site.ext.data).to.deep.equal(Object.assign({}, bidderRequest.bids[0].params.inventory, context)); + expect(request.data.user.ext.data).to.deep.equal(Object.assign({}, bidderRequest.bids[0].params.visitor, user)); + }); }); describe('combineSlotUrlParams', function () { From 1d0acf198049beffa7abeef0c8214777725e4062 Mon Sep 17 00:00:00 2001 From: Mark Monday Date: Fri, 15 Nov 2019 16:11:13 -0500 Subject: [PATCH 10/12] Place user and context within fpd object --- modules/prebidServerBidAdapter/index.js | 18 ++++++++---------- modules/rubiconBidAdapter.js | 12 ++++++------ .../modules/prebidServerBidAdapter_spec.js | 2 +- test/spec/modules/rubiconBidAdapter_spec.js | 18 +++++++++++------- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 373202256ad..69940dd06a9 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -301,20 +301,18 @@ function addFirstPartyDataToRequest(request) { const allowedBidders = []; Object.keys(bidderConfig).forEach(bidder => { const currBidderConfig = bidderConfig[bidder]; - let isAllowed = false; - - if (currBidderConfig.context) { - context = Object.assign({}, context, currBidderConfig.context); - isAllowed = true; + if (currBidderConfig.fpd) { + allowedBidders.push(bidder); + } else { + return; } - if (currBidderConfig.user) { - user = Object.assign({}, user, currBidderConfig.user); - isAllowed = true; + if (currBidderConfig.fpd.context) { + context = Object.assign({}, context, currBidderConfig.fpd.context); } - if (isAllowed) { - allowedBidders.push(bidder); + if (currBidderConfig.fpd.user) { + user = Object.assign({}, user, currBidderConfig.fpd.user); } }); diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 5acf70a3efc..205646fc1cc 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -272,8 +272,8 @@ export const spec = { utils.deepSetValue(data, 'source.ext.schain', bidRequest.schain); } - const siteData = Object.assign({}, bidRequest.params.inventory, config.getConfig('context')); - const userData = Object.assign({}, bidRequest.params.visitor, config.getConfig('user')); + const siteData = Object.assign({}, bidRequest.params.inventory, config.getConfig('fpd.context')); + const userData = Object.assign({}, bidRequest.params.visitor, config.getConfig('fpd.user')); if (!utils.isEmpty(siteData) || !utils.isEmpty(userData)) { utils.deepSetValue(data, 'ext.prebid.data.bidders', [ bidderRequest.bidderCode ]); @@ -483,7 +483,7 @@ export const spec = { } // visitor properties - const visitorData = Object.assign({}, params.visitor, config.getConfig('user')); + const visitorData = Object.assign({}, params.visitor, config.getConfig('fpd.user')); Object.keys(visitorData).forEach((key) => { if (visitorData[key] != null && key !== 'keywords') { data[`tg_v.${key}`] = typeof visitorData[key] === 'object' && !Array.isArray(visitorData[key]) @@ -493,7 +493,7 @@ export const spec = { }); // inventory properties - const inventoryData = Object.assign({}, params.inventory, config.getConfig('context')); + const inventoryData = Object.assign({}, params.inventory, config.getConfig('fpd.context')); Object.keys(inventoryData).forEach((key) => { if (inventoryData[key] != null && key !== 'keywords') { data[`tg_i.${key}`] = typeof inventoryData[key] === 'object' && !Array.isArray(inventoryData[key]) @@ -504,8 +504,8 @@ export const spec = { // keywords const keywords = (params.keywords || []).concat( - utils.deepAccess(config.getConfig('user'), 'keywords') || [], - utils.deepAccess(config.getConfig('context'), 'keywords') || []); + utils.deepAccess(config.getConfig('fpd.user'), 'keywords') || [], + utils.deepAccess(config.getConfig('fpd.context'), 'keywords') || []); data.kw = keywords.length ? keywords.join(',') : ''; // digitrust properties diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 0de439fffc4..5957e24915f 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1337,7 +1337,7 @@ describe('S2S Adapter', function () { }; const allowedBidders = [ 'rubicon', 'appnexus' ]; - config.setBidderConfig({ bidders: allowedBidders, config: { context, user } }); + config.setBidderConfig({ bidders: allowedBidders, config: { fpd: { context, user } } }); adapter.callBids(s2sBidRequest, bidRequests, addBidResponse, done, ajax); const parsedRequestBody = JSON.parse(requests[0].requestBody); expect(parsedRequestBody.ext.prebid.data.bidders).to.deep.equal(allowedBidders); diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index a3c14d76466..d2a31a5c92c 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -947,7 +947,7 @@ describe('the rubicon adapter', function () { }); }); - it('should use protected data in bidder request over the bid params, if present', () => { + it('should use first party data from getConfig over the bid params, if present', () => { const context = { keywords: ['e', 'f'], rating: '4-star' @@ -961,10 +961,12 @@ describe('the rubicon adapter', function () { sandbox.stub(config, 'getConfig').callsFake(key => { const config = { - context, - user + fpd: { + context, + user + } }; - return config[key]; + return utils.deepAccess(config, key); }); const expectedQuery = { @@ -1594,10 +1596,12 @@ describe('the rubicon adapter', function () { sandbox.stub(config, 'getConfig').callsFake(key => { const config = { - context, - user + fpd: { + context, + user + } }; - return config[key]; + return utils.deepAccess(config, key); }); const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); From 9ee631b4ed1655c6600c2a2c1c9ccd71ac7ebae1 Mon Sep 17 00:00:00 2001 From: Mark Monday Date: Fri, 13 Dec 2019 15:25:53 -0500 Subject: [PATCH 11/12] Separate global and bidder-specific first party data --- modules/prebidServerBidAdapter/index.js | 59 ++++++++++--------- modules/rubiconBidAdapter.js | 13 +++- .../modules/prebidServerBidAdapter_spec.js | 25 +++++--- test/spec/modules/rubiconBidAdapter_spec.js | 14 ++++- 4 files changed, 71 insertions(+), 40 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 8160fc1c571..e1328d3d9b0 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -11,6 +11,7 @@ import events from '../../src/events'; import includes from 'core-js/library/fn/array/includes'; import { S2S_VENDORS } from './config.js'; import { ajax } from '../../src/ajax'; +import find from 'core-js/library/fn/array/find'; const getConfig = config.getConfig; @@ -286,38 +287,35 @@ function _appendSiteAppDevice(request, pageUrl) { } } -function addFirstPartyDataToRequest(request) { +function addBidderFirstPartyDataToRequest(request) { const bidderConfig = config.getBidderConfig(); - let context = {}; - let user = {}; - const allowedBidders = []; - Object.keys(bidderConfig).forEach(bidder => { + const fpdConfigs = Object.keys(bidderConfig).reduce((acc, bidder) => { const currBidderConfig = bidderConfig[bidder]; if (currBidderConfig.fpd) { - allowedBidders.push(bidder); - } else { - return; - } - - if (currBidderConfig.fpd.context) { - context = Object.assign({}, context, currBidderConfig.fpd.context); - } - - if (currBidderConfig.fpd.user) { - user = Object.assign({}, user, currBidderConfig.fpd.user); - } - }); - - if (allowedBidders.length) { - utils.deepSetValue(request, 'ext.prebid.data.bidders', allowedBidders); + const fpd = {}; + if (currBidderConfig.fpd.context) { + fpd.site = currBidderConfig.fpd.context; + } + if (currBidderConfig.fpd.user) { + fpd.user = currBidderConfig.fpd.user; + } - if (!utils.isEmpty(context)) { - utils.deepSetValue(request, 'site.ext.data', context); + const matchingFpd = find(acc, addedFpd => + JSON.stringify(addedFpd.config.fpd) === JSON.stringify(fpd)); + if (matchingFpd) { + matchingFpd.bidders.push(bidder); + } else { + acc.push({ + bidders: [ bidder ], + config: { fpd } + }); + } } + return acc; + }, []); - if (!utils.isEmpty(user)) { - utils.deepSetValue(request, 'user.ext.data', user); - } + if (fpdConfigs.length) { + utils.deepSetValue(request, 'ext.prebid.bidderconfig', fpdConfigs); } } @@ -680,7 +678,14 @@ const OPEN_RTB_PROTOCOL = { utils.deepSetValue(request, 'regs.coppa', 1); } - addFirstPartyDataToRequest(request); + const commonFpd = getConfig('fpd') || {}; + if (commonFpd.context) { + utils.deepSetValue(request, 'site.ext.data', commonFpd.context); + } + if (commonFpd.user) { + utils.deepSetValue(request, 'user.ext.data', commonFpd.user); + } + addBidderFirstPartyDataToRequest(request); return request; }, diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 401aac2336f..1552cb11ada 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -270,15 +270,22 @@ export const spec = { const siteData = Object.assign({}, bidRequest.params.inventory, config.getConfig('fpd.context')); const userData = Object.assign({}, bidRequest.params.visitor, config.getConfig('fpd.user')); if (!utils.isEmpty(siteData) || !utils.isEmpty(userData)) { - utils.deepSetValue(data, 'ext.prebid.data.bidders', [ bidderRequest.bidderCode ]); + const bidderData = { + bidders: [ bidderRequest.bidderCode ], + config: { + fpd: {} + } + }; if (!utils.isEmpty(siteData)) { - utils.deepSetValue(data, 'site.ext.data', siteData); + bidderData.config.fpd.site = siteData; } if (!utils.isEmpty(userData)) { - utils.deepSetValue(data, 'user.ext.data', userData); + bidderData.config.fpd.user = userData; } + + utils.deepSetValue(data, 'ext.prebid.bidderconfig.0', bidderData); } return { diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 5bb9e993792..0fd1c31307f 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1297,9 +1297,16 @@ describe('S2S Adapter', function () { const s2sBidRequest = utils.deepClone(REQUEST); const bidRequests = utils.deepClone(BID_REQUESTS); - const context = { + const commonContext = { keywords: ['power tools'], - search: 'drill', + search: 'drill' + }; + const commonUser = { + keywords: ['a', 'b'], + gender: 'M' + }; + + const context = { content: { userrating: 4 }, data: { pageType: 'article', @@ -1307,8 +1314,6 @@ describe('S2S Adapter', function () { } }; const user = { - keywords: ['a', 'b'], - gender: 'M', yob: '1984', geo: { country: 'ca' }, data: { @@ -1318,12 +1323,18 @@ describe('S2S Adapter', function () { }; const allowedBidders = [ 'rubicon', 'appnexus' ]; + const expected = [{ + bidders: allowedBidders, + config: { fpd: { site: context, user } } + }]; + + config.setConfig({ fpd: { context: commonContext, user: commonUser } }); config.setBidderConfig({ bidders: allowedBidders, config: { fpd: { context, user } } }); adapter.callBids(s2sBidRequest, bidRequests, addBidResponse, done, ajax); const parsedRequestBody = JSON.parse(requests[0].requestBody); - expect(parsedRequestBody.ext.prebid.data.bidders).to.deep.equal(allowedBidders); - expect(parsedRequestBody.site.ext.data).to.deep.equal(context); - expect(parsedRequestBody.user.ext.data).to.deep.equal(user); + expect(parsedRequestBody.ext.prebid.bidderconfig).to.deep.equal(expected); + expect(parsedRequestBody.site.ext.data).to.deep.equal(commonContext); + expect(parsedRequestBody.user.ext.data).to.deep.equal(commonUser); }); }); diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 5b455a7f066..c2617bf804b 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1638,10 +1638,18 @@ describe('the rubicon adapter', function () { return utils.deepAccess(config, key); }); + const expected = [{ + bidders: [ 'rubicon' ], + config: { + fpd: { + site: Object.assign({}, bidderRequest.bids[0].params.inventory, context), + user: Object.assign({}, bidderRequest.bids[0].params.visitor, user) + } + } + }]; + const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - expect(request.data.ext.prebid.data.bidders).to.deep.equal([ 'rubicon' ]); - expect(request.data.site.ext.data).to.deep.equal(Object.assign({}, bidderRequest.bids[0].params.inventory, context)); - expect(request.data.user.ext.data).to.deep.equal(Object.assign({}, bidderRequest.bids[0].params.visitor, user)); + expect(request.data.ext.prebid.bidderconfig).to.deep.equal(expected); }); }); From 0bc36610efbfca4482287b17d49c14d06c9b62c2 Mon Sep 17 00:00:00 2001 From: Mark Monday Date: Thu, 13 Feb 2020 15:24:53 -0500 Subject: [PATCH 12/12] Repeat FPD for each bidder when sending to PBS --- modules/prebidServerBidAdapter/index.js | 14 ++++---------- test/spec/modules/prebidServerBidAdapter_spec.js | 6 +++--- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 7e4cc27adfa..8f7727a61aa 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -300,16 +300,10 @@ function addBidderFirstPartyDataToRequest(request) { fpd.user = currBidderConfig.fpd.user; } - const matchingFpd = find(acc, addedFpd => - JSON.stringify(addedFpd.config.fpd) === JSON.stringify(fpd)); - if (matchingFpd) { - matchingFpd.bidders.push(bidder); - } else { - acc.push({ - bidders: [ bidder ], - config: { fpd } - }); - } + acc.push({ + bidders: [ bidder ], + config: { fpd } + }); } return acc; }, []); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index b46b5615807..bc655c6e432 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1341,10 +1341,10 @@ describe('S2S Adapter', function () { }; const allowedBidders = [ 'rubicon', 'appnexus' ]; - const expected = [{ - bidders: allowedBidders, + const expected = allowedBidders.map(bidder => ({ + bidders: [ bidder ], config: { fpd: { site: context, user } } - }]; + })); config.setConfig({ fpd: { context: commonContext, user: commonUser } }); config.setBidderConfig({ bidders: allowedBidders, config: { fpd: { context, user } } });