From 8d635ff86ca3c3bde767bca6acee8b89d5b1f2ca Mon Sep 17 00:00:00 2001 From: nakamoto Date: Fri, 15 Feb 2019 17:28:34 +0900 Subject: [PATCH 01/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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 4e1679b686a74dcc6c714eee39f4bc62490e4072 Mon Sep 17 00:00:00 2001 From: Mark Monday Date: Tue, 17 Dec 2019 13:50:42 -0500 Subject: [PATCH 09/37] Re-add rubicon analytics without deprecated getTopWindowUrl util --- modules/rubiconAnalyticsAdapter.js | 484 +++++++++++ .../modules/rubiconAnalyticsAdapter_spec.js | 815 ++++++++++++++++++ 2 files changed, 1299 insertions(+) create mode 100644 modules/rubiconAnalyticsAdapter.js create mode 100644 test/spec/modules/rubiconAnalyticsAdapter_spec.js diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js new file mode 100644 index 00000000000..94307b495c0 --- /dev/null +++ b/modules/rubiconAnalyticsAdapter.js @@ -0,0 +1,484 @@ +import adapter from '../src/AnalyticsAdapter'; +import adapterManager from '../src/adapterManager'; +import CONSTANTS from '../src/constants.json'; +import { ajax } from '../src/ajax'; +import { config } from '../src/config'; +import * as utils from '../src/utils'; + +const { + EVENTS: { + AUCTION_INIT, + AUCTION_END, + BID_REQUESTED, + BID_RESPONSE, + BIDDER_DONE, + BID_TIMEOUT, + BID_WON, + SET_TARGETING + }, + STATUS: { + GOOD, + NO_BID + } +} = CONSTANTS; + +let serverConfig; +config.getConfig('s2sConfig', ({s2sConfig}) => { + serverConfig = s2sConfig; +}); + +export const SEND_TIMEOUT = 3000; +const DEFAULT_INTEGRATION = 'pbjs'; + +const cache = { + auctions: {}, + targeting: {}, + timeouts: {}, +}; + +function stringProperties(obj) { + return Object.keys(obj).reduce((newObj, prop) => { + let value = obj[prop]; + if (typeof value === 'number') { + value = value.toFixed(3); + } else if (typeof value !== 'string') { + value = String(value); + } + newObj[prop] = value; + return newObj; + }, {}); +} + +function sizeToDimensions(size) { + return { + width: size.w || size[0], + height: size.h || size[1] + }; +} + +function validMediaType(type) { + return ['banner', 'native', 'video'].indexOf(type) !== -1; +} + +function formatSource(src) { + if (typeof src === 'undefined') { + src = 'client'; + } else if (src === 's2s') { + src = 'server'; + } + return src.toLowerCase(); +} + +function sendMessage(auctionId, bidWonId) { + function formatBid(bid) { + return utils.pick(bid, [ + 'bidder', + 'bidId', bidId => utils.deepAccess(bid, 'bidResponse.seatBidId') || bidId, + 'status', + 'error', + 'source', (source, bid) => { + if (source) { + return source; + } + return serverConfig && Array.isArray(serverConfig.bidders) && serverConfig.bidders.indexOf(bid.bidder) !== -1 + ? 'server' : 'client' + }, + 'clientLatencyMillis', + 'serverLatencyMillis', + 'params', + 'bidResponse', bidResponse => bidResponse ? utils.pick(bidResponse, [ + 'bidPriceUSD', + 'dealId', + 'dimensions', + 'mediaType' + ]) : undefined + ]); + } + function formatBidWon(bid) { + return Object.assign(formatBid(bid), utils.pick(bid.adUnit, [ + 'adUnitCode', + 'transactionId', + 'videoAdFormat', () => bid.videoAdFormat, + 'mediaTypes' + ]), { + adserverTargeting: stringProperties(cache.targeting[bid.adUnit.adUnitCode] || {}), + bidwonStatus: 'success', // hard-coded for now + accountId, + siteId: bid.siteId, + zoneId: bid.zoneId, + samplingFactor + }); + } + let auctionCache = cache.auctions[auctionId]; + let referrer = config.getConfig('pageUrl') || auctionCache.referrer; + let message = { + eventTimeMillis: Date.now(), + integration: config.getConfig('rubicon.int_type') || DEFAULT_INTEGRATION, + version: '$prebid.version$', + referrerUri: referrer + }; + const wrapperName = config.getConfig('rubicon.wrapperName'); + if (wrapperName) { + message.wrapperName = wrapperName; + } + if (auctionCache && !auctionCache.sent) { + let adUnitMap = Object.keys(auctionCache.bids).reduce((adUnits, bidId) => { + let bid = auctionCache.bids[bidId]; + let adUnit = adUnits[bid.adUnit.adUnitCode]; + if (!adUnit) { + adUnit = adUnits[bid.adUnit.adUnitCode] = utils.pick(bid.adUnit, [ + 'adUnitCode', + 'transactionId', + 'mediaTypes', + 'dimensions', + 'adserverTargeting', () => stringProperties(cache.targeting[bid.adUnit.adUnitCode] || {}) + ]); + adUnit.bids = []; + } + + // Add site and zone id if not there and if we found a rubicon bidder + if ((!adUnit.siteId || !adUnit.zoneId) && rubiconAliases.indexOf(bid.bidder) !== -1) { + if (utils.deepAccess(bid, 'params.accountId') == accountId) { + adUnit.accountId = parseInt(accountId); + adUnit.siteId = parseInt(utils.deepAccess(bid, 'params.siteId')); + adUnit.zoneId = parseInt(utils.deepAccess(bid, 'params.zoneId')); + } + } + + if (bid.videoAdFormat && !adUnit.videoAdFormat) { + adUnit.videoAdFormat = bid.videoAdFormat; + } + + // determine adUnit.status from its bid statuses. Use priority below to determine, higher index is better + let statusPriority = ['error', 'no-bid', 'success']; + if (statusPriority.indexOf(bid.status) > statusPriority.indexOf(adUnit.status)) { + adUnit.status = bid.status; + } + + adUnit.bids.push(formatBid(bid)); + + return adUnits; + }, {}); + + // We need to mark each cached bid response with its appropriate rubicon site-zone id + // This allows the bidWon events to have these params even in the case of a delayed render + Object.keys(auctionCache.bids).forEach(function (bidId) { + let adCode = auctionCache.bids[bidId].adUnit.adUnitCode; + Object.assign(auctionCache.bids[bidId], utils.pick(adUnitMap[adCode], ['accountId', 'siteId', 'zoneId'])); + }); + + let auction = { + clientTimeoutMillis: auctionCache.timeout, + samplingFactor, + accountId, + adUnits: Object.keys(adUnitMap).map(i => adUnitMap[i]) + }; + + if (serverConfig) { + auction.serverTimeoutMillis = serverConfig.timeout; + } + + message.auctions = [auction]; + + let bidsWon = Object.keys(auctionCache.bidsWon).reduce((memo, adUnitCode) => { + let bidId = auctionCache.bidsWon[adUnitCode]; + if (bidId) { + memo.push(formatBidWon(auctionCache.bids[bidId])); + } + return memo; + }, []); + + if (bidsWon.length > 0) { + message.bidsWon = bidsWon; + } + + auctionCache.sent = true; + } else if (bidWonId && auctionCache && auctionCache.bids[bidWonId]) { + message.bidsWon = [ + formatBidWon(auctionCache.bids[bidWonId]) + ]; + } + + ajax( + this.getUrl(), + null, + JSON.stringify(message), + { + contentType: 'application/json' + } + ); +} + +function getBidPrice(bid) { + if (typeof bid.currency === 'string' && bid.currency.toUpperCase() === 'USD') { + return Number(bid.cpm); + } + // use currency conversion function if present + if (typeof bid.getCpmInNewCurrency === 'function') { + return Number(bid.getCpmInNewCurrency('USD')); + } + utils.logWarn('Rubicon Analytics Adapter: Could not determine the bidPriceUSD of the bid ', bid); +} + +export function parseBidResponse(bid, previousBidResponse) { + // The current bidResponse for this matching requestId/bidRequestId + let responsePrice = getBidPrice(bid) + // we need to compare it with the previous one (if there was one) + if (previousBidResponse && previousBidResponse.bidPriceUSD > responsePrice) { + return previousBidResponse; + } + return utils.pick(bid, [ + 'bidPriceUSD', () => responsePrice, + 'dealId', + 'status', + 'mediaType', + 'dimensions', () => utils.pick(bid, [ + 'width', + 'height' + ]), + 'seatBidId', + ]); +} + +let samplingFactor = 1; +let accountId; +// List of known rubicon aliases +// This gets updated on auction init to account for any custom aliases present +let rubiconAliases = ['rubicon']; + +/* + Checks the alias registry for any entries of the rubicon bid adapter. + adds to the rubiconAliases list if found +*/ +function setRubiconAliases(aliasRegistry) { + Object.keys(aliasRegistry).forEach(function (alias) { + if (aliasRegistry[alias] === 'rubicon') { + rubiconAliases.push(alias); + } + }); +} + +let baseAdapter = adapter({analyticsType: 'endpoint'}); +let rubiconAdapter = Object.assign({}, baseAdapter, { + enableAnalytics(config = {}) { + let error = false; + samplingFactor = 1; + + if (typeof config.options === 'object') { + if (config.options.accountId) { + accountId = Number(config.options.accountId); + } + if (config.options.endpoint) { + this.getUrl = () => config.options.endpoint; + } else { + utils.logError('required endpoint missing from rubicon analytics'); + error = true; + } + if (typeof config.options.sampling !== 'undefined') { + samplingFactor = 1 / parseFloat(config.options.sampling); + } + if (typeof config.options.samplingFactor !== 'undefined') { + if (typeof config.options.sampling !== 'undefined') { + utils.logWarn('Both options.samplingFactor and options.sampling enabled in rubicon analytics, defaulting to samplingFactor'); + } + samplingFactor = parseFloat(config.options.samplingFactor); + config.options.sampling = 1 / samplingFactor; + } + } + + let validSamplingFactors = [1, 10, 20, 40, 100]; + if (validSamplingFactors.indexOf(samplingFactor) === -1) { + error = true; + utils.logError('invalid samplingFactor for rubicon analytics: ' + samplingFactor + ', must be one of ' + validSamplingFactors.join(', ')); + } else if (!accountId) { + error = true; + utils.logError('required accountId missing for rubicon analytics'); + } + + if (!error) { + baseAdapter.enableAnalytics.call(this, config); + } + }, + disableAnalytics() { + this.getUrl = baseAdapter.getUrl; + accountId = null; + baseAdapter.disableAnalytics.apply(this, arguments); + }, + track({eventType, args}) { + switch (eventType) { + case AUCTION_INIT: + // set the rubicon aliases + setRubiconAliases(adapterManager.aliasRegistry); + let cacheEntry = utils.pick(args, [ + 'timestamp', + 'timeout' + ]); + cacheEntry.bids = {}; + cacheEntry.bidsWon = {}; + cache.auctions[args.auctionId] = cacheEntry; + break; + case BID_REQUESTED: + cache.auctions[args.auctionId].referrer = args.refererInfo.referer; + Object.assign(cache.auctions[args.auctionId].bids, args.bids.reduce((memo, bid) => { + // mark adUnits we expect bidWon events for + cache.auctions[args.auctionId].bidsWon[bid.adUnitCode] = false; + + memo[bid.bidId] = utils.pick(bid, [ + 'bidder', bidder => bidder.toLowerCase(), + 'bidId', + 'status', () => 'no-bid', // default a bid to no-bid until response is recieved or bid is timed out + 'finalSource as source', + 'params', (params, bid) => { + switch (bid.bidder) { + // specify bidder params we want here + case 'rubicon': + return utils.pick(params, [ + 'accountId', + 'siteId', + 'zoneId' + ]); + } + }, + 'videoAdFormat', (_, cachedBid) => { + if (cachedBid.bidder === 'rubicon') { + return ({ + 201: 'pre-roll', + 202: 'interstitial', + 203: 'outstream', + 204: 'mid-roll', + 205: 'post-roll', + 207: 'vertical' + })[utils.deepAccess(bid, 'params.video.size_id')]; + } else { + let startdelay = parseInt(utils.deepAccess(bid, 'params.video.startdelay'), 10); + if (!isNaN(startdelay)) { + if (startdelay > 0) { + return 'mid-roll'; + } + return ({ + '0': 'pre-roll', + '-1': 'mid-roll', + '-2': 'post-roll' + })[startdelay] + } + } + }, + 'adUnit', () => utils.pick(bid, [ + 'adUnitCode', + 'transactionId', + 'sizes as dimensions', sizes => sizes.map(sizeToDimensions), + 'mediaTypes', (types) => { + if (bid.mediaType && validMediaType(bid.mediaType)) { + return [bid.mediaType]; + } + if (Array.isArray(types)) { + return types.filter(validMediaType); + } + if (typeof types === 'object') { + if (!bid.sizes) { + bid.dimensions = []; + utils._each(types, (type) => + bid.dimensions = bid.dimensions.concat( + type.sizes.map(sizeToDimensions) + ) + ); + } + return Object.keys(types).filter(validMediaType); + } + return ['banner']; + } + ]) + ]); + return memo; + }, {})); + break; + case BID_RESPONSE: + let bid = cache.auctions[args.auctionId].bids[args.requestId]; + if (!bid) { + utils.logError('Rubicon Anlytics Adapter Error: Could not find associated bid request for bid response with requestId: ', args.requestId); + break; + } + bid.source = formatSource(bid.source || args.source); + switch (args.getStatusCode()) { + case GOOD: + bid.status = 'success'; + delete bid.error; // it's possible for this to be set by a previous timeout + break; + case NO_BID: + bid.status = 'no-bid'; + delete bid.error; + break; + default: + bid.status = 'error'; + bid.error = { + code: 'request-error' + }; + } + bid.clientLatencyMillis = Date.now() - cache.auctions[args.auctionId].timestamp; + bid.bidResponse = parseBidResponse(args, bid.bidResponse); + // RP server banner overwrites bidId with bid.seatBidId + if (utils.deepAccess(bid, 'bidResponse.seatBidId') && bid.bidder === 'rubicon' && bid.source === 'server' && ['video', 'banner'].some(i => utils.deepAccess(bid, 'bidResponse.mediaType') === i)) { + bid.seatBidId = bid.bidResponse.seatBidId; + } + break; + case BIDDER_DONE: + args.bids.forEach(bid => { + let cachedBid = cache.auctions[bid.auctionId].bids[bid.bidId || bid.requestId]; + if (typeof bid.serverResponseTimeMs !== 'undefined') { + cachedBid.serverLatencyMillis = bid.serverResponseTimeMs; + } + if (!cachedBid.status) { + cachedBid.status = 'no-bid'; + } + if (!cachedBid.clientLatencyMillis) { + cachedBid.clientLatencyMillis = Date.now() - cache.auctions[bid.auctionId].timestamp; + } + }); + break; + case SET_TARGETING: + Object.assign(cache.targeting, args); + break; + case BID_WON: + let auctionCache = cache.auctions[args.auctionId]; + auctionCache.bidsWon[args.adUnitCode] = args.requestId; + + // check if this BID_WON missed the boat, if so send by itself + if (auctionCache.sent === true) { + sendMessage.call(this, args.auctionId, args.requestId); + } else if (Object.keys(auctionCache.bidsWon).reduce((memo, adUnitCode) => { + // only send if we've received bidWon events for all adUnits in auction + memo = memo && auctionCache.bidsWon[adUnitCode]; + return memo; + }, true)) { + clearTimeout(cache.timeouts[args.auctionId]); + delete cache.timeouts[args.auctionId]; + + sendMessage.call(this, args.auctionId); + } + break; + case AUCTION_END: + // start timer to send batched payload just in case we don't hear any BID_WON events + cache.timeouts[args.auctionId] = setTimeout(() => { + sendMessage.call(this, args.auctionId); + }, SEND_TIMEOUT); + break; + case BID_TIMEOUT: + args.forEach(badBid => { + let auctionCache = cache.auctions[badBid.auctionId]; + let bid = auctionCache.bids[badBid.bidId || badBid.requestId]; + bid.status = 'error'; + bid.error = { + code: 'timeout-error' + }; + }); + break; + } + } +}); + +adapterManager.registerAnalyticsAdapter({ + adapter: rubiconAdapter, + code: 'rubicon' +}); + +export default rubiconAdapter; diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..a4d07c35bc4 --- /dev/null +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -0,0 +1,815 @@ +import rubiconAnalyticsAdapter, { SEND_TIMEOUT, parseBidResponse } from 'modules/rubiconAnalyticsAdapter'; +import CONSTANTS from 'src/constants.json'; +import { config } from 'src/config'; + +import { + setConfig, + addBidResponseHook, +} from 'modules/currency'; + +let Ajv = require('ajv'); +let schema = require('./rubiconAnalyticsSchema.json'); +let ajv = new Ajv({ + allErrors: true +}); + +let validator = ajv.compile(schema); + +function validate(message) { + validator(message); + expect(validator.errors).to.deep.equal(null); +} + +// using es6 "import * as events from 'src/events'" causes the events.getEvents stub not to work... +let events = require('src/events'); +let ajax = require('src/ajax'); +let utils = require('src/utils'); + +const { + EVENTS: { + AUCTION_INIT, + AUCTION_END, + BID_REQUESTED, + BID_RESPONSE, + BIDDER_DONE, + BID_WON, + BID_TIMEOUT, + SET_TARGETING + } +} = CONSTANTS; + +const BID = { + 'bidder': 'rubicon', + 'width': 640, + 'height': 480, + 'mediaType': 'video', + 'statusMessage': 'Bid available', + 'bidId': '2ecff0db240757', + 'adId': 'fake_ad_id', + 'source': 'client', + 'requestId': '2ecff0db240757', + 'currency': 'USD', + 'creativeId': '3571560', + 'cpm': 1.22752, + 'ttl': 300, + 'netRevenue': false, + 'ad': '', + 'rubiconTargeting': { + 'rpfl_elemid': '/19968336/header-bid-tag-0', + 'rpfl_14062': '2_tier0100' + }, + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'responseTimestamp': 1519149629415, + 'requestTimestamp': 1519149628471, + 'adUnitCode': '/19968336/header-bid-tag-0', + 'timeToRespond': 944, + 'pbLg': '1.00', + 'pbMg': '1.20', + 'pbHg': '1.22', + 'pbAg': '1.20', + 'pbDg': '1.22', + 'pbCg': '', + 'size': '640x480', + 'adserverTargeting': { + 'hb_bidder': 'rubicon', + 'hb_adid': '2ecff0db240757', + 'hb_pb': 1.20, + 'hb_size': '640x480', + 'hb_source': 'client' + }, + getStatusCode() { + return 1; + } +}; + +const BID2 = Object.assign({}, BID, { + adUnitCode: '/19968336/header-bid-tag1', + bidId: '3bd4ebb1c900e2', + adId: 'fake_ad_id', + requestId: '3bd4ebb1c900e2', + width: 728, + height: 90, + mediaType: 'banner', + cpm: 1.52, + source: 'server', + seatBidId: 'aaaa-bbbb-cccc-dddd', + rubiconTargeting: { + 'rpfl_elemid': '/19968336/header-bid-tag1', + 'rpfl_14062': '2_tier0100' + }, + adserverTargeting: { + 'hb_bidder': 'rubicon', + 'hb_adid': '3bd4ebb1c900e2', + 'hb_pb': '1.500', + 'hb_size': '728x90', + 'hb_source': 'server' + } +}); + +const MOCK = { + SET_TARGETING: { + [BID.adUnitCode]: BID.adserverTargeting, + [BID2.adUnitCode]: BID2.adserverTargeting + }, + AUCTION_INIT: { + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'timestamp': 1519767010567, + 'auctionStatus': 'inProgress', + 'adUnits': [ { + 'code': '/19968336/header-bid-tag1', + 'sizes': [[640, 480]], + 'bids': [ { + 'bidder': 'rubicon', + 'params': { + 'accountId': 1001, 'siteId': 113932, 'zoneId': 535512 + } + } ], + 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014' + } + ], + 'adUnitCodes': ['/19968336/header-bid-tag1'], + 'bidderRequests': [ { + 'bidderCode': 'rubicon', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'bidderRequestId': '1be65d7958826a', + 'bids': [ { + 'bidder': 'rubicon', + 'params': { + 'accountId': 1001, 'siteId': 113932, 'zoneId': 535512 + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[640, 480]] + } + }, + 'adUnitCode': '/19968336/header-bid-tag1', + 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', + 'sizes': [[640, 480]], + 'bidId': '2ecff0db240757', + 'bidderRequestId': '1be65d7958826a', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'src': 'client', + 'bidRequestsCount': 1 + } + ], + 'timeout': 3000, + 'refererInfo': { + 'referer': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html'] + } + } + ], + 'bidsReceived': [], + 'winningBids': [], + 'timeout': 3000, + 'config': { + 'accountId': 1001, 'endpoint': '//localhost:9999/event' + } + }, + BID_REQUESTED: { + 'bidder': 'rubicon', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', + 'bidderRequestId': '1be65d7958826a', + 'bids': [ + { + 'bidder': 'rubicon', + 'params': { + 'accountId': '1001', + 'siteId': '70608', + 'zoneId': '335918', + 'userId': '12346', + 'keywords': ['a', 'b', 'c'], + 'inventory': 'test', + 'visitor': {'ucat': 'new', 'lastsearch': 'iphone'}, + 'position': 'btf', + 'video': { + 'language': 'en', + 'playerHeight': 480, + 'playerWidth': 640, + 'size_id': 203, + 'skip': 1, + 'skipdelay': 15, + 'aeParams': { + 'p_aso.video.ext.skip': '1', + 'p_aso.video.ext.skipdelay': '15' + } + } + }, + 'mediaType': 'video', + 'adUnitCode': '/19968336/header-bid-tag-0', + 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', + 'sizes': [[640, 480]], + 'bidId': '2ecff0db240757', + 'bidderRequestId': '1be65d7958826a', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' + }, + { + 'bidder': 'rubicon', + 'params': { + 'accountId': '14062', + 'siteId': '70608', + 'zoneId': '335918', + 'userId': '12346', + 'keywords': ['a', 'b', 'c'], + 'inventory': {'rating': '4-star', 'prodtype': 'tech'}, + 'visitor': {'ucat': 'new', 'lastsearch': 'iphone'}, + 'position': 'atf' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[1000, 300], [970, 250], [728, 90]] + } + }, + 'adUnitCode': '/19968336/header-bid-tag1', + 'transactionId': 'c116413c-9e3f-401a-bee1-d56aec29a1d4', + 'sizes': [[1000, 300], [970, 250], [728, 90]], + 'bidId': '3bd4ebb1c900e2', + 'seatBidId': 'aaaa-bbbb-cccc-dddd', + 'bidderRequestId': '1be65d7958826a', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' + } + ], + 'auctionStart': 1519149536560, + 'timeout': 5000, + 'start': 1519149562216, + 'refererInfo': { + 'referer': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html'] + } + }, + BID_RESPONSE: [ + BID, + BID2 + ], + AUCTION_END: { + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' + }, + BID_WON: [ + Object.assign({}, BID, { + 'status': 'rendered' + }), + Object.assign({}, BID2, { + 'status': 'rendered' + }) + ], + BIDDER_DONE: { + 'bidderCode': 'rubicon', + 'bids': [ + BID, + Object.assign({}, BID2, { + 'serverResponseTimeMs': 42, + }) + ] + }, + BID_TIMEOUT: [ + { + 'bidId': '2ecff0db240757', + 'bidder': 'rubicon', + 'adUnitCode': '/19968336/header-bid-tag-0', + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' + } + ] +}; + +const ANALYTICS_MESSAGE = { + 'eventTimeMillis': 1519767013781, + 'integration': 'pbjs', + 'version': '$prebid.version$', + 'referrerUri': 'http://www.test.com/page.html', + 'auctions': [ + { + 'clientTimeoutMillis': 3000, + 'serverTimeoutMillis': 1000, + 'accountId': 1001, + 'samplingFactor': 1, + 'adUnits': [ + { + 'adUnitCode': '/19968336/header-bid-tag-0', + 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', + 'videoAdFormat': 'outstream', + 'mediaTypes': [ + 'video' + ], + 'dimensions': [ + { + 'width': 640, + 'height': 480 + } + ], + 'status': 'success', + 'accountId': 1001, + 'siteId': 70608, + 'zoneId': 335918, + 'adserverTargeting': { + 'hb_bidder': 'rubicon', + 'hb_adid': '2ecff0db240757', + 'hb_pb': '1.200', + 'hb_size': '640x480', + 'hb_source': 'client' + }, + 'bids': [ + { + 'bidder': 'rubicon', + 'bidId': '2ecff0db240757', + 'status': 'success', + 'source': 'client', + 'clientLatencyMillis': 3214, + 'params': { + 'accountId': '1001', + 'siteId': '70608', + 'zoneId': '335918' + }, + 'bidResponse': { + 'bidPriceUSD': 1.22752, + 'dimensions': { + 'width': 640, + 'height': 480 + }, + 'mediaType': 'video' + } + } + ] + }, + { + 'adUnitCode': '/19968336/header-bid-tag1', + 'transactionId': 'c116413c-9e3f-401a-bee1-d56aec29a1d4', + 'mediaTypes': [ + 'banner' + ], + 'dimensions': [ + { + 'width': 1000, + 'height': 300 + }, + { + 'width': 970, + 'height': 250 + }, + { + 'width': 728, + 'height': 90 + } + ], + 'status': 'success', + 'adserverTargeting': { + 'hb_bidder': 'rubicon', + 'hb_adid': '3bd4ebb1c900e2', + 'hb_pb': '1.500', + 'hb_size': '728x90', + 'hb_source': 'server' + }, + 'bids': [ + { + 'bidder': 'rubicon', + 'bidId': 'aaaa-bbbb-cccc-dddd', + 'status': 'success', + 'source': 'server', + 'clientLatencyMillis': 3214, + 'serverLatencyMillis': 42, + 'params': { + 'accountId': '14062', + 'siteId': '70608', + 'zoneId': '335918' + }, + 'bidResponse': { + 'bidPriceUSD': 1.52, + 'dimensions': { + 'width': 728, + 'height': 90 + }, + 'mediaType': 'banner' + } + } + ] + } + ] + } + ], + 'bidsWon': [ + { + 'bidder': 'rubicon', + 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', + 'adUnitCode': '/19968336/header-bid-tag-0', + 'bidId': '2ecff0db240757', + 'status': 'success', + 'source': 'client', + 'clientLatencyMillis': 3214, + 'samplingFactor': 1, + 'accountId': 1001, + 'siteId': 70608, + 'zoneId': 335918, + 'params': { + 'accountId': '1001', + 'siteId': '70608', + 'zoneId': '335918' + }, + 'videoAdFormat': 'outstream', + 'mediaTypes': [ + 'video' + ], + 'adserverTargeting': { + 'hb_bidder': 'rubicon', + 'hb_adid': '2ecff0db240757', + 'hb_pb': '1.200', + 'hb_size': '640x480', + 'hb_source': 'client' + }, + 'bidResponse': { + 'bidPriceUSD': 1.22752, + 'dimensions': { + 'width': 640, + 'height': 480 + }, + 'mediaType': 'video' + }, + 'bidwonStatus': 'success' + }, + { + 'bidder': 'rubicon', + 'transactionId': 'c116413c-9e3f-401a-bee1-d56aec29a1d4', + 'adUnitCode': '/19968336/header-bid-tag1', + 'bidId': 'aaaa-bbbb-cccc-dddd', + 'status': 'success', + 'source': 'server', + 'clientLatencyMillis': 3214, + 'serverLatencyMillis': 42, + 'samplingFactor': 1, + 'accountId': 1001, + 'params': { + 'accountId': '14062', + 'siteId': '70608', + 'zoneId': '335918' + }, + 'mediaTypes': [ + 'banner' + ], + 'adserverTargeting': { + 'hb_bidder': 'rubicon', + 'hb_adid': '3bd4ebb1c900e2', + 'hb_pb': '1.500', + 'hb_size': '728x90', + 'hb_source': 'server' + }, + 'bidResponse': { + 'bidPriceUSD': 1.52, + 'dimensions': { + 'width': 728, + 'height': 90 + }, + 'mediaType': 'banner' + }, + 'bidwonStatus': 'success' + } + ], + 'wrapperName': '10000_fakewrapper_test' +}; + +function performStandardAuction() { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(BID_WON, MOCK.BID_WON[0]); + events.emit(BID_WON, MOCK.BID_WON[1]); +} + +describe('rubicon analytics adapter', function () { + let sandbox; + let xhr; + let requests; + let oldScreen; + let clock; + + beforeEach(function () { + sandbox = sinon.sandbox.create(); + + xhr = sandbox.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = request => requests.push(request); + + sandbox.stub(events, 'getEvents').returns([]); + + clock = sandbox.useFakeTimers(1519767013781); + + config.setConfig({ + s2sConfig: { + timeout: 1000, + accountId: 10000, + }, + rubicon: { + wrapperName: '10000_fakewrapper_test' + } + }) + }); + + afterEach(function () { + sandbox.restore(); + config.resetConfig(); + }); + + it('should require accountId', function () { + sandbox.stub(utils, 'logError'); + + rubiconAnalyticsAdapter.enableAnalytics({ + options: { + endpoint: '//localhost:9999/event' + } + }); + + expect(utils.logError.called).to.equal(true); + }); + + it('should require endpoint', function () { + sandbox.stub(utils, 'logError'); + + rubiconAnalyticsAdapter.enableAnalytics({ + options: { + accountId: 1001 + } + }); + + expect(utils.logError.called).to.equal(true); + }); + + describe('sampling', function () { + beforeEach(function () { + sandbox.stub(Math, 'random').returns(0.08); + sandbox.stub(utils, 'logError'); + }); + + afterEach(function () { + rubiconAnalyticsAdapter.disableAnalytics(); + }); + + describe('with options.samplingFactor', function () { + it('should sample', function () { + rubiconAnalyticsAdapter.enableAnalytics({ + options: { + endpoint: '//localhost:9999/event', + accountId: 1001, + samplingFactor: 10 + } + }); + + performStandardAuction(); + + expect(requests.length).to.equal(1); + }); + + it('should unsample', function () { + rubiconAnalyticsAdapter.enableAnalytics({ + options: { + endpoint: '//localhost:9999/event', + accountId: 1001, + samplingFactor: 20 + } + }); + + performStandardAuction(); + + expect(requests.length).to.equal(0); + }); + + it('should throw errors for invalid samplingFactor', function () { + rubiconAnalyticsAdapter.enableAnalytics({ + options: { + endpoint: '//localhost:9999/event', + accountId: 1001, + samplingFactor: 30 + } + }); + + performStandardAuction(); + + expect(requests.length).to.equal(0); + expect(utils.logError.called).to.equal(true); + }); + }); + describe('with options.sampling', function () { + it('should sample', function () { + rubiconAnalyticsAdapter.enableAnalytics({ + options: { + endpoint: '//localhost:9999/event', + accountId: 1001, + sampling: 0.1 + } + }); + + performStandardAuction(); + + expect(requests.length).to.equal(1); + }); + + it('should unsample', function () { + rubiconAnalyticsAdapter.enableAnalytics({ + options: { + endpoint: '//localhost:9999/event', + accountId: 1001, + sampling: 0.05 + } + }); + + performStandardAuction(); + + expect(requests.length).to.equal(0); + }); + + it('should throw errors for invalid samplingFactor', function () { + rubiconAnalyticsAdapter.enableAnalytics({ + options: { + endpoint: '//localhost:9999/event', + accountId: 1001, + sampling: 1 / 30 + } + }); + + performStandardAuction(); + + expect(requests.length).to.equal(0); + expect(utils.logError.called).to.equal(true); + }); + }); + }); + + describe('when handling events', function () { + beforeEach(function () { + rubiconAnalyticsAdapter.enableAnalytics({ + options: { + endpoint: '//localhost:9999/event', + accountId: 1001 + } + }); + }); + + afterEach(function () { + rubiconAnalyticsAdapter.disableAnalytics(); + }); + + it('should build a batched message from prebid events', function () { + performStandardAuction(); + + expect(requests.length).to.equal(1); + let request = requests[0]; + + expect(request.url).to.equal('//localhost:9999/event'); + + let message = JSON.parse(request.requestBody); + validate(message); + + expect(message).to.deep.equal(ANALYTICS_MESSAGE); + }); + + it('should pick the highest cpm bid if more than one bid per bidRequestId', function () { + // Only want one bid request in our mock auction + let bidRequested = utils.deepClone(MOCK.BID_REQUESTED); + bidRequested.bids.shift(); + let auctionInit = utils.deepClone(MOCK.AUCTION_INIT); + auctionInit.adUnits.shift(); + + // clone the mock bidResponse and duplicate + let duplicateResponse1 = utils.deepClone(BID2); + duplicateResponse1.cpm = 1.0; + duplicateResponse1.adserverTargeting.hb_pb = '1.0'; + duplicateResponse1.adserverTargeting.hb_adid = '1111'; + let duplicateResponse2 = utils.deepClone(BID2); + duplicateResponse2.cpm = 5.5; + duplicateResponse2.adserverTargeting.hb_pb = '5.5'; + duplicateResponse2.adserverTargeting.hb_adid = '5555'; + let duplicateResponse3 = utils.deepClone(BID2); + duplicateResponse3.cpm = 0.1; + duplicateResponse3.adserverTargeting.hb_pb = '0.1'; + duplicateResponse3.adserverTargeting.hb_adid = '3333'; + + const setTargeting = { + [duplicateResponse2.adUnitCode]: duplicateResponse2.adserverTargeting + }; + + const bidWon = Object.assign({}, duplicateResponse2, { + 'status': 'rendered' + }); + + // spoof the auction with just our duplicates + events.emit(AUCTION_INIT, auctionInit); + events.emit(BID_REQUESTED, bidRequested); + events.emit(BID_RESPONSE, duplicateResponse1); + events.emit(BID_RESPONSE, duplicateResponse2); + events.emit(BID_RESPONSE, duplicateResponse3); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, setTargeting); + events.emit(BID_WON, bidWon); + + let message = JSON.parse(requests[0].requestBody); + validate(message); + expect(message.auctions[0].adUnits[0].bids[0].bidResponse.bidPriceUSD).to.equal(5.5); + expect(message.auctions[0].adUnits[0].adserverTargeting.hb_pb).to.equal('5.5'); + expect(message.auctions[0].adUnits[0].adserverTargeting.hb_adid).to.equal('5555'); + expect(message.bidsWon.length).to.equal(1); + expect(message.bidsWon[0].bidResponse.bidPriceUSD).to.equal(5.5); + expect(message.bidsWon[0].adserverTargeting.hb_pb).to.equal('5.5'); + expect(message.bidsWon[0].adserverTargeting.hb_adid).to.equal('5555'); + }); + + it('should send batched message without BID_WON if necessary and further BID_WON events individually', function () { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(BID_WON, MOCK.BID_WON[0]); + + clock.tick(SEND_TIMEOUT + 1000); + + events.emit(BID_WON, MOCK.BID_WON[1]); + + expect(requests.length).to.equal(2); + + let message = JSON.parse(requests[0].requestBody); + validate(message); + expect(message.bidsWon.length).to.equal(1); + expect(message.auctions).to.deep.equal(ANALYTICS_MESSAGE.auctions); + expect(message.bidsWon[0]).to.deep.equal(ANALYTICS_MESSAGE.bidsWon[0]); + + message = JSON.parse(requests[1].requestBody); + validate(message); + expect(message.bidsWon.length).to.equal(1); + expect(message).to.not.have.property('auctions'); + expect(message.bidsWon[0]).to.deep.equal(ANALYTICS_MESSAGE.bidsWon[1]); + }); + + it('should properly mark bids as timed out', function () { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + events.emit(BID_TIMEOUT, MOCK.BID_TIMEOUT); + events.emit(AUCTION_END, MOCK.AUCTION_END); + + clock.tick(SEND_TIMEOUT + 1000); + + expect(requests.length).to.equal(1); + + let message = JSON.parse(requests[0].requestBody); + validate(message); + let timedOutBid = message.auctions[0].adUnits[0].bids[0]; + expect(timedOutBid.status).to.equal('error'); + expect(timedOutBid.error.code).to.equal('timeout-error'); + expect(timedOutBid).to.not.have.property('bidResponse'); + }); + + it('should successfully convert bid price to USD in parseBidResponse', function () { + // Set the rates + setConfig({ + adServerCurrency: 'JPY', + rates: { + USD: { + JPY: 100 + } + } + }); + + // set our bid response to JPY + const bidCopy = utils.deepClone(BID2); + bidCopy.currency = 'JPY'; + bidCopy.cpm = 100; + + // Now add the bidResponse hook which hooks on the currenct conversion function onto the bid response + let innerBid; + addBidResponseHook(function(adCodeId, bid) { + innerBid = bid; + }, 'elementId', bidCopy); + + // Use the rubi analytics parseBidResponse Function to get the resulting cpm from the bid response! + const bidResponseObj = parseBidResponse(innerBid); + expect(bidResponseObj).to.have.property('bidPriceUSD'); + expect(bidResponseObj.bidPriceUSD).to.equal(1.0); + }); + }); + + describe('config with integration type', () => { + it('should use the integration type provided in the config instead of the default', () => { + sandbox.stub(config, 'getConfig').callsFake(function (key) { + const config = { + 'rubicon.int_type': 'testType' + }; + return config[key]; + }); + + rubiconAnalyticsAdapter.enableAnalytics({ + options: { + endpoint: '//localhost:9999/event', + accountId: 1001 + } + }); + + performStandardAuction(); + + expect(requests.length).to.equal(1); + const request = requests[0]; + const message = JSON.parse(request.requestBody); + expect(message.integration).to.equal('testType'); + + rubiconAnalyticsAdapter.disableAnalytics(); + }); + }); +}); From fd80acd9cb842bc4eb6e0e46565a82dbbf972685 Mon Sep 17 00:00:00 2001 From: Mark Monday Date: Tue, 17 Dec 2019 15:01:07 -0500 Subject: [PATCH 10/37] Cache referrer on auction_init instead of bid_requested --- modules/rubiconAnalyticsAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 94307b495c0..b130a881c72 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -315,10 +315,10 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { ]); cacheEntry.bids = {}; cacheEntry.bidsWon = {}; + cacheEntry.referrer = args.bidderRequests[0].refererInfo.referer; cache.auctions[args.auctionId] = cacheEntry; break; case BID_REQUESTED: - cache.auctions[args.auctionId].referrer = args.refererInfo.referer; Object.assign(cache.auctions[args.auctionId].bids, args.bids.reduce((memo, bid) => { // mark adUnits we expect bidWon events for cache.auctions[args.auctionId].bidsWon[bid.adUnitCode] = false; From 5574a52412fdfe891303a3195ae1837020925bfc Mon Sep 17 00:00:00 2001 From: idettman Date: Fri, 24 Apr 2020 07:53:49 -0700 Subject: [PATCH 11/37] merged remote master changes --- modules/advangelistsBidAdapter.js | 0 modules/advangelistsBidAdapter.md | 0 modules/criteoBidAdapter.js | 9 +- modules/lunamediaBidAdapter.js | 383 ++++++++++++++++++ modules/lunamediaBidAdapter.md | 67 +++ modules/marsmediaBidAdapter.js | 2 +- package.json | 2 +- src/prebid.js | 1 - test/spec/modules/criteoBidAdapter_spec.js | 5 +- test/spec/modules/lunamediaBidAdapter_spec.js | 137 +++++++ test/spec/modules/marsmediaBidAdapter_spec.js | 4 +- 11 files changed, 596 insertions(+), 14 deletions(-) mode change 100644 => 100755 modules/advangelistsBidAdapter.js mode change 100644 => 100755 modules/advangelistsBidAdapter.md create mode 100755 modules/lunamediaBidAdapter.js create mode 100755 modules/lunamediaBidAdapter.md create mode 100755 test/spec/modules/lunamediaBidAdapter_spec.js diff --git a/modules/advangelistsBidAdapter.js b/modules/advangelistsBidAdapter.js old mode 100644 new mode 100755 diff --git a/modules/advangelistsBidAdapter.md b/modules/advangelistsBidAdapter.md old mode 100644 new mode 100755 diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index eca1000536c..5d137c994fe 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -8,10 +8,9 @@ import { verify } from 'criteo-direct-rsa-validate/build/verify.js'; import { getStorageManager } from '../src/storageManager.js'; const GVLID = 91; -export const ADAPTER_VERSION = 28; +export const ADAPTER_VERSION = 29; const BIDDER_CODE = 'criteo'; const CDB_ENDPOINT = 'https://bidder.criteo.com/cdb'; -const CRITEO_VENDOR_ID = 91; const PROFILE_ID_INLINE = 207; export const PROFILE_ID_PUBLISHERTAG = 185; const storage = getStorageManager(GVLID); @@ -259,6 +258,7 @@ function checkNativeSendId(bidRequest) { /** * @param {CriteoContext} context * @param {BidRequest[]} bidRequests + * @param bidderRequest * @return {*} */ function buildCdbRequest(context, bidRequests, bidderRequest) { @@ -328,10 +328,7 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { if (typeof bidderRequest.gdprConsent.gdprApplies !== 'undefined') { request.gdprConsent.gdprApplies = !!(bidderRequest.gdprConsent.gdprApplies); } - if (bidderRequest.gdprConsent.vendorData && bidderRequest.gdprConsent.vendorData.vendorConsents && - typeof bidderRequest.gdprConsent.vendorData.vendorConsents[ CRITEO_VENDOR_ID.toString(10) ] !== 'undefined') { - request.gdprConsent.consentGiven = !!(bidderRequest.gdprConsent.vendorData.vendorConsents[ CRITEO_VENDOR_ID.toString(10) ]); - } + request.gdprConsent.version = bidderRequest.gdprConsent.apiVersion; if (typeof bidderRequest.gdprConsent.consentString !== 'undefined') { request.gdprConsent.consentData = bidderRequest.gdprConsent.consentString; } diff --git a/modules/lunamediaBidAdapter.js b/modules/lunamediaBidAdapter.js new file mode 100755 index 00000000000..ee431deea1b --- /dev/null +++ b/modules/lunamediaBidAdapter.js @@ -0,0 +1,383 @@ +import * as utils from '../src/utils.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { VIDEO, BANNER } from '../src/mediaTypes.js'; +import find from 'core-js/library/fn/array/find.js'; +import includes from 'core-js/library/fn/array/includes.js'; + +const ADAPTER_VERSION = '1.0'; +const BIDDER_CODE = 'lunamedia'; + +export const VIDEO_ENDPOINT = 'https://api.lunamedia.io/xp/get?pubid=';// 0cf8d6d643e13d86a5b6374148a4afac'; +export const BANNER_ENDPOINT = 'https://api.lunamedia.io/xp/get?pubid=';// 0cf8d6d643e13d86a5b6374148a4afac'; +export const OUTSTREAM_SRC = 'https://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 + }); + }); + return requests; + }, + + interpretResponse(serverResponse, {bidRequest}) { + let response = serverResponse.body; + if (response !== null && utils.isEmpty(response) == false) { + if (isVideoBid(bidRequest)) { + 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 { + 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 + } + } + } + } +}; + +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 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); + const 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; + const 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'); + let floor = getVideoBidParam(bid, 'floor'); + if (floor == null) { floor = 0.5; } + + for (let j = 0; j < sizes.length; j++) { + o.imp.push({ + 'id': '' + j, + 'displaymanager': '' + BIDDER_CODE, + 'displaymanagerver': '' + ADAPTER_VERSION, + 'tagId': placement, + 'bidfloor': floor, + '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 utils.parseUrl(config.getConfig('pageUrl') || url, { decodeSearchAsString: true }); +} + +function createBannerRequestData(bid, bidderRequest) { + let topLocation = getTopWindowLocation(bidderRequest); + let topReferrer = getTopWindowReferrer(); + + let sizes = getBannerSizes(bid); + + const 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; + const 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]; + + let floor = getBannerBidParam(bid, 'floor'); + if (floor == null) { floor = 0.1; } + + o.imp.push({ + 'id': '' + j, + 'displaymanager': '' + BIDDER_CODE, + 'displaymanagerver': '' + ADAPTER_VERSION, + 'tagId': placement, + 'bidfloor': floor, + '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/lunamediaBidAdapter.md b/modules/lunamediaBidAdapter.md new file mode 100755 index 00000000000..d0314d0fa41 --- /dev/null +++ b/modules/lunamediaBidAdapter.md @@ -0,0 +1,67 @@ +# Overview + +``` +Module Name: LunaMedia Bidder Adapter +Module Type: Bidder Adapter +Maintainer: lokesh@advangelists.com +``` + +# Description + +Connects to LunaMedia exchange for bids. + +LunaMedia bid adapter supports Banner and Video ads currently. + +For more informatio + +# Sample Display Ad Unit: For Publishers +```javascript + +var displayAdUnit = [ +{ + code: 'display', + mediaTypes: { + banner: { + sizes: [[300, 250],[320, 50]] + } + } + bids: [{ + bidder: 'lunamedia', + params: { + pubid: '121ab139faf7ac67428a23f1d0a9a71b', + 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: 'lunamedia', + params: { + pubid: '121ab139faf7ac67428a23f1d0a9a71b', + 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/modules/marsmediaBidAdapter.js b/modules/marsmediaBidAdapter.js index 0d0526778ff..b6dc7800dd9 100644 --- a/modules/marsmediaBidAdapter.js +++ b/modules/marsmediaBidAdapter.js @@ -203,7 +203,7 @@ function MarsmediaAdapter() { return []; } - var uri = 'https://hb.azeriondigital.com/bidder/?bid=3mhdom&zoneId=' + fallbackZoneId; + var uri = 'https://hb.go2speed.media/bidder/?bid=3mhdom&zoneId=' + fallbackZoneId; var fat = /(^v|(\.0)+$)/gi; var prebidVersion = '$prebid.version$'; diff --git a/package.json b/package.json index f8f363cf28f..79feb816b16 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "3.17.0-pre", + "version": "3.18.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { diff --git a/src/prebid.js b/src/prebid.js index aa2f3bad334..f45a15d5f19 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -521,7 +521,6 @@ $$PREBID_GLOBAL$$.requestBids = hook('async', function ({ bidsBackHandler, timeo adUnitCodes.forEach(code => targeting.setLatestAuctionForAdUnit(code, auction.getAuctionId())); auction.callBids(); - return auction; }); export function executeStorageCallbacks(fn, reqBidsConfigObj) { diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 10f9bc935eb..042f1a4d61d 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -385,6 +385,7 @@ describe('The Criteo bidding adapter', function () { '91': 1 }, }, + apiVersion: 1, }, }; @@ -436,7 +437,7 @@ describe('The Criteo bidding adapter', function () { expect(ortbRequest.slots[0].zoneid).to.equal(123); expect(ortbRequest.gdprConsent.consentData).to.equal('concentDataString'); expect(ortbRequest.gdprConsent.gdprApplies).to.equal(true); - expect(ortbRequest.gdprConsent.consentGiven).to.equal(true); + expect(ortbRequest.gdprConsent.version).to.equal(1); }); it('should properly build a networkId request', function () { @@ -484,7 +485,6 @@ describe('The Criteo bidding adapter', function () { expect(ortbRequest.slots[0].sizes[1]).to.equal('728x90'); expect(ortbRequest.gdprConsent.consentData).to.equal(undefined); expect(ortbRequest.gdprConsent.gdprApplies).to.equal(false); - expect(ortbRequest.gdprConsent.consentGiven).to.equal(undefined); }); it('should properly build a mixed request', function () { @@ -553,7 +553,6 @@ describe('The Criteo bidding adapter', function () { const ortbRequest = spec.buildRequests(bidRequests, bidderRequest).data; expect(ortbRequest.gdprConsent.consentData).to.equal(undefined); expect(ortbRequest.gdprConsent.gdprApplies).to.equal(undefined); - expect(ortbRequest.gdprConsent.consentGiven).to.equal(undefined); }); it('should properly build a request with ccpa consent field', function () { diff --git a/test/spec/modules/lunamediaBidAdapter_spec.js b/test/spec/modules/lunamediaBidAdapter_spec.js new file mode 100755 index 00000000000..fc8648bf8a0 --- /dev/null +++ b/test/spec/modules/lunamediaBidAdapter_spec.js @@ -0,0 +1,137 @@ +import { expect } from 'chai'; +import { spec } from 'modules/lunamediaBidAdapter.js'; +import { BANNER, VIDEO } from 'src/mediaTypes.js'; + +describe('lunamediaBidAdapter', function () { + let bidRequests; + let bidRequestsVid; + + beforeEach(function () { + bidRequests = [{'bidder': 'lunamedia', 'params': {'pubid': '0cf8d6d643e13d86a5b6374148a4afac', 'floor': 0.5, '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': 'lunamedia', 'params': {'pubid': '8537f00948fc37cc03c5f0f88e198a76', 'floor': 1.0, '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 () { + 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 () { + expect(spec.isBidRequestValid()).to.equal(false); + 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(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 lunamediabidreqVid = {'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://api.lunamedia.io/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 }, lunamediabidreqVid); + delete bidResponseVid['vastUrl']; + delete bidResponseVid['ad']; + expect(bidResponseVid).to.deep.equal({ + requestId: bidRequestsVid[0].bidId, + bidderCode: 'lunamedia', + 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 lunamediabidreq = {bids: {}}; + bidRequests.forEach(bid => { + let _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER); + lunamediabidreq.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 }, lunamediabidreq); + expect(bidResponse).to.deep.equal({ + requestId: bidRequests[0].bidId, + ad: serverResponse.seatbid[0].bid[0].adm, + bidderCode: 'lunamedia', + 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 + }); + }); + }); + }); +}); diff --git a/test/spec/modules/marsmediaBidAdapter_spec.js b/test/spec/modules/marsmediaBidAdapter_spec.js index c43c5daaa7c..c0399e5d0a2 100644 --- a/test/spec/modules/marsmediaBidAdapter_spec.js +++ b/test/spec/modules/marsmediaBidAdapter_spec.js @@ -38,7 +38,7 @@ describe('marsmedia adapter tests', function () { var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - expect(bidRequest.url).to.have.string('https://hb.azeriondigital.com/bidder/?bid=3mhdom&zoneId=9999&hbv='); + expect(bidRequest.url).to.have.string('https://hb.go2speed.media/bidder/?bid=3mhdom&zoneId=9999&hbv='); expect(bidRequest.method).to.equal('POST'); const openrtbRequest = JSON.parse(bidRequest.data); expect(openrtbRequest.site).to.not.equal(null); @@ -108,7 +108,7 @@ describe('marsmedia adapter tests', function () { var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - expect(bidRequest.url).to.have.string('https://hb.azeriondigital.com/bidder/?bid=3mhdom&zoneId=9999&hbv='); + expect(bidRequest.url).to.have.string('https://hb.go2speed.media/bidder/?bid=3mhdom&zoneId=9999&hbv='); expect(bidRequest.method).to.equal('POST'); const openrtbRequest = JSON.parse(bidRequest.data); expect(openrtbRequest.site).to.not.equal(null); From d81917aeb0f8d46d45edd85b5d8acdebbe828bd9 Mon Sep 17 00:00:00 2001 From: idettman Date: Tue, 18 Aug 2020 12:37:29 -0700 Subject: [PATCH 12/37] update liveramp userid support --- modules/rubiconBidAdapter.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 47ca694dc43..54bd03789a9 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -243,7 +243,7 @@ export const spec = { } if (bidRequest.userId && typeof bidRequest.userId === 'object' && - (bidRequest.userId.tdid || bidRequest.userId.pubcid || bidRequest.userId.lipb || bidRequest.userId.idl_env)) { + (bidRequest.userId.tdid || bidRequest.userId.pubcid || bidRequest.userId.lipb || bidRequest.userId.idl_env || bidRequest.userId.sharedId)) { utils.deepSetValue(data, 'user.ext.eids', []); if (bidRequest.userId.tdid) { @@ -295,6 +295,20 @@ export const spec = { }] }); } + + // support shared id + if (bidRequest.userId.sharedId) { + data.user.ext.eids.push({ + source: 'sharedID.org', + uids: [{ + id: bidRequest.userId.sharedId, + atype: 3, + ext: { + third: bidRequest.userId.sharedId + } + }] + }); + } } if (config.getConfig('coppa') === true) { @@ -556,6 +570,11 @@ export const spec = { if (bidRequest.userId.idl_env) { data['x_liverampidl'] = bidRequest.userId.idl_env; } + + // support shared id + if (bidRequest.userId.sharedId) { + data['eid_sharedid.org'] = `${bidRequest.userId.sharedId}^3^${bidRequest.userId.sharedId}`; + } } if (bidderRequest.gdprConsent) { From 6cf396f40b55569975bd1e5fcb0c042517a3bc4f Mon Sep 17 00:00:00 2001 From: idettman Date: Tue, 18 Aug 2020 12:39:33 -0700 Subject: [PATCH 13/37] changed source value to all lowercase --- modules/rubiconBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 54bd03789a9..49c9fa4ecc7 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -299,7 +299,7 @@ export const spec = { // support shared id if (bidRequest.userId.sharedId) { data.user.ext.eids.push({ - source: 'sharedID.org', + source: 'sharedid.org', uids: [{ id: bidRequest.userId.sharedId, atype: 3, From 3d996b054aab2187aa1b145cc5f110ff57d80e6b Mon Sep 17 00:00:00 2001 From: idettman Date: Tue, 18 Aug 2020 12:59:54 -0700 Subject: [PATCH 14/37] update share id name --- modules/rubiconBidAdapter.js | 12 ++++++------ test/spec/modules/rubiconBidAdapter_spec.js | 13 +++++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 49c9fa4ecc7..05a76413e09 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -243,7 +243,7 @@ export const spec = { } if (bidRequest.userId && typeof bidRequest.userId === 'object' && - (bidRequest.userId.tdid || bidRequest.userId.pubcid || bidRequest.userId.lipb || bidRequest.userId.idl_env || bidRequest.userId.sharedId)) { + (bidRequest.userId.tdid || bidRequest.userId.pubcid || bidRequest.userId.lipb || bidRequest.userId.idl_env || bidRequest.userId.sharedid)) { utils.deepSetValue(data, 'user.ext.eids', []); if (bidRequest.userId.tdid) { @@ -297,14 +297,14 @@ export const spec = { } // support shared id - if (bidRequest.userId.sharedId) { + if (bidRequest.userId.sharedid) { data.user.ext.eids.push({ source: 'sharedid.org', uids: [{ - id: bidRequest.userId.sharedId, + id: bidRequest.userId.sharedid, atype: 3, ext: { - third: bidRequest.userId.sharedId + third: bidRequest.userId.sharedid } }] }); @@ -572,8 +572,8 @@ export const spec = { } // support shared id - if (bidRequest.userId.sharedId) { - data['eid_sharedid.org'] = `${bidRequest.userId.sharedId}^3^${bidRequest.userId.sharedId}`; + if (bidRequest.userId.sharedid) { + data['eid_sharedid.org'] = `${bidRequest.userId.sharedid}^3^${bidRequest.userId.sharedid}`; } } diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 808d4840418..fef929f9386 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1354,6 +1354,19 @@ describe('the rubicon adapter', function () { expect(data['x_liverampidl']).to.equal('1111-2222-3333-4444'); }); }); + + describe('SharedID support', function () { + it('should send sharedid when userId defines sharedId', function () { + const clonedBid = utils.deepClone(bidderRequest.bids[0]); + clonedBid.userId = { + sharedid: '1111' + }; + let [request] = spec.buildRequests([clonedBid], bidderRequest); + let data = parseQuery(request.data); + + expect(data['eid_sharedid.org']).to.equal('1111^3^1111'); + }); + }); }) describe('Prebid AdSlot', function () { From b64efbb209a586a59a7307653107d6368283bf30 Mon Sep 17 00:00:00 2001 From: idettman Date: Tue, 18 Aug 2020 13:13:04 -0700 Subject: [PATCH 15/37] add unit test for shareid eid --- test/spec/modules/rubiconBidAdapter_spec.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index fef929f9386..b41e6443c8f 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -223,7 +223,8 @@ describe('the rubicon adapter', function () { lipbid: '0000-1111-2222-3333', segments: ['segA', 'segB'] }, - idl_env: '1111-2222-3333-4444' + idl_env: '1111-2222-3333-4444', + sharedid: '1111' }; bid.storedAuctionResponse = 11111; } @@ -1561,6 +1562,12 @@ describe('the rubicon adapter', function () { // LiveRamp should exist expect(post.user.ext.eids[1].source).to.equal('liveramp_idl'); expect(post.user.ext.eids[1].uids[0].id).to.equal('1111-2222-3333-4444'); + // SharedId should exist + expect(post.user.ext.eids[2].source).to.equal('sharedid.org'); + expect(post.user.ext.eids[2].uids[0].id).to.equal('1111'); + expect(post.user.ext.eids[2].uids[0].atype).to.equal(3); + expect(post.user.ext.eids[2].uids[0].ext.third).to.equal('1111'); + expect(post.rp).that.is.an('object'); expect(post.rp.target).that.is.an('object'); expect(post.rp.target.LIseg).that.is.an('array'); From 1ae7999d57381e23ac789df6b5ddf9913a818715 Mon Sep 17 00:00:00 2001 From: idettman Date: Tue, 18 Aug 2020 13:56:01 -0700 Subject: [PATCH 16/37] update shareid obj paths --- modules/rubiconBidAdapter.js | 6 +++--- test/spec/modules/rubiconBidAdapter_spec.js | 14 ++++++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 05a76413e09..01216cc1923 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -301,10 +301,10 @@ export const spec = { data.user.ext.eids.push({ source: 'sharedid.org', uids: [{ - id: bidRequest.userId.sharedid, + id: bidRequest.userId.sharedid.id, atype: 3, ext: { - third: bidRequest.userId.sharedid + third: bidRequest.userId.sharedid.third } }] }); @@ -573,7 +573,7 @@ export const spec = { // support shared id if (bidRequest.userId.sharedid) { - data['eid_sharedid.org'] = `${bidRequest.userId.sharedid}^3^${bidRequest.userId.sharedid}`; + data['eid_sharedid.org'] = `${bidRequest.userId.sharedid.id}^3^${bidRequest.userId.sharedid.third}`; } } diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index b41e6443c8f..e78cbb6be7a 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -224,7 +224,10 @@ describe('the rubicon adapter', function () { segments: ['segA', 'segB'] }, idl_env: '1111-2222-3333-4444', - sharedid: '1111' + sharedid: { + id: '1111', + third: '2222' + } }; bid.storedAuctionResponse = 11111; } @@ -1360,12 +1363,15 @@ describe('the rubicon adapter', function () { it('should send sharedid when userId defines sharedId', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); clonedBid.userId = { - sharedid: '1111' + sharedid: { + id: '1111', + third: '2222' + } }; let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); - expect(data['eid_sharedid.org']).to.equal('1111^3^1111'); + expect(data['eid_sharedid.org']).to.equal('1111^3^2222'); }); }); }) @@ -1566,7 +1572,7 @@ describe('the rubicon adapter', function () { expect(post.user.ext.eids[2].source).to.equal('sharedid.org'); expect(post.user.ext.eids[2].uids[0].id).to.equal('1111'); expect(post.user.ext.eids[2].uids[0].atype).to.equal(3); - expect(post.user.ext.eids[2].uids[0].ext.third).to.equal('1111'); + expect(post.user.ext.eids[2].uids[0].ext.third).to.equal('2222'); expect(post.rp).that.is.an('object'); expect(post.rp.target).that.is.an('object'); From 3b0490037625056da9f8e176a94ca2ea0447431a Mon Sep 17 00:00:00 2001 From: idettman Date: Mon, 24 Aug 2020 12:18:27 -0700 Subject: [PATCH 17/37] fixed atype value --- 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 01216cc1923..db7e6c3e84d 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -302,7 +302,7 @@ export const spec = { source: 'sharedid.org', uids: [{ id: bidRequest.userId.sharedid.id, - atype: 3, + atype: 1, ext: { third: bidRequest.userId.sharedid.third } diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index e78cbb6be7a..6989083f2ed 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1571,7 +1571,7 @@ describe('the rubicon adapter', function () { // SharedId should exist expect(post.user.ext.eids[2].source).to.equal('sharedid.org'); expect(post.user.ext.eids[2].uids[0].id).to.equal('1111'); - expect(post.user.ext.eids[2].uids[0].atype).to.equal(3); + expect(post.user.ext.eids[2].uids[0].atype).to.equal(1); expect(post.user.ext.eids[2].uids[0].ext.third).to.equal('2222'); expect(post.rp).that.is.an('object'); From 3cf599dc91e156e39861c8640737e95e7f5158a9 Mon Sep 17 00:00:00 2001 From: idettman Date: Tue, 25 Aug 2020 23:29:49 -0700 Subject: [PATCH 18/37] update to use eids set by userId/eids.js --- modules/rubiconBidAdapter.js | 99 ++++++++------------- test/spec/modules/rubiconBidAdapter_spec.js | 38 +++++++- 2 files changed, 74 insertions(+), 63 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index f0e34ed34a2..94cfa9dc407 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -1,3 +1,4 @@ +import find from 'core-js-pure/features/array/find.js'; import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; @@ -247,73 +248,49 @@ export const spec = { utils.deepSetValue(data, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - if (bidRequest.userId && typeof bidRequest.userId === 'object' && - (bidRequest.userId.tdid || bidRequest.userId.pubcid || bidRequest.userId.lipb || bidRequest.userId.idl_env || bidRequest.userId.sharedid)) { - utils.deepSetValue(data, 'user.ext.eids', []); - - if (bidRequest.userId.tdid) { - data.user.ext.eids.push({ - source: 'adserver.org', - uids: [{ - id: bidRequest.userId.tdid, - ext: { - rtiPartner: 'TDID' - } - }] - }); + const bidUserIdAsEids = utils.deepAccess(bidderRequest, 'bids.0.userIdAsEids'); + // if property exists, it is a array with at least one element (set by a function from userId/eids.js called from userId/index.js) + if (bidUserIdAsEids) { + /** + * @param {string} source + * @param {function} update + * @return {{source: string, update: (function|undefined)}} + */ + const getEid = function (source, update) { + const eid = { source }; + if (typeof update === 'function') { + eid.update = update; + } + return eid; } - if (bidRequest.userId.pubcid) { - data.user.ext.eids.push({ - source: 'pubcommon', - uids: [{ - id: bidRequest.userId.pubcid, - }] - }); - } + utils.deepSetValue(data, 'user.ext.eids', []); - // support liveintent ID - if (bidRequest.userId.lipb && bidRequest.userId.lipb.lipbid) { - data.user.ext.eids.push({ - source: 'liveintent.com', - uids: [{ - id: bidRequest.userId.lipb.lipbid - }] + // UserID EID support for adserver, pubcommon, liveintent, liveramp, sharedid + [ getEid('adserver.org'), + getEid('pubcommon'), + getEid('liveintent.com', function (data, eid) { + data.user.ext.tpid = { source: eid.source, uid: eid.uids[0].id }; + if (eid.ext && eid.ext.segments) { + utils.deepSetValue(data, 'rp.target.LIseg', eid.ext.segments); + } + }), + getEid('liveramp.com'), + getEid('sharedid.org') + ].forEach(function (eidSource) { + const eid = find(bidUserIdAsEids, function (i) { + return i.source === eidSource.source; }); - data.user.ext.tpid = { - source: 'liveintent.com', - uid: bidRequest.userId.lipb.lipbid - }; - - if (Array.isArray(bidRequest.userId.lipb.segments) && bidRequest.userId.lipb.segments.length) { - utils.deepSetValue(data, 'rp.target.LIseg', bidRequest.userId.lipb.segments); + if (eid) { + if (typeof eidSource.update === 'function') { + // console.log('HERE: if (typeof eidSource.update === \'function\') {', eid); + eidSource.update(data, eid); + } + data.user.ext.eids.push(eid); + // console.log('data.user.ext.eids', data.user.ext.eids); } - } - - // support identityLink (aka LiveRamp) - if (bidRequest.userId.idl_env) { - data.user.ext.eids.push({ - source: 'liveramp_idl', - uids: [{ - id: bidRequest.userId.idl_env - }] - }); - } - - // support shared id - if (bidRequest.userId.sharedid) { - data.user.ext.eids.push({ - source: 'sharedid.org', - uids: [{ - id: bidRequest.userId.sharedid.id, - atype: 1, - ext: { - third: bidRequest.userId.sharedid.third - } - }] - }); - } + }); } if (config.getConfig('coppa') === true) { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 8f6d0bc4ab9..e5d8a92e960 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -5,6 +5,30 @@ import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; import find from 'core-js-pure/features/array/find.js'; +/** + * Helper to create eids objs for userid tests + * @param {string} source + * @param {Object} props + * @param {Object} ext + * @return {{uids: [{atype: number, id: string, ext: (Object|undefined)}], source: string, ext: (Object|undefined)}} + */ +function createEid(source, props, ext) { + const eid = { + source, + uids: [ + Object.keys(props).reduce(function (aggregate, key) { + aggregate[key] = props[key]; + return aggregate; + }, {}) + ] + }; + + if (ext) { + eid.ext = ext; + } + return eid; +} + const INTEGRATION = `pbjs_lite_v$prebid.version$`; // $prebid.version$ will be substituted in by gulp in built prebid const PBS_INTEGRATION = 'pbjs'; @@ -218,6 +242,11 @@ describe('the rubicon adapter', function () { 'playerWidth': 640, 'size_id': 201, }; + bid.userIdAsEids = [ + createEid('liveintent.com', { id: '0000-1111-2222-3333', atype: 1 }, { segments: ['segA', 'segB'] }), + createEid('liveramp.com', { id: '1111-2222-3333-4444', atype: 1 }), + createEid('sharedid.org', { id: '1111', atype: 1, ext: { third: '2222' } }) + ]; bid.userId = { lipb: { lipbid: '0000-1111-2222-3333', @@ -922,7 +951,7 @@ describe('the rubicon adapter', function () { expect(data['gdpr_consent']).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); }); - it('should send only "gdpr_consent", when gdprConsent defines only consentString', function () { + it('should send only "gdpr_consent", when gdprConsent defines only consentString', function () { createGdprBidderRequest(); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let data = parseQuery(request.data); @@ -1320,6 +1349,7 @@ describe('the rubicon adapter', function () { describe('user id config', function() { it('should send tpid_tdid when userId defines tdid', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); + clonedBid.userIdAsEids = [createEid('adserver.org', { id: 'abcd-efgh-ijkl-mnop-1234', atype: 1 })]; clonedBid.userId = { tdid: 'abcd-efgh-ijkl-mnop-1234' }; @@ -1332,6 +1362,7 @@ describe('the rubicon adapter', function () { describe('LiveIntent support', function () { it('should send tpid_liveintent.com when userId defines lipd', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); + clonedBid.userIdAsEids = [createEid('liveintent.com', { id: '0000-1111-2222-3333', atype: 1 })]; clonedBid.userId = { lipb: { lipbid: '0000-1111-2222-3333' @@ -1345,6 +1376,7 @@ describe('the rubicon adapter', function () { it('should send tg_v.LIseg when userId defines lipd.segments', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); + clonedBid.userIdAsEids = [createEid('liveintent.com', { id: '1111-2222-3333-4444', atype: 1 }, { segments: ['segD', 'segE'] })]; clonedBid.userId = { lipb: { lipbid: '1111-2222-3333-4444', @@ -1362,6 +1394,7 @@ describe('the rubicon adapter', function () { describe('LiveRamp support', function () { it('should send x_liverampidl when userId defines idl_env', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); + clonedBid.userIdAsEids = [createEid('liveramp.com', { id: '1111-2222-3333-4444', atype: 1 })]; clonedBid.userId = { idl_env: '1111-2222-3333-4444' }; @@ -1375,6 +1408,7 @@ describe('the rubicon adapter', function () { describe('SharedID support', function () { it('should send sharedid when userId defines sharedId', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); + clonedBid.userIdAsEids = [createEid('sharedid.org', { id: '1111', atype: 1, ext: { third: '2222' } })]; clonedBid.userId = { sharedid: { id: '1111', @@ -1579,7 +1613,7 @@ describe('the rubicon adapter', function () { expect(post.user.ext.tpid.source).to.equal('liveintent.com'); expect(post.user.ext.tpid.uid).to.equal('0000-1111-2222-3333'); // LiveRamp should exist - expect(post.user.ext.eids[1].source).to.equal('liveramp_idl'); + expect(post.user.ext.eids[1].source).to.equal('liveramp.com'); expect(post.user.ext.eids[1].uids[0].id).to.equal('1111-2222-3333-4444'); // SharedId should exist expect(post.user.ext.eids[2].source).to.equal('sharedid.org'); From 9b5d0f7ce1dfaed29f93748a42085a17dc0a93a4 Mon Sep 17 00:00:00 2001 From: idettman Date: Tue, 25 Aug 2020 23:55:09 -0700 Subject: [PATCH 19/37] update eid to include ext object for adserver.org --- test/spec/modules/rubiconBidAdapter_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index e5d8a92e960..8583c354dee 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1349,7 +1349,7 @@ describe('the rubicon adapter', function () { describe('user id config', function() { it('should send tpid_tdid when userId defines tdid', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); - clonedBid.userIdAsEids = [createEid('adserver.org', { id: 'abcd-efgh-ijkl-mnop-1234', atype: 1 })]; + clonedBid.userIdAsEids = [createEid('adserver.org', { id: 'abcd-efgh-ijkl-mnop-1234', atype: 1, ext: { rtiPartner: 'TDID' } })]; clonedBid.userId = { tdid: 'abcd-efgh-ijkl-mnop-1234' }; From 1c349653b2d8ec0700a7adab248e9cd71ab76941 Mon Sep 17 00:00:00 2001 From: idettman Date: Wed, 26 Aug 2020 00:17:39 -0700 Subject: [PATCH 20/37] optimize remove lines --- modules/rubiconBidAdapter.js | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 94cfa9dc407..f38a6c0dfe2 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -251,23 +251,15 @@ export const spec = { const bidUserIdAsEids = utils.deepAccess(bidderRequest, 'bids.0.userIdAsEids'); // if property exists, it is a array with at least one element (set by a function from userId/eids.js called from userId/index.js) if (bidUserIdAsEids) { - /** - * @param {string} source - * @param {function} update - * @return {{source: string, update: (function|undefined)}} - */ - const getEid = function (source, update) { - const eid = { source }; - if (typeof update === 'function') { - eid.update = update; - } - return eid; - } + const getEid = (source, callback) => { + return { source, callback }; + }; utils.deepSetValue(data, 'user.ext.eids', []); // UserID EID support for adserver, pubcommon, liveintent, liveramp, sharedid - [ getEid('adserver.org'), + [ + getEid('adserver.org'), getEid('pubcommon'), getEid('liveintent.com', function (data, eid) { data.user.ext.tpid = { source: eid.source, uid: eid.uids[0].id }; @@ -277,18 +269,13 @@ export const spec = { }), getEid('liveramp.com'), getEid('sharedid.org') - ].forEach(function (eidSource) { - const eid = find(bidUserIdAsEids, function (i) { - return i.source === eidSource.source; - }); - + ].forEach(function (item) { + const eid = find(bidUserIdAsEids, (i) => i.source === item.source); if (eid) { - if (typeof eidSource.update === 'function') { - // console.log('HERE: if (typeof eidSource.update === \'function\') {', eid); - eidSource.update(data, eid); - } data.user.ext.eids.push(eid); - // console.log('data.user.ext.eids', data.user.ext.eids); + if (typeof item.callback === 'function') { + item.callback(data, eid); + } } }); } From 15cb95504fc2875e9811aa52727e321d1b774578 Mon Sep 17 00:00:00 2001 From: idettman Date: Wed, 26 Aug 2020 01:25:39 -0700 Subject: [PATCH 21/37] optimize for line reduction --- modules/rubiconBidAdapter.js | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index f38a6c0dfe2..02fb65cb438 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -249,28 +249,23 @@ export const spec = { } const bidUserIdAsEids = utils.deepAccess(bidderRequest, 'bids.0.userIdAsEids'); - // if property exists, it is a array with at least one element (set by a function from userId/eids.js called from userId/index.js) if (bidUserIdAsEids) { - const getEid = (source, callback) => { - return { source, callback }; - }; - utils.deepSetValue(data, 'user.ext.eids', []); - // UserID EID support for adserver, pubcommon, liveintent, liveramp, sharedid [ - getEid('adserver.org'), - getEid('pubcommon'), - getEid('liveintent.com', function (data, eid) { - data.user.ext.tpid = { source: eid.source, uid: eid.uids[0].id }; - if (eid.ext && eid.ext.segments) { - utils.deepSetValue(data, 'rp.target.LIseg', eid.ext.segments); - } - }), - getEid('liveramp.com'), - getEid('sharedid.org') - ].forEach(function (item) { - const eid = find(bidUserIdAsEids, (i) => i.source === item.source); + { source: 'adserver.org' }, + { source: 'pubcommon' }, + { source: 'liveintent.com', + callback: (data, eid) => { + data.user.ext.tpid = { source: eid.source, uid: eid.uids[0].id }; + if (eid.ext && eid.ext.segments) { + utils.deepSetValue(data, 'rp.target.LIseg', eid.ext.segments); + } + }}, + { source: 'liveramp.com' }, + { source: 'sharedid.org' } + ].forEach(item => { + const eid = find(bidUserIdAsEids, i => i.source === item.source); if (eid) { data.user.ext.eids.push(eid); if (typeof item.callback === 'function') { From d8ede4833d60c750fa7ddb839b45efb1e4bfc678 Mon Sep 17 00:00:00 2001 From: idettman Date: Wed, 26 Aug 2020 01:41:06 -0700 Subject: [PATCH 22/37] optimization --- modules/rubiconBidAdapter.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 02fb65cb438..dafe9cd52d3 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -253,8 +253,8 @@ export const spec = { utils.deepSetValue(data, 'user.ext.eids', []); // UserID EID support for adserver, pubcommon, liveintent, liveramp, sharedid [ - { source: 'adserver.org' }, - { source: 'pubcommon' }, + 'adserver.org', + 'pubcommon', { source: 'liveintent.com', callback: (data, eid) => { data.user.ext.tpid = { source: eid.source, uid: eid.uids[0].id }; @@ -262,10 +262,11 @@ export const spec = { utils.deepSetValue(data, 'rp.target.LIseg', eid.ext.segments); } }}, - { source: 'liveramp.com' }, - { source: 'sharedid.org' } + 'liveramp.com', + 'sharedid.org' ].forEach(item => { - const eid = find(bidUserIdAsEids, i => i.source === item.source); + const source = (typeof item === 'string') ? item : item.source; + const eid = find(bidUserIdAsEids, i => i.source === source); if (eid) { data.user.ext.eids.push(eid); if (typeof item.callback === 'function') { From 3ab09600032b7c63a2e23b3ab8fc2db0bd08203b Mon Sep 17 00:00:00 2001 From: idettman Date: Wed, 26 Aug 2020 01:43:20 -0700 Subject: [PATCH 23/37] optimization --- modules/rubiconBidAdapter.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index dafe9cd52d3..2eab32a86d8 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -265,8 +265,7 @@ export const spec = { 'liveramp.com', 'sharedid.org' ].forEach(item => { - const source = (typeof item === 'string') ? item : item.source; - const eid = find(bidUserIdAsEids, i => i.source === source); + const eid = find(bidUserIdAsEids, i => i.source === (typeof item === 'string' ? item : item.source)); if (eid) { data.user.ext.eids.push(eid); if (typeof item.callback === 'function') { From 4e37d0227cb7ce7d55b385c0766e30bfd8eb3ecf Mon Sep 17 00:00:00 2001 From: idettman Date: Wed, 26 Aug 2020 02:22:20 -0700 Subject: [PATCH 24/37] fixed liveramp missing property --- modules/rubiconBidAdapter.js | 13 +++++++++---- test/spec/modules/rubiconBidAdapter_spec.js | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 2eab32a86d8..f203136c9e4 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -254,15 +254,20 @@ export const spec = { // UserID EID support for adserver, pubcommon, liveintent, liveramp, sharedid [ 'adserver.org', - 'pubcommon', - { source: 'liveintent.com', + 'pubcommon', { + source: 'liveintent.com', callback: (data, eid) => { data.user.ext.tpid = { source: eid.source, uid: eid.uids[0].id }; if (eid.ext && eid.ext.segments) { utils.deepSetValue(data, 'rp.target.LIseg', eid.ext.segments); } - }}, - 'liveramp.com', + } + }, { + source: 'liveramp.com', + callback: (data, eid) => { + utils.deepSetValue(data, 'user.ext.liveramp_idl', eid.uids[0].id) + } + }, 'sharedid.org' ].forEach(item => { const eid = find(bidUserIdAsEids, i => i.source === (typeof item === 'string' ? item : item.source)); diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 8583c354dee..32a20403d8d 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1615,6 +1615,7 @@ describe('the rubicon adapter', function () { // LiveRamp should exist expect(post.user.ext.eids[1].source).to.equal('liveramp.com'); expect(post.user.ext.eids[1].uids[0].id).to.equal('1111-2222-3333-4444'); + expect(post.user.ext.liveramp_idl).to.equal('1111-2222-3333-4444'); // SharedId should exist expect(post.user.ext.eids[2].source).to.equal('sharedid.org'); expect(post.user.ext.eids[2].uids[0].id).to.equal('1111'); From fff8ee1032431f93d323d57fd4564bd6dbfed3ed Mon Sep 17 00:00:00 2001 From: idettman Date: Wed, 26 Aug 2020 02:29:57 -0700 Subject: [PATCH 25/37] fixed import ordering --- modules/rubiconBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index f203136c9e4..f0c3562dff1 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -1,8 +1,8 @@ -import find from 'core-js-pure/features/array/find.js'; import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import find from 'core-js-pure/features/array/find.js'; const DEFAULT_INTEGRATION = 'pbjs_lite'; const DEFAULT_PBS_INTEGRATION = 'pbjs'; From 935fa3283fea0e9efd5824e6f600430b60c9de73 Mon Sep 17 00:00:00 2001 From: idettman Date: Wed, 26 Aug 2020 02:34:39 -0700 Subject: [PATCH 26/37] reverted change --- modules/rubiconBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index f0c3562dff1..b5f2c44e4d0 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -1159,7 +1159,7 @@ export function hasValidSupplyChainParams(schain) { if (!schain.nodes) return isValid; isValid = schain.nodes.reduce((status, node) => { if (!status) return status; - return requiredFields.every(field => node[field]); + return requiredFields.every(field => node.hasOwnProperty(field)); }, true); if (!isValid) utils.logError('Rubicon: required schain params missing'); return isValid; From 7a81bd334be831b70c7c636b0ad73e3d40ecb51d Mon Sep 17 00:00:00 2001 From: idettman Date: Wed, 26 Aug 2020 14:59:32 -0700 Subject: [PATCH 27/37] undo revert --- modules/rubiconBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index b5f2c44e4d0..f0c3562dff1 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -1159,7 +1159,7 @@ export function hasValidSupplyChainParams(schain) { if (!schain.nodes) return isValid; isValid = schain.nodes.reduce((status, node) => { if (!status) return status; - return requiredFields.every(field => node.hasOwnProperty(field)); + return requiredFields.every(field => node[field]); }, true); if (!isValid) utils.logError('Rubicon: required schain params missing'); return isValid; From 146aeb8ee60eb9abfe824f5b2f209668e8ca924c Mon Sep 17 00:00:00 2001 From: idettman Date: Wed, 26 Aug 2020 16:20:32 -0700 Subject: [PATCH 28/37] fix tests expected values --- modules/rubiconBidAdapter.js | 5 +-- test/spec/modules/rubiconBidAdapter_spec.js | 37 ++++++++------------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 884ecb25a1f..bb7410ad888 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -251,7 +251,8 @@ export const spec = { const bidUserIdAsEids = utils.deepAccess(bidderRequest, 'bids.0.userIdAsEids'); if (bidUserIdAsEids) { utils.deepSetValue(data, 'user.ext.eids', []); - // UserID EID support for adserver, pubcommon, liveintent, liveramp, sharedid + // Array of supported id systems + // Note: liveintent and liveramp have a callback defined to set additional props using their EID values [ 'adserver.org', 'pubcommon', { @@ -1171,7 +1172,7 @@ export function hasValidSupplyChainParams(schain) { if (!schain.nodes) return isValid; isValid = schain.nodes.reduce((status, node) => { if (!status) return status; - return requiredFields.every(field => node[field]); + return requiredFields.every(field => node.hasOwnProperty(field)); }, true); if (!isValid) utils.logError('Rubicon: required schain params missing'); return isValid; diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index b1de86963bb..2f0984e538d 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1624,14 +1624,27 @@ describe('the rubicon adapter', function () { expect(imp.ext.rubicon.video.skipafter).to.equal(15); expect(imp.ext.prebid.auctiontimestamp).to.equal(1472239426000); expect(post.user.ext.consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); + // LiveIntent should exist expect(post.user.ext.eids[0].source).to.equal('liveintent.com'); expect(post.user.ext.eids[0].uids[0].id).to.equal('0000-1111-2222-3333'); + expect(post.user.ext.eids[0].uids[0].atype).to.equal(1); + expect(post.user.ext.eids[0].ext).that.is.an('object'); + expect(post.user.ext.eids[0].ext.segments).that.is.an('array'); + expect(post.user.ext.eids[0].ext.segments[0]).to.equal('segA'); + expect(post.user.ext.eids[0].ext.segments[1]).to.equal('segB'); + // Non-EID properties set using liveintent EID values expect(post.user.ext.tpid).that.is.an('object'); expect(post.user.ext.tpid.source).to.equal('liveintent.com'); expect(post.user.ext.tpid.uid).to.equal('0000-1111-2222-3333'); + expect(post.rp).that.is.an('object'); + expect(post.rp.target).that.is.an('object'); + expect(post.rp.target.LIseg).that.is.an('array'); + expect(post.rp.target.LIseg[0]).to.equal('segA'); + expect(post.rp.target.LIseg[1]).to.equal('segB'); // LiveRamp should exist expect(post.user.ext.eids[1].source).to.equal('liveramp.com'); expect(post.user.ext.eids[1].uids[0].id).to.equal('1111-2222-3333-4444'); + expect(post.user.ext.eids[1].uids[0].atype).to.equal(1); expect(post.user.ext.liveramp_idl).to.equal('1111-2222-3333-4444'); // SharedId should exist expect(post.user.ext.eids[2].source).to.equal('sharedid.org'); @@ -1639,11 +1652,6 @@ describe('the rubicon adapter', function () { expect(post.user.ext.eids[2].uids[0].atype).to.equal(1); expect(post.user.ext.eids[2].uids[0].ext.third).to.equal('2222'); - expect(post.rp).that.is.an('object'); - expect(post.rp.target).that.is.an('object'); - expect(post.rp.target.LIseg).that.is.an('array'); - expect(post.rp.target.LIseg[0]).to.equal('segA'); - expect(post.rp.target.LIseg[1]).to.equal('segB'); expect(post.regs.ext.gdpr).to.equal(1); expect(post.regs.ext.us_privacy).to.equal('1NYN'); expect(post).to.have.property('ext').that.is.an('object'); @@ -2111,25 +2119,6 @@ describe('the rubicon adapter', function () { expect(imp.ext.rubicon.video.skipafter).to.equal(15); expect(imp.ext.prebid.auctiontimestamp).to.equal(1472239426000); expect(post.user.ext.consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); - expect(post.user.ext.eids[0].source).to.equal('liveintent.com'); - expect(post.user.ext.eids[0].uids[0].id).to.equal('0000-1111-2222-3333'); - expect(post.user.ext.tpid).that.is.an('object'); - expect(post.user.ext.tpid.source).to.equal('liveintent.com'); - expect(post.user.ext.tpid.uid).to.equal('0000-1111-2222-3333'); - // LiveRamp should exist - expect(post.user.ext.eids[1].source).to.equal('liveramp_idl'); - expect(post.user.ext.eids[1].uids[0].id).to.equal('1111-2222-3333-4444'); - // SharedId should exist - expect(post.user.ext.eids[2].source).to.equal('sharedid.org'); - expect(post.user.ext.eids[2].uids[0].id).to.equal('1111'); - expect(post.user.ext.eids[2].uids[0].atype).to.equal(3); - expect(post.user.ext.eids[2].uids[0].ext.third).to.equal('2222'); - - expect(post.rp).that.is.an('object'); - expect(post.rp.target).that.is.an('object'); - expect(post.rp.target.LIseg).that.is.an('array'); - expect(post.rp.target.LIseg[0]).to.equal('segA'); - expect(post.rp.target.LIseg[1]).to.equal('segB'); // Config user.id expect(post.user.id).to.equal('123'); From 75f282f7d7e6adc1ebcab95cb2c34360ca79dc2e Mon Sep 17 00:00:00 2001 From: idettman Date: Wed, 26 Aug 2020 16:32:02 -0700 Subject: [PATCH 29/37] added fields to expected test results --- test/spec/modules/rubiconBidAdapter_spec.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 2f0984e538d..4e28d75681a 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -951,7 +951,7 @@ describe('the rubicon adapter', function () { expect(data['gdpr_consent']).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); }); - it('should send only "gdpr_consent", when gdprConsent defines only consentString', function () { + it('should send only "gdpr_consent", when gdprConsent defines only consentString', function () { createGdprBidderRequest(); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let data = parseQuery(request.data); @@ -1624,27 +1624,30 @@ describe('the rubicon adapter', function () { expect(imp.ext.rubicon.video.skipafter).to.equal(15); expect(imp.ext.prebid.auctiontimestamp).to.equal(1472239426000); expect(post.user.ext.consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); + // EIDs should exist + expect(post.user.ext).to.have.property('eids').that.is.an('array'); // LiveIntent should exist expect(post.user.ext.eids[0].source).to.equal('liveintent.com'); expect(post.user.ext.eids[0].uids[0].id).to.equal('0000-1111-2222-3333'); expect(post.user.ext.eids[0].uids[0].atype).to.equal(1); - expect(post.user.ext.eids[0].ext).that.is.an('object'); - expect(post.user.ext.eids[0].ext.segments).that.is.an('array'); + expect(post.user.ext.eids[0]).to.have.property('ext').that.is.an('object'); + expect(post.user.ext.eids[0].ext).to.have.property('segments').that.is.an('array'); expect(post.user.ext.eids[0].ext.segments[0]).to.equal('segA'); expect(post.user.ext.eids[0].ext.segments[1]).to.equal('segB'); // Non-EID properties set using liveintent EID values - expect(post.user.ext.tpid).that.is.an('object'); + expect(post.user.ext).to.have.property('tpid').that.is.an('object'); expect(post.user.ext.tpid.source).to.equal('liveintent.com'); expect(post.user.ext.tpid.uid).to.equal('0000-1111-2222-3333'); - expect(post.rp).that.is.an('object'); - expect(post.rp.target).that.is.an('object'); - expect(post.rp.target.LIseg).that.is.an('array'); + expect(post).to.have.property('rp').that.is.an('object'); + expect(post.rp).to.have.property('target').that.is.an('object'); + expect(post.rp.target).to.have.property('LIseg').that.is.an('array'); expect(post.rp.target.LIseg[0]).to.equal('segA'); expect(post.rp.target.LIseg[1]).to.equal('segB'); // LiveRamp should exist expect(post.user.ext.eids[1].source).to.equal('liveramp.com'); expect(post.user.ext.eids[1].uids[0].id).to.equal('1111-2222-3333-4444'); expect(post.user.ext.eids[1].uids[0].atype).to.equal(1); + expect(post.user.ext).to.have.property('liveramp_idl').that.is.an('string'); expect(post.user.ext.liveramp_idl).to.equal('1111-2222-3333-4444'); // SharedId should exist expect(post.user.ext.eids[2].source).to.equal('sharedid.org'); From 7ac875173cd5b2d14a2411c431d85f4ef86cb78c Mon Sep 17 00:00:00 2001 From: idettman Date: Wed, 26 Aug 2020 18:56:13 -0700 Subject: [PATCH 30/37] refactor to user eids.js to mock test data --- test/spec/modules/rubiconBidAdapter_spec.js | 41 ++++----------------- 1 file changed, 7 insertions(+), 34 deletions(-) diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 4e28d75681a..378acf8b31d 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -4,30 +4,7 @@ import {parse as parseQuery} from 'querystring'; import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; import find from 'core-js-pure/features/array/find.js'; - -/** - * Helper to create eids objs for userid tests - * @param {string} source - * @param {Object} props - * @param {Object} ext - * @return {{uids: [{atype: number, id: string, ext: (Object|undefined)}], source: string, ext: (Object|undefined)}} - */ -function createEid(source, props, ext) { - const eid = { - source, - uids: [ - Object.keys(props).reduce(function (aggregate, key) { - aggregate[key] = props[key]; - return aggregate; - }, {}) - ] - }; - - if (ext) { - eid.ext = ext; - } - return eid; -} +import { createEidsArray } from 'modules/userId/eids.js'; const INTEGRATION = `pbjs_lite_v$prebid.version$`; // $prebid.version$ will be substituted in by gulp in built prebid const PBS_INTEGRATION = 'pbjs'; @@ -242,11 +219,6 @@ describe('the rubicon adapter', function () { 'playerWidth': 640, 'size_id': 201, }; - bid.userIdAsEids = [ - createEid('liveintent.com', { id: '0000-1111-2222-3333', atype: 1 }, { segments: ['segA', 'segB'] }), - createEid('liveramp.com', { id: '1111-2222-3333-4444', atype: 1 }), - createEid('sharedid.org', { id: '1111', atype: 1, ext: { third: '2222' } }) - ]; bid.userId = { lipb: { lipbid: '0000-1111-2222-3333', @@ -258,6 +230,7 @@ describe('the rubicon adapter', function () { third: '2222' } }; + bid.userIdAsEids = createEidsArray(bid.userId); bid.storedAuctionResponse = 11111; } @@ -1349,10 +1322,10 @@ describe('the rubicon adapter', function () { describe('user id config', function() { it('should send tpid_tdid when userId defines tdid', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); - clonedBid.userIdAsEids = [createEid('adserver.org', { id: 'abcd-efgh-ijkl-mnop-1234', atype: 1, ext: { rtiPartner: 'TDID' } })]; clonedBid.userId = { tdid: 'abcd-efgh-ijkl-mnop-1234' }; + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1362,12 +1335,12 @@ describe('the rubicon adapter', function () { describe('LiveIntent support', function () { it('should send tpid_liveintent.com when userId defines lipd', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); - clonedBid.userIdAsEids = [createEid('liveintent.com', { id: '0000-1111-2222-3333', atype: 1 })]; clonedBid.userId = { lipb: { lipbid: '0000-1111-2222-3333' } }; + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1376,13 +1349,13 @@ describe('the rubicon adapter', function () { it('should send tg_v.LIseg when userId defines lipd.segments', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); - clonedBid.userIdAsEids = [createEid('liveintent.com', { id: '1111-2222-3333-4444', atype: 1 }, { segments: ['segD', 'segE'] })]; clonedBid.userId = { lipb: { lipbid: '1111-2222-3333-4444', segments: ['segD', 'segE'] } }; + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); let [request] = spec.buildRequests([clonedBid], bidderRequest); const unescapedData = unescape(request.data); @@ -1394,10 +1367,10 @@ describe('the rubicon adapter', function () { describe('LiveRamp support', function () { it('should send x_liverampidl when userId defines idl_env', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); - clonedBid.userIdAsEids = [createEid('liveramp.com', { id: '1111-2222-3333-4444', atype: 1 })]; clonedBid.userId = { idl_env: '1111-2222-3333-4444' }; + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1408,13 +1381,13 @@ describe('the rubicon adapter', function () { describe('SharedID support', function () { it('should send sharedid when userId defines sharedId', function () { const clonedBid = utils.deepClone(bidderRequest.bids[0]); - clonedBid.userIdAsEids = [createEid('sharedid.org', { id: '1111', atype: 1, ext: { third: '2222' } })]; clonedBid.userId = { sharedid: { id: '1111', third: '2222' } }; + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); From 361e4ab304b0739555fab0fbee166c873b4d9970 Mon Sep 17 00:00:00 2001 From: idettman Date: Thu, 27 Aug 2020 05:09:25 -0700 Subject: [PATCH 31/37] optimize code to reduce lines --- modules/rubiconBidAdapter.js | 43 ++++++++++++++---------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index bb7410ad888..b75a6846262 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -248,35 +248,26 @@ export const spec = { utils.deepSetValue(data, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - const bidUserIdAsEids = utils.deepAccess(bidderRequest, 'bids.0.userIdAsEids'); - if (bidUserIdAsEids) { - utils.deepSetValue(data, 'user.ext.eids', []); - // Array of supported id systems - // Note: liveintent and liveramp have a callback defined to set additional props using their EID values - [ - 'adserver.org', - 'pubcommon', { - source: 'liveintent.com', - callback: (data, eid) => { - data.user.ext.tpid = { source: eid.source, uid: eid.uids[0].id }; - if (eid.ext && eid.ext.segments) { - utils.deepSetValue(data, 'rp.target.LIseg', eid.ext.segments); - } - } - }, { - source: 'liveramp.com', - callback: (data, eid) => { - utils.deepSetValue(data, 'user.ext.liveramp_idl', eid.uids[0].id) + const userIdAsEids = utils.deepAccess(bidderRequest, 'bids.0.userIdAsEids'); + if (userIdAsEids) { + // filter out unsupported id systems + utils.deepSetValue(data, 'user.ext.eids', userIdAsEids.filter(i => ['adserver.org', 'pubcommon', 'liveintent.com', 'liveramp.com', 'sharedid.org'].indexOf(i.source) !== -1)); + // liveintent and liveramp require additional props besides eids to be set + const eidMap = { + 'liveintent.com': (data, eid) => { + utils.deepSetValue(data, 'user.ext.tpid', { source: eid.source, uid: eid.uids[0].id }); + if (eid.ext && eid.ext.segments) { + utils.deepSetValue(data, 'rp.target.LIseg', eid.ext.segments); } }, - 'sharedid.org' - ].forEach(item => { - const eid = find(bidUserIdAsEids, i => i.source === (typeof item === 'string' ? item : item.source)); + 'liveramp.com': (data, eid) => { + utils.deepSetValue(data, 'user.ext.liveramp_idl', eid.uids[0].id); + } + }; + Object.keys(eidMap).forEach(key => { + const eid = find(data.user.ext.eids, i => i.source === key); if (eid) { - data.user.ext.eids.push(eid); - if (typeof item.callback === 'function') { - item.callback(data, eid); - } + eidMap[key](data, eid); } }); } From 5d9c3e4139ede052a4a3c38633eb720333df50b9 Mon Sep 17 00:00:00 2001 From: idettman Date: Thu, 27 Aug 2020 05:15:57 -0700 Subject: [PATCH 32/37] removed extra braces --- modules/rubiconBidAdapter.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index b75a6846262..6c7ca715226 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -260,9 +260,7 @@ export const spec = { utils.deepSetValue(data, 'rp.target.LIseg', eid.ext.segments); } }, - 'liveramp.com': (data, eid) => { - utils.deepSetValue(data, 'user.ext.liveramp_idl', eid.uids[0].id); - } + 'liveramp.com': (data, eid) => utils.deepSetValue(data, 'user.ext.liveramp_idl', eid.uids[0].id) }; Object.keys(eidMap).forEach(key => { const eid = find(data.user.ext.eids, i => i.source === key); From 75273cbe2090bce81be915fa003522b15020372a Mon Sep 17 00:00:00 2001 From: idettman Date: Thu, 27 Aug 2020 05:47:29 -0700 Subject: [PATCH 33/37] removed extra lines and renamed userIdAsEids to eids --- modules/rubiconBidAdapter.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 6c7ca715226..238907fa2b9 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -248,11 +248,11 @@ export const spec = { utils.deepSetValue(data, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - const userIdAsEids = utils.deepAccess(bidderRequest, 'bids.0.userIdAsEids'); - if (userIdAsEids) { + const eids = utils.deepAccess(bidderRequest, 'bids.0.userIdAsEids'); + if (eids) { // filter out unsupported id systems - utils.deepSetValue(data, 'user.ext.eids', userIdAsEids.filter(i => ['adserver.org', 'pubcommon', 'liveintent.com', 'liveramp.com', 'sharedid.org'].indexOf(i.source) !== -1)); - // liveintent and liveramp require additional props besides eids to be set + utils.deepSetValue(data, 'user.ext.eids', eids.filter(eid => ['adserver.org', 'pubcommon', 'liveintent.com', 'liveramp.com', 'sharedid.org'].indexOf(eid.source) !== -1)); + // liveintent and liveramp require additional props to be set const eidMap = { 'liveintent.com': (data, eid) => { utils.deepSetValue(data, 'user.ext.tpid', { source: eid.source, uid: eid.uids[0].id }); @@ -262,12 +262,7 @@ export const spec = { }, 'liveramp.com': (data, eid) => utils.deepSetValue(data, 'user.ext.liveramp_idl', eid.uids[0].id) }; - Object.keys(eidMap).forEach(key => { - const eid = find(data.user.ext.eids, i => i.source === key); - if (eid) { - eidMap[key](data, eid); - } - }); + Object.keys(eidMap).map(source => find(data.user.ext.eids, eid => eid.source === source)).filter(eid => !!eid).forEach(eid => eidMap[eid.source](data, eid)); } // set user.id value from config value From ab0779a410f8d74259e4006cf6ee2963091e2f81 Mon Sep 17 00:00:00 2001 From: idettman Date: Thu, 27 Aug 2020 06:36:44 -0700 Subject: [PATCH 34/37] add tests for all supported sources --- modules/rubiconBidAdapter.js | 2 +- test/spec/modules/rubiconBidAdapter_spec.js | 23 +++++++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 238907fa2b9..9c0ef5c9b9b 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -251,7 +251,7 @@ export const spec = { const eids = utils.deepAccess(bidderRequest, 'bids.0.userIdAsEids'); if (eids) { // filter out unsupported id systems - utils.deepSetValue(data, 'user.ext.eids', eids.filter(eid => ['adserver.org', 'pubcommon', 'liveintent.com', 'liveramp.com', 'sharedid.org'].indexOf(eid.source) !== -1)); + utils.deepSetValue(data, 'user.ext.eids', eids.filter(eid => ['adserver.org', 'pubcid.org', 'liveintent.com', 'liveramp.com', 'sharedid.org'].indexOf(eid.source) !== -1)); // liveintent and liveramp require additional props to be set const eidMap = { 'liveintent.com': (data, eid) => { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 378acf8b31d..2c8ce320aab 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -219,16 +219,13 @@ describe('the rubicon adapter', function () { 'playerWidth': 640, 'size_id': 201, }; + bid.userId = { - lipb: { - lipbid: '0000-1111-2222-3333', - segments: ['segA', 'segB'] - }, - idl_env: '1111-2222-3333-4444', - sharedid: { - id: '1111', - third: '2222' - } + lipb: { lipbid: '0000-1111-2222-3333', segments: ['segA', 'segB'] }, // 'liveintent.com' liveIntentId + idl_env: '1111-2222-3333-4444', // 'liveramp.com' identityLink + sharedid: { id: '1111', third: '2222' }, // 'sharedid.org' sharedId + tdid: '3000', // 'adserver.org' unifiedId + pubcid: '4000' // 'pubcid.org' pubCommonId }; bid.userIdAsEids = createEidsArray(bid.userId); bid.storedAuctionResponse = 11111; @@ -1627,6 +1624,14 @@ describe('the rubicon adapter', function () { expect(post.user.ext.eids[2].uids[0].id).to.equal('1111'); expect(post.user.ext.eids[2].uids[0].atype).to.equal(1); expect(post.user.ext.eids[2].uids[0].ext.third).to.equal('2222'); + // UnifiedId should exist + expect(post.user.ext.eids[3].source).to.equal('adserver.org'); + expect(post.user.ext.eids[3].uids[0].atype).to.equal(1); + expect(post.user.ext.eids[3].uids[0].id).to.equal('3000'); + // PubCommonId should exist + expect(post.user.ext.eids[4].source).to.equal('pubcid.org'); + expect(post.user.ext.eids[4].uids[0].atype).to.equal(1); + expect(post.user.ext.eids[4].uids[0].id).to.equal('4000'); expect(post.regs.ext.gdpr).to.equal(1); expect(post.regs.ext.us_privacy).to.equal('1NYN'); From 3ed9347ee0c10c2754a071e2fb7222d60858e6c0 Mon Sep 17 00:00:00 2001 From: idettman Date: Thu, 27 Aug 2020 06:43:22 -0700 Subject: [PATCH 35/37] removed unnecessary comments --- test/spec/modules/rubiconBidAdapter_spec.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 2c8ce320aab..3a6453bb63b 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -219,13 +219,12 @@ describe('the rubicon adapter', function () { 'playerWidth': 640, 'size_id': 201, }; - bid.userId = { - lipb: { lipbid: '0000-1111-2222-3333', segments: ['segA', 'segB'] }, // 'liveintent.com' liveIntentId - idl_env: '1111-2222-3333-4444', // 'liveramp.com' identityLink - sharedid: { id: '1111', third: '2222' }, // 'sharedid.org' sharedId - tdid: '3000', // 'adserver.org' unifiedId - pubcid: '4000' // 'pubcid.org' pubCommonId + lipb: { lipbid: '0000-1111-2222-3333', segments: ['segA', 'segB'] }, + idl_env: '1111-2222-3333-4444', + sharedid: { id: '1111', third: '2222' }, + tdid: '3000', + pubcid: '4000' }; bid.userIdAsEids = createEidsArray(bid.userId); bid.storedAuctionResponse = 11111; From 81cd98ad468c155a939dfd04e0492ac671ec9a41 Mon Sep 17 00:00:00 2001 From: idettman Date: Thu, 27 Aug 2020 06:55:41 -0700 Subject: [PATCH 36/37] udate condition to check for eids length --- modules/rubiconBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 9c0ef5c9b9b..9c5e0bd22f0 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -249,7 +249,7 @@ export const spec = { } const eids = utils.deepAccess(bidderRequest, 'bids.0.userIdAsEids'); - if (eids) { + if (eids && eids.length) { // filter out unsupported id systems utils.deepSetValue(data, 'user.ext.eids', eids.filter(eid => ['adserver.org', 'pubcid.org', 'liveintent.com', 'liveramp.com', 'sharedid.org'].indexOf(eid.source) !== -1)); // liveintent and liveramp require additional props to be set From ef0cbbbb68bf2b2dcf347636c100fa5429344035 Mon Sep 17 00:00:00 2001 From: idettman Date: Thu, 27 Aug 2020 07:59:19 -0700 Subject: [PATCH 37/37] removed liveramp.com from eidMap, and refactored to use single variable --- modules/rubiconBidAdapter.js | 20 +++++++++----------- test/spec/modules/rubiconBidAdapter_spec.js | 3 +-- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 9c5e0bd22f0..4a0dd10281b 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -252,17 +252,15 @@ export const spec = { if (eids && eids.length) { // filter out unsupported id systems utils.deepSetValue(data, 'user.ext.eids', eids.filter(eid => ['adserver.org', 'pubcid.org', 'liveintent.com', 'liveramp.com', 'sharedid.org'].indexOf(eid.source) !== -1)); - // liveintent and liveramp require additional props to be set - const eidMap = { - 'liveintent.com': (data, eid) => { - utils.deepSetValue(data, 'user.ext.tpid', { source: eid.source, uid: eid.uids[0].id }); - if (eid.ext && eid.ext.segments) { - utils.deepSetValue(data, 'rp.target.LIseg', eid.ext.segments); - } - }, - 'liveramp.com': (data, eid) => utils.deepSetValue(data, 'user.ext.liveramp_idl', eid.uids[0].id) - }; - Object.keys(eidMap).map(source => find(data.user.ext.eids, eid => eid.source === source)).filter(eid => !!eid).forEach(eid => eidMap[eid.source](data, eid)); + + // liveintent requires additional props to be set + const liveIntentEid = find(data.user.ext.eids, eid => eid.source === 'liveintent.com'); + if (liveIntentEid) { + utils.deepSetValue(data, 'user.ext.tpid', { source: liveIntentEid.source, uid: liveIntentEid.uids[0].id }); + if (liveIntentEid.ext && liveIntentEid.ext.segments) { + utils.deepSetValue(data, 'rp.target.LIseg', liveIntentEid.ext.segments); + } + } } // set user.id value from config value diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 3a6453bb63b..c28af4a3f9b 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1616,8 +1616,7 @@ describe('the rubicon adapter', function () { expect(post.user.ext.eids[1].source).to.equal('liveramp.com'); expect(post.user.ext.eids[1].uids[0].id).to.equal('1111-2222-3333-4444'); expect(post.user.ext.eids[1].uids[0].atype).to.equal(1); - expect(post.user.ext).to.have.property('liveramp_idl').that.is.an('string'); - expect(post.user.ext.liveramp_idl).to.equal('1111-2222-3333-4444'); + // SharedId should exist expect(post.user.ext.eids[2].source).to.equal('sharedid.org'); expect(post.user.ext.eids[2].uids[0].id).to.equal('1111');