From fbcecc9ee7d1f86411c90109365ee6938df50bad Mon Sep 17 00:00:00 2001 From: Regulyarniy Nikolay Date: Wed, 27 Jul 2022 23:58:29 +1000 Subject: [PATCH 001/246] Videonow Bid Adapter: Initial Bid Adapter Release (#8669) * Videonow bid adapter: add videonow bid adapter * Update videonowBidAdapter.md Co-authored-by: Patrick McCann --- modules/videonowBidAdapter.js | 122 +++++++++++++++++++ modules/videonowBidAdapter.md | 37 ++++++ test/spec/modules/videonowBidAdapter_spec.js | 80 ++++++++++++ 3 files changed, 239 insertions(+) create mode 100644 modules/videonowBidAdapter.js create mode 100644 modules/videonowBidAdapter.md create mode 100644 test/spec/modules/videonowBidAdapter_spec.js diff --git a/modules/videonowBidAdapter.js b/modules/videonowBidAdapter.js new file mode 100644 index 00000000000..bfbc07fdff1 --- /dev/null +++ b/modules/videonowBidAdapter.js @@ -0,0 +1,122 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER} from '../src/mediaTypes.js'; +import {_each, getBidIdParameter, getValue, logError, logInfo} from '../src/utils.js'; + +const BIDDER_CODE = 'videonow'; +const RTB_URL = 'https://adx.videonow.ru/yhb' +const DEFAULT_CURRENCY = 'RUB' +const DEFAULT_CODE_TYPE = 'combo' +const TTL_SECONDS = 60 * 5 + +export const spec = { + + code: BIDDER_CODE, + url: RTB_URL, + supportedMediaTypes: [ BANNER ], + + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bidRequest The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function (bidRequest) { + if (!bidRequest.params) { + return false; + } + + if (!bidRequest.params.pId) { + logError('failed validation: pId not declared'); + return false + } + + return true + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} validBidRequests - an array of bids + * @param bidderRequest + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests, bidderRequest) { + logInfo('validBidRequests', validBidRequests); + + const bidRequests = []; + + _each(validBidRequests, (bid) => { + const bidId = getBidIdParameter('bidId', bid) + const placementId = getValue(bid.params, 'pId') + const currency = getValue(bid.params, 'currency') || DEFAULT_CURRENCY + const url = getValue(bid.params, 'url') || RTB_URL + const codeType = getValue(bid.params, 'codeType') || DEFAULT_CODE_TYPE + const sizes = getValue(bid, 'sizes') + + bidRequests.push({ + method: 'POST', + url, + data: { + places: [ + { + id: bidId, + placementId, + codeType, + sizes + } + ], + settings: { + currency, + } + }, + }) + }) + + return bidRequests; + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @param {BidRequest} bidRequest The bid params + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse, bidRequest) { + logInfo('serverResponse', serverResponse.body); + + const responsesBody = serverResponse ? serverResponse.body : {}; + const bidResponses = []; + try { + if (!responsesBody?.bids?.length) { + return []; + } + + _each(responsesBody.bids, (bid) => { + if (bid?.displayCode) { + const bidResponse = { + requestId: bid.id, + cpm: bid.cpm, + currency: bid.currency, + width: bid.size.width, + height: bid.size.height, + ad: bid.displayCode, + ttl: TTL_SECONDS, + creativeId: bid.id, + netRevenue: true, + meta: { + advertiserDomains: bid.adDomain ? [bid.adDomain] : [] + } + }; + bidResponses.push(bidResponse) + } + }) + } catch (error) { + logError(error); + } + + return bidResponses + }, +} + +registerBidder(spec); diff --git a/modules/videonowBidAdapter.md b/modules/videonowBidAdapter.md new file mode 100644 index 00000000000..0762880f666 --- /dev/null +++ b/modules/videonowBidAdapter.md @@ -0,0 +1,37 @@ +# Overview + +**Module Name**: Videonow Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: nregularniy@videonow.ru + +# Description + +Videonow Bidder Adapter for Prebid.js. About: https://videonow.ru/ + + +Use `videonow` as bidder: + +# Params +- `pId` required, profile ID +- `currency` optional, currency, default is 'RUB' +- `url` optional, for debug, bidder url +- `codeType` optional, for debug, yhb codeType + +## AdUnits configuration example +``` + var adUnits = [{ + code: 'your-slot', //use exactly the same code as your slot div id. + mediaTypes: { + banner: { + sizes: [[640, 480]] + } + }, + bids: [{ + bidder: 'videonow', + params: { + pId: '1234', + currency: 'RUB', + } + }] + }]; +``` diff --git a/test/spec/modules/videonowBidAdapter_spec.js b/test/spec/modules/videonowBidAdapter_spec.js new file mode 100644 index 00000000000..c9eb5ba0bbf --- /dev/null +++ b/test/spec/modules/videonowBidAdapter_spec.js @@ -0,0 +1,80 @@ +import {expect} from 'chai'; +import {spec} from 'modules/videonowBidAdapter'; + +describe('videonowBidAdapter', function () { + it('minimal params', function () { + expect(spec.isBidRequestValid({ + bidder: 'videonow', + params: { + pId: 'advDesktopBillboard' + }})).to.equal(true) + }) + + it('minimal params no placementId', function () { + expect(spec.isBidRequestValid({ + bidder: 'videonow', + params: { + currency: `GBP` + }})).to.equal(false) + }) + + it('generated_params common case', function () { + const bidRequestData = [{ + bidId: 'bid1234', + bidder: 'videonow', + params: { + pId: 'advDesktopBillboard', + currency: `GBP` + }, + sizes: [[240, 400]] + }]; + + const request = spec.buildRequests(bidRequestData); + const req_data = request[0].data; + + expect(req_data.places[0].id).to.equal(`bid1234`) + expect(req_data.places[0].placementId).to.equal(`advDesktopBillboard`) + expect(req_data.settings.currency).to.equal(`GBP`) + expect(req_data.places[0].sizes[0][0]).to.equal(240); + expect(req_data.places[0].sizes[0][1]).to.equal(400); + }); + + it('response_params common case', function () { + const bidRequestData = { + data: { + bidId: 'bid1234' + } + }; + + const serverResponse = { + body: { + bids: [ + { + 'displayCode': 'test html', + 'id': '123456', + 'cpm': 375, + 'currency': 'RUB', + 'placementId': 'profileName', + 'codeType': 'js', + 'size': { + 'width': 640, + 'height': 480 + } + } + ] + } + }; + + const bids = spec.interpretResponse(serverResponse, bidRequestData); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.requestId).to.equal('123456') + expect(bid.cpm).to.equal(375); + expect(bid.currency).to.equal('RUB'); + expect(bid.width).to.equal(640); + expect(bid.height).to.equal(480); + expect(bid.ad).to.equal('test html'); + expect(bid.creativeId).to.equal(`123456`) + expect(bid.netRevenue).to.equal(true); + }); +}) From 15b688c9a4f15a7669fd1fa579f9c76e1a917949 Mon Sep 17 00:00:00 2001 From: rcheptanariu <35690143+rcheptanariu@users.noreply.github.com> Date: Wed, 27 Jul 2022 18:54:16 +0300 Subject: [PATCH 002/246] InvibesBidAdapter - local storage bypass fixed (#8720) * InvibesBidAdapter - local storage bypass fixed * InvibesBidAdapter - fixed error when param not defined Co-authored-by: Patrick McCann --- modules/invibesBidAdapter.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/invibesBidAdapter.js b/modules/invibesBidAdapter.js index 99434e18adb..2f80b89fb9c 100644 --- a/modules/invibesBidAdapter.js +++ b/modules/invibesBidAdapter.js @@ -381,7 +381,10 @@ function getUserIds(bidUserId) { function parseQueryStringParams() { let params = {}; try { - params = JSON.parse(readFromLocalStorage('ivbs')); + let storedParam = storage.getDataFromLocalStorage('ivbs'); + if (storedParam != null) { + params = JSON.parse(storedParam); + } } catch (e) { } let re = /[\\?&]([^=]+)=([^\\?&#]+)/g; From 7c267f73935eed73940c3b83a3b390ac42f04aca Mon Sep 17 00:00:00 2001 From: jsfledd Date: Wed, 27 Jul 2022 11:08:24 -0700 Subject: [PATCH 003/246] Nativo Bid Adapter: add Price Floors Module support (#8666) * Initial nativoBidAdapter document creation (js, md and spec) * Fulling working prebid using nativoBidAdapter. Support for GDPR and CCPA in user syncs. * Added defult size settings based on the largest ad unit. Added response body validation. Added consent to request url qs params. * Changed bidder endpoint url * Changed double quotes to single quotes. * Reverted package-json.lock to remove modifications from PR * Added optional bidder param 'url' so the ad server can force- match an existing placement * Lint fix. Added space after if. * Added new QS param to send various adUnit data to adapter endpopint * Updated unit test for new QS param * Added qs param to keep track of ad unit refreshes * Updated bidMap key default value * Updated refresh increment logic * Refactored spread operator for IE11 support * Updated isBidRequestValid check * Refactored Object.enties to use Object.keys to fix CircleCI testing errors * Updated bid mapping key creation to prioritize ad unit code over placementId * Added filtering by ad, advertiser and campaign. * Merged master * Added more robust bidDataMap with multiple key access * Deduped filer values * Rolled back package.json * Duped upstream/master's package.lock file ... not sure how it got changed in the first place * Small refactor of filterData length check. Removed comparison with 0 since a length value of 0 is already falsy. * Added bid sizes to request * Fixed function name in spec. Added unit tests. * Added priceFloor module support * Added protection agains empty url parameter --- modules/nativoBidAdapter.js | 198 ++++++++++++++++++--- test/spec/modules/nativoBidAdapter_spec.js | 149 +++++++++++++++- 2 files changed, 319 insertions(+), 28 deletions(-) diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 271ecac7aa2..54c99b17834 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -11,6 +11,8 @@ const GVLID = 263 const TIME_TO_LIVE = 360 const SUPPORTED_AD_TYPES = [BANNER] +const FLOOR_PRICE_CURRENCY = 'USD' +const PRICE_FLOOR_WILDCARD = '*' /** * Keep track of bid data by keys @@ -131,84 +133,106 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { + // Parse values from bid requests const placementIds = new Set() const bidDataMap = BidDataMap() const placementSizes = { length: 0 } + const floorPriceData = {} let placementId, pageUrl - validBidRequests.forEach((request) => { + validBidRequests.forEach((bidRequest) => { pageUrl = deepAccess( - request, + bidRequest, 'params.url', - bidderRequest.refererInfo.page ) - placementId = deepAccess(request, 'params.placementId') + if (pageUrl == undefined || pageUrl === '') { + pageUrl = bidderRequest.refererInfo.page + } + + placementId = deepAccess(bidRequest, 'params.placementId') - const bidDataKeys = [request.adUnitCode] + const bidDataKeys = [bidRequest.adUnitCode] if (placementId && !placementIds.has(placementId)) { placementIds.add(placementId) bidDataKeys.push(placementId) - placementSizes[placementId] = request.sizes + placementSizes[placementId] = bidRequest.sizes placementSizes.length++ } const bidData = { - bidId: request.bidId, - size: getLargestSize(request.sizes), + bidId: bidRequest.bidId, + size: getLargestSize(bidRequest.sizes), } bidDataMap.addBidData(bidData, bidDataKeys) + + const bidRequestFloorPriceData = parseFloorPriceData(bidRequest) + if (bidRequestFloorPriceData) { + floorPriceData[bidRequest.adUnitCode] = bidRequestFloorPriceData + } }) bidRequestMap[bidderRequest.bidderRequestId] = bidDataMap // Build adUnit data - const adUnitData = { - adUnits: validBidRequests.map((adUnit) => { - // Track if we've already requested for this ad unit code - adUnitsRequested[adUnit.adUnitCode] = - adUnitsRequested[adUnit.adUnitCode] !== undefined - ? adUnitsRequested[adUnit.adUnitCode] + 1 - : 0 - return { - adUnitCode: adUnit.adUnitCode, - mediaTypes: adUnit.mediaTypes, - } - }), - } + const adUnitData = buildAdUnitData(validBidRequests) - // Build QS Params + // Build basic required QS Params let params = [ + // Prebid request id { key: 'ntv_pb_rid', value: bidderRequest.bidderRequestId }, + // Ad unit data { key: 'ntv_ppc', value: btoa(JSON.stringify(adUnitData)), // Convert to Base 64 }, + // Number count of requests per ad unit { key: 'ntv_dbr', - value: btoa(JSON.stringify(adUnitsRequested)), + value: btoa(JSON.stringify(adUnitsRequested)), // Convert to Base 64 }, + // Page url { key: 'ntv_url', value: encodeURIComponent(pageUrl), }, ] + // Floor pricing + if (Object.keys(floorPriceData).length) { + params.unshift({ + key: 'ntv_ppf', + value: btoa(JSON.stringify(floorPriceData)), + }) + } + // Add filtering if (adsToFilter.size > 0) { - params.unshift({ key: 'ntv_atf', value: Array.from(adsToFilter).join(',') }) + params.unshift({ + key: 'ntv_atf', + value: Array.from(adsToFilter).join(','), + }) } if (advertisersToFilter.size > 0) { - params.unshift({ key: 'ntv_avtf', value: Array.from(advertisersToFilter).join(',') }) + params.unshift({ + key: 'ntv_avtf', + value: Array.from(advertisersToFilter).join(','), + }) } if (campaignsToFilter.size > 0) { - params.unshift({ key: 'ntv_ctf', value: Array.from(campaignsToFilter).join(',') }) + params.unshift({ + key: 'ntv_ctf', + value: Array.from(campaignsToFilter).join(','), + }) } // Placement Sizes if (placementSizes.length) { - params.unshift({ key: 'ntv_pas', value: btoa(JSON.stringify(placementSizes)) }) + params.unshift({ + key: 'ntv_pas', + value: btoa(JSON.stringify(placementSizes)), + }) } // Add placement IDs @@ -429,6 +453,126 @@ export const spec = { registerBidder(spec) // Utils +export function parseFloorPriceData(bidRequest) { + if (typeof bidRequest.getFloor !== 'function') return + + // Setup price floor data per bid request + let bidRequestFloorPriceData = {} + let bidMediaTypes = bidRequest.mediaTypes + let sizeOptions = new Set() + // Step through meach media type so we can get floor data for each media type per bid request + Object.keys(bidMediaTypes).forEach((mediaType) => { + // Setup price floor data per media type + let mediaTypeData = bidMediaTypes[mediaType] + let mediaTypeFloorPriceData = {} + // Step through each size of the media type so we can get floor data for each size per media type + mediaTypeData.sizes.forEach((size) => { + // Get floor price data per the getFloor method and respective media type / size combination + const priceFloorData = bidRequest.getFloor({ + currency: FLOOR_PRICE_CURRENCY, + mediaType, + size, + }) + // Save the data and track the sizes + mediaTypeFloorPriceData[sizeToString(size)] = priceFloorData.floor + sizeOptions.add(size) + }) + bidRequestFloorPriceData[mediaType] = mediaTypeFloorPriceData + + // Get floor price of current media type with a wildcard size + const sizeWildcardFloor = getSizeWildcardPrice(bidRequest, mediaType) + // Save the wildcard floor price if it was retrieved successfully + if (sizeWildcardFloor.floor > 0) { + mediaTypeFloorPriceData['*'] = sizeWildcardFloor.floor + } + }) + + // Get floor price for wildcard media type using all of the sizes present in the previous media types + const mediaWildCardPrices = getMediaWildcardPrices(bidRequest, [ + PRICE_FLOOR_WILDCARD, + ...Array.from(sizeOptions), + ]) + bidRequestFloorPriceData['*'] = mediaWildCardPrices + + return bidRequestFloorPriceData +} + +/** + * Get price floor data by always setting the size value to the wildcard for a specific size + * @param {Object} bidRequest - The bid request + * @param {String} mediaType - The media type + * @returns {Object} - Bid floor data + */ +export function getSizeWildcardPrice(bidRequest, mediaType) { + return bidRequest.getFloor({ + currency: FLOOR_PRICE_CURRENCY, + mediaType, + size: PRICE_FLOOR_WILDCARD, + }) +} + +/** + * Get price data for a range of sizes and always setting the media type to the wildcard value + * @param {*} bidRequest - The bid request + * @param {*} sizes - The sizes to get the floor price data for + * @returns {Object} - Bid floor data + */ +export function getMediaWildcardPrices( + bidRequest, + sizes = [PRICE_FLOOR_WILDCARD] +) { + const sizePrices = {} + sizes.forEach((size) => { + // MODIFY the bid request's mediaTypes property (so we can get the wildcard media type value) + const temp = bidRequest.mediaTypes + bidRequest.mediaTypes = { PRICE_FLOOR_WILDCARD: temp.sizes } + // Get price floor data + const priceFloorData = bidRequest.getFloor({ + currency: FLOOR_PRICE_CURRENCY, + mediaType: PRICE_FLOOR_WILDCARD, + size, + }) + // RESTORE initial property value + bidRequest.mediaTypes = temp + + // Only save valid floor price data + const key = + size !== PRICE_FLOOR_WILDCARD ? sizeToString(size) : PRICE_FLOOR_WILDCARD + sizePrices[key] = priceFloorData.floor + }) + return sizePrices +} + +/** + * Format size array to a string + * @param {Array} size - Size data [width, height] + * @returns {String} - Formated size string + */ +export function sizeToString(size) { + if (!Array.isArray(size) || size.length < 2) return '' + return `${size[0]}x${size[1]}` +} + +/** + * Build the ad unit data to send back to the request endpoint + * @param {Array} requests - Bid requests + * @returns {Array} - Array of ad unit data + */ +function buildAdUnitData(requests) { + return requests.map((request) => { + // Track if we've already requested for this ad unit code + adUnitsRequested[request.adUnitCode] = + adUnitsRequested[request.adUnitCode] !== undefined + ? adUnitsRequested[request.adUnitCode] + 1 + : 0 + // Return a new object with only the data we need + return { + adUnitCode: request.adUnitCode, + mediaTypes: request.mediaTypes, + } + }) +} + /** * Append QS param to existing string * @param {String} str - String to append to diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index 0690c7f90e1..be6b07f9acc 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -1,5 +1,11 @@ import { expect } from 'chai' import { spec, BidDataMap } from 'modules/nativoBidAdapter.js' +import { + getSizeWildcardPrice, + getMediaWildcardPrices, + sizeToString, + parseFloorPriceData, +} from '../../../modules/nativoBidAdapter' describe('bidDataMap', function () { it('Should fail gracefully if no key value pairs have been added and no key is sent', function () { @@ -94,7 +100,7 @@ describe('nativoBidAdapterTests', function () { const bidRequestString = JSON.stringify(bidRequest) let bidRequests - beforeEach(function() { + beforeEach(function () { // Clone bidRequest each time bidRequests = [JSON.parse(bidRequestString)] }) @@ -119,6 +125,20 @@ describe('nativoBidAdapterTests', function () { expect(request.url).to.include('ntv_pas') }) + it('ntv_url parameter should NOT be empty even if the utl parameter was set as an empty value', function () { + bidRequests[0].params.url = '' + const request = spec.buildRequests(bidRequests, { + bidderRequestId: 123456, + refererInfo: { + referer: 'https://www.test.com', + }, + }) + + expect(request.url).to.exist + expect(request.url).to.be.a('string') + expect(request.url).to.not.be.empty + }) + it('url should NOT contain placement specific query string parameters if placementId option is not provided', function () { bidRequests[0].params = {} const request = spec.buildRequests(bidRequests, { @@ -489,3 +509,130 @@ describe('Response to Request Filter Flow', () => { expect(request.url).to.include('ntv_ctf=234') }) }) + +describe('sizeToString', () => { + it('Formats size array correctly', () => { + const sizeString = sizeToString([300, 250]) + expect(sizeString).to.be.equal('300x250') + }) + + it('Returns an empty array for invalid data', () => { + // Not an array + let sizeString = sizeToString(300, 350) + expect(sizeString).to.be.equal('') + // Single entry + sizeString = sizeToString([300]) + expect(sizeString).to.be.equal('') + // Undefined + sizeString = sizeToString(undefined) + expect(sizeString).to.be.equal('') + }) +}) + +describe('getSizeWildcardPrice', () => { + it('Generates the correct floor price data', () => { + let floorPrice = { + currency: 'USD', + floor: 1.0, + } + let getFloorMock = () => { + return floorPrice + } + let floorMockSpy = sinon.spy(getFloorMock) + let bidRequest = { + getFloor: floorMockSpy, + mediaTypes: { + banner: { + sizes: [300, 250], + }, + }, + } + + let result = getSizeWildcardPrice(bidRequest, 'banner') + expect( + floorMockSpy.calledWith({ + currency: 'USD', + mediaType: 'banner', + size: '*', + }) + ).to.be.true + expect(result).to.equal(floorPrice) + }) +}) + +describe('getMediaWildcardPrices', () => { + it('Generates the correct floor price data', () => { + let defaultFloorPrice = { + currency: 'USD', + floor: 1.1, + } + let sizefloorPrice = { + currency: 'USD', + floor: 2.2, + } + let getFloorMock = ({ currency, mediaType, size }) => { + if (Array.isArray(size)) return sizefloorPrice + + return defaultFloorPrice + } + let floorMockSpy = sinon.spy(getFloorMock) + let bidRequest = { + getFloor: floorMockSpy, + mediaTypes: { + banner: { + sizes: [300, 250], + }, + }, + } + + let result = getMediaWildcardPrices(bidRequest, ['*', [300, 250]]) + expect( + floorMockSpy.calledWith({ + currency: 'USD', + mediaType: '*', + size: '*', + }) + ).to.be.true + expect( + floorMockSpy.calledWith({ + currency: 'USD', + mediaType: '*', + size: [300, 250], + }) + ).to.be.true + expect(result).to.deep.equal({ '*': 1.1, '300x250': 2.2 }) + }) +}) + +describe('parseFloorPriceData', () => { + it('Generates the correct floor price data', () => { + let defaultFloorPrice = { + currency: 'USD', + floor: 1.1, + } + let sizefloorPrice = { + currency: 'USD', + floor: 2.2, + } + let getFloorMock = ({ currency, mediaType, size }) => { + if (Array.isArray(size)) return sizefloorPrice + + return defaultFloorPrice + } + let floorMockSpy = sinon.spy(getFloorMock) + let bidRequest = { + getFloor: floorMockSpy, + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + } + + let result = parseFloorPriceData(bidRequest) + expect(result).to.deep.equal({ + '*': { '*': 1.1, '300x250': 2.2 }, + banner: { '*': 1.1, '300x250': 2.2 }, + }) + }) +}) From 7e12865005b876fd46c93ce399584d7255be0b23 Mon Sep 17 00:00:00 2001 From: ahmadlob <109217988+ahmadlob@users.noreply.github.com> Date: Thu, 28 Jul 2022 05:33:48 +0300 Subject: [PATCH 004/246] Taboola Bid Adapter: dynamic ttl (#8747) * update-ttl-passing * Update taboolaBidAdapter_spec.js * add fallback default value in case of null * add semicolons * . * . * . * .. * ... --- modules/taboolaBidAdapter.js | 5 ++-- test/spec/modules/taboolaBidAdapter_spec.js | 26 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/modules/taboolaBidAdapter.js b/modules/taboolaBidAdapter.js index 1a35f9e548e..e94e7aba7ca 100644 --- a/modules/taboolaBidAdapter.js +++ b/modules/taboolaBidAdapter.js @@ -222,9 +222,8 @@ function getBid(requestId, currency, bidResponse) { if (!bidResponse) { return; } - const { - price: cpm, crid: creativeId, adm: ad, w: width, h: height, adomain: advertiserDomains, meta = {} + price: cpm, crid: creativeId, adm: ad, w: width, h: height, exp: ttl, adomain: advertiserDomains, meta = {} } = bidResponse; if (advertiserDomains && advertiserDomains.length > 0) { @@ -233,7 +232,7 @@ function getBid(requestId, currency, bidResponse) { return { requestId, - ttl: 60, + ttl, mediaType: BANNER, cpm, creativeId, diff --git a/test/spec/modules/taboolaBidAdapter_spec.js b/test/spec/modules/taboolaBidAdapter_spec.js index 4749a6a5919..739335e8f1f 100644 --- a/test/spec/modules/taboolaBidAdapter_spec.js +++ b/test/spec/modules/taboolaBidAdapter_spec.js @@ -311,6 +311,7 @@ describe('Taboola Adapter', function () { 'crid': '278195503434041083381', 'w': 300, 'h': 250, + 'exp': 60, 'lurl': 'http://us-trc.taboola.com/sample' } ], @@ -386,6 +387,31 @@ describe('Taboola Adapter', function () { const res = spec.interpretResponse(serverResponse, request) expect(res).to.deep.equal(expectedRes) }); + + it('should set the correct ttl form the response', function () { + // set exp-ttl to be 125 + const [bid] = serverResponse.body.seatbid[0].bid; + serverResponse.body.seatbid[0].bid[0].exp = 125; + const expectedRes = [ + { + requestId: request.bids[0].bidId, + cpm: bid.price, + creativeId: bid.crid, + ttl: 125, + netRevenue: false, + currency: serverResponse.body.cur, + mediaType: 'banner', + ad: bid.adm, + width: bid.w, + height: bid.h, + meta: { + 'advertiserDomains': bid.adomain + }, + } + ]; + const res = spec.interpretResponse(serverResponse, request); + expect(res).to.deep.equal(expectedRes); + }); }) describe('userData', function () { From 963c1fbe9c87419c2e3e2757c4405878dd51e823 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Jul 2022 08:24:50 -0400 Subject: [PATCH 005/246] Bump parse-url from 6.0.0 to 6.0.5 (#8756) Bumps [parse-url](https://github.com/IonicaBizau/parse-url) from 6.0.0 to 6.0.5. - [Release notes](https://github.com/IonicaBizau/parse-url/releases) - [Commits](https://github.com/IonicaBizau/parse-url/compare/6.0.0...6.0.5) --- updated-dependencies: - dependency-name: parse-url dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6c4269824d7..ccf4d92a711 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18110,9 +18110,9 @@ } }, "node_modules/parse-url": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.0.tgz", - "integrity": "sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.5.tgz", + "integrity": "sha512-e35AeLTSIlkw/5GFq70IN7po8fmDUjpDPY1rIK+VubRfsUvBonjQ+PBZG+vWMACnQSmNlvl524IucoDmcioMxA==", "dev": true, "dependencies": { "is-ssh": "^1.3.0", @@ -37633,9 +37633,9 @@ } }, "parse-url": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.0.tgz", - "integrity": "sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.5.tgz", + "integrity": "sha512-e35AeLTSIlkw/5GFq70IN7po8fmDUjpDPY1rIK+VubRfsUvBonjQ+PBZG+vWMACnQSmNlvl524IucoDmcioMxA==", "dev": true, "requires": { "is-ssh": "^1.3.0", From c94c8de82d2aea4076e1067e4ab582fa3ebc22f6 Mon Sep 17 00:00:00 2001 From: Carlos Felix Date: Thu, 28 Jul 2022 07:46:03 -0500 Subject: [PATCH 006/246] 33Across Bid Adapter: Obtain display-related attributes (#8730) * capture display-related client side attributes * rename the client display attributes * Obtain the UA entropy values * apply CR feedback, reuse win constant * pass gpid into build * feedback changes * fix missing native property in some browsers * rename entropy fields * only store entropy data when it's present * remove client hints Co-authored-by: Anthony Lin Co-authored-by: anthonyjl92 --- modules/33acrossBidAdapter.js | 74 +++++- test/spec/modules/33acrossBidAdapter_spec.js | 231 ++++++++++++++++++- 2 files changed, 301 insertions(+), 4 deletions(-) diff --git a/modules/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js index 700b1409da2..ad2b092baa8 100644 --- a/modules/33acrossBidAdapter.js +++ b/modules/33acrossBidAdapter.js @@ -254,6 +254,7 @@ function _createServerRequest({ bidRequests, gdprConsent = {}, uspConsent, pageU }); ttxRequest.site = { id: siteId }; + ttxRequest.device = _buildDeviceORTB(); if (pageUrl) { ttxRequest.site.page = pageUrl; @@ -333,12 +334,15 @@ function setExtensions(obj = {}, extFields) { // BUILD REQUESTS: IMP function _buildImpORTB(bidRequest) { + const gpid = deepAccess(bidRequest, 'ortb2Imp.ext.gpid'); + const imp = { id: bidRequest.bidId, ext: { ttx: { prod: deepAccess(bidRequest, 'params.productId') - } + }, + ...(gpid ? { gpid } : {}) } }; @@ -734,6 +738,74 @@ function _createSync({ siteId = 'zzz000000000003zzz', gdprConsent = {}, uspConse return sync; } +// BUILD REQUESTS: DEVICE +function _buildDeviceORTB() { + const win = getWindowSelf(); + + return { + ext: { + ttx: { + ...getScreenDimensions(), + pxr: win.devicePixelRatio, + vp: getViewportDimensions(), + ah: win.screen.availHeight, + mtp: win.navigator.maxTouchPoints + } + } + }; +} + +function getTopMostAccessibleWindow() { + let mostAccessibleWindow = getWindowSelf(); + + try { + while (mostAccessibleWindow.parent !== mostAccessibleWindow && + mostAccessibleWindow.parent.document) { + mostAccessibleWindow = mostAccessibleWindow.parent; + } + } catch (err) { + // Do not throw an exception if we can't access the topmost frame. + } + + return mostAccessibleWindow; +} + +function getViewportDimensions() { + const topWin = getTopMostAccessibleWindow(); + const documentElement = topWin.document.documentElement; + + return { + w: documentElement.clientWidth, + h: documentElement.clientHeight, + }; +} + +function getScreenDimensions() { + const { + innerWidth: windowWidth, + innerHeight: windowHeight, + screen + } = getWindowSelf(); + + const [biggerDimension, smallerDimension] = [ + Math.max(screen.width, screen.height), + Math.min(screen.width, screen.height), + ]; + + if (windowHeight > windowWidth) { // Portrait mode + return { + w: smallerDimension, + h: biggerDimension, + }; + } + + // Landscape mode + return { + w: biggerDimension, + h: smallerDimension, + }; +} + export const spec = { NON_MEASURABLE, diff --git a/test/spec/modules/33acrossBidAdapter_spec.js b/test/spec/modules/33acrossBidAdapter_spec.js index 4c5ff808bc0..3657f7da912 100644 --- a/test/spec/modules/33acrossBidAdapter_spec.js +++ b/test/spec/modules/33acrossBidAdapter_spec.js @@ -30,6 +30,21 @@ describe('33acrossBidAdapter:', function () { site: { id: siteId }, + device: { + ext: { + ttx: { + w: 1024, + h: 728, + pxr: 2, + vp: { + w: 800, + h: 600 + }, + ah: 500, + mtp: 0 + } + } + }, id: 'r1', regs: { ext: { @@ -117,7 +132,7 @@ describe('33acrossBidAdapter:', function () { this.withProduct = (prod = 'siab') => { ttxRequest.imp.forEach((imp) => { - Object.assign(imp, { + utils.mergeDeep(imp, { ext: { ttx: { prod @@ -129,6 +144,18 @@ describe('33acrossBidAdapter:', function () { return this; }; + this.withGpid = (gpid) => { + ttxRequest.imp.forEach((imp) => { + utils.mergeDeep(imp, { + ext: { + gpid + } + }); + }); + + return this; + }; + this.withGdprConsent = (consent, gdpr) => { Object.assign(ttxRequest, { user: { @@ -166,6 +193,12 @@ describe('33acrossBidAdapter:', function () { return this; }; + this.withDevice = (device) => { + utils.mergeDeep(ttxRequest, { device }); + + return this; + }; + this.withPageUrl = pageUrl => { Object.assign(ttxRequest.site, { page: pageUrl @@ -360,8 +393,21 @@ describe('33acrossBidAdapter:', function () { }; win = { parent: null, + devicePixelRatio: 2, + screen: { + width: 1024, + height: 728, + availHeight: 500 + }, + navigator: { + maxTouchPoints: 0 + }, document: { - visibilityState: 'visible' + visibilityState: 'visible', + documentElement: { + clientWidth: 800, + clientHeight: 600 + } }, innerWidth: 800, @@ -373,7 +419,6 @@ describe('33acrossBidAdapter:', function () { .withBanner() .build() ); - sandbox = sinon.sandbox.create(); sandbox.stub(Date, 'now').returns(1); sandbox.stub(document, 'getElementById').returns(element); @@ -755,6 +800,151 @@ describe('33acrossBidAdapter:', function () { const [ buildRequest ] = spec.buildRequests(bidRequests); validateBuiltServerRequest(buildRequest, serverRequest); }); + + context('when all the wrapping windows are accessible', function() { + it('returns the viewport dimensions of the top most accessible window', function() { + const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withDevice({ + ext: { + ttx: { + vp: { + w: 6789, + h: 2345 + } + } + } + }) + .withProduct() + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + + sandbox.stub(win, 'parent').value({ + document: { + documentElement: { + clientWidth: 1234, + clientHeight: 4567 + } + }, + parent: { + document: { + documentElement: { + clientWidth: 6789, + clientHeight: 2345 + } + }, + } + }); + + const [ buildRequest ] = spec.buildRequests(bidRequests); + validateBuiltServerRequest(buildRequest, serverRequest); + }); + }); + + context('when one of the wrapping windows cannot be accessed', function() { + it('returns the viewport dimensions of the top most accessible window', function() { + const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withDevice({ + ext: { + ttx: { + vp: { + w: 9876, + h: 5432 + } + } + } + }) + .withProduct() + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + const notAccessibleParentWindow = {}; + + Object.defineProperty(notAccessibleParentWindow, 'document', { + get() { throw new Error('fakeError'); } + }); + + sandbox.stub(win, 'parent').value({ + document: { + documentElement: { + clientWidth: 1234, + clientHeight: 4567 + } + }, + parent: { + parent: notAccessibleParentWindow, + document: { + documentElement: { + clientWidth: 9876, + clientHeight: 5432 + } + }, + } + }); + + const [ buildRequest ] = spec.buildRequests(bidRequests); + validateBuiltServerRequest(buildRequest, serverRequest); + }); + }); + }); + + it('returns the screen dimensions', function() { + const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withDevice({ + ext: { + ttx: { + w: 1024, + h: 728 + } + } + }) + .withProduct() + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + + win.screen.width = 1024; + win.screen.height = 728; + + const [ buildRequest ] = spec.buildRequests(bidRequests); + + validateBuiltServerRequest(buildRequest, serverRequest); + }); + + context('when the window height is greater than the width', function() { + it('returns the smaller screen dimension as the width', function() { + const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withDevice({ + ext: { + ttx: { + w: 728, + h: 1024 + } + } + }) + .withProduct() + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + + win.screen.width = 1024; + win.screen.height = 728; + + win.innerHeight = 728; + win.innerWidth = 727; + + const [ buildRequest ] = spec.buildRequests(bidRequests); + + validateBuiltServerRequest(buildRequest, serverRequest); + }); }); context('when tab is inactive', function() { @@ -977,6 +1167,41 @@ describe('33acrossBidAdapter:', function () { }); }); + context('when Global Placement ID (gpid) is defined', function() { + let bidderRequest; + + beforeEach(function() { + bidderRequest = {}; + }); + + it('passes the Global Placement ID (gpid) in the request', function() { + const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() + .withGpid('fakeGPID0') + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + + let copyBidRequest = utils.deepClone(bidRequests); + const bidRequestsWithGpid = copyBidRequest.map(function(bidRequest, index) { + return { + ...bidRequest, + ortb2Imp: { + ext: { + gpid: 'fakeGPID' + index + } + } + }; + }); + + const [ builtServerRequest ] = spec.buildRequests(bidRequestsWithGpid, bidderRequest); + + validateBuiltServerRequest(builtServerRequest, serverRequest); + }); + }); + context('when referer value is not available', function() { it('returns corresponding server requests without site.page set', function() { const bidderRequest = { From 58123576ddd62c4de65f8f0e10f6b65afb96218e Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 28 Jul 2022 05:52:16 -0700 Subject: [PATCH 007/246] Core & multiple modules: strict purpose 1 consent option; do not require vendor consent for "core" storage / ID modules (#8661) * Allow sharedId to work without vendor consent * Remove superfluous enforcement check from ixBidAdapter * Make core storage respect device access rules * respect storage access enforcement in userSync.js * UserID: check whether storage is enabled only once consent can be enforced * Add pubProvidedId * GDPR enforcement: enforce consent when data is not available, but GDPR module is enabled * Always enforce deviceAccess; move vendorless storage P1 enforcement behind `consentManagement.strictStorageEnforcement` --- modules/gdprEnforcement.js | 54 +++++++++--- modules/ixBidAdapter.js | 13 +-- modules/pubCommonId.js | 2 +- modules/sharedIdSystem.js | 8 +- modules/userId/index.js | 63 +++++++++----- src/storageManager.js | 21 ++--- src/userSync.js | 14 +-- test/helpers/consentData.js | 5 ++ test/spec/modules/adnuntiusBidAdapter_spec.js | 7 +- test/spec/modules/gdprEnforcement_spec.js | 87 ++++++++++++++++--- test/spec/modules/ixBidAdapter_spec.js | 1 + test/spec/modules/userId_spec.js | 10 ++- test/spec/unit/core/storageManager_spec.js | 29 ++++++- test/test_deps.js | 1 + 14 files changed, 220 insertions(+), 95 deletions(-) diff --git a/modules/gdprEnforcement.js b/modules/gdprEnforcement.js index 9e300e1de97..b7035c509b6 100644 --- a/modules/gdprEnforcement.js +++ b/modules/gdprEnforcement.js @@ -12,6 +12,15 @@ import {validateStorageEnforcement} from '../src/storageManager.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; +// modules for which vendor consent is not needed (see https://github.com/prebid/Prebid.js/issues/8161) +const VENDORLESS_MODULES = new Set([ + 'sharedId', + 'pubCommonId', + 'pubProvidedId', +]); + +export const STRICT_STORAGE_ENFORCEMENT = 'strictStorageEnforcement'; + const TCF2 = { 'purpose1': { id: 1, name: 'storage' }, 'purpose2': { id: 2, name: 'basicAds' }, @@ -44,6 +53,7 @@ const biddersBlocked = []; const analyticsBlocked = []; let hooksAdded = false; +let strictStorageEnforcement = false; // Helps in stubbing these functions in unit tests. export const internal = { @@ -116,6 +126,18 @@ function getGvlidForAnalyticsAdapter(code) { return adapterManager.getAnalyticsAdapter(code) && (adapterManager.getAnalyticsAdapter(code).gvlid || null); } +export function shouldEnforce(consentData, purpose, name) { + if (consentData == null && gdprDataHandler.enabled) { + // there is no consent data, but the GDPR module has been installed and configured + // NOTE: this check is not foolproof, as when Prebid first loads, enforcement hooks have not been attached yet + // This piece of code would not run at all, and `gdprDataHandler.enabled` would be false, until the first + // `setConfig({consentManagement})` + logWarn(`Attempting operation that requires purpose ${purpose} consent while consent data is not available${name ? ` (module: ${name})` : ''}. Assuming no consent was given.`) + return true; + } + return consentData && consentData.gdprApplies; +} + /** * This function takes in a rule and consentData and validates against the consentData provided. Depending on what it returns, * the caller may decide to suppress a TCF-sensitive activity. @@ -123,9 +145,10 @@ function getGvlidForAnalyticsAdapter(code) { * @param {Object} consentData - gdpr consent data * @param {string=} currentModule - Bidder code of the current module * @param {number=} gvlId - GVL ID for the module + * @param vendorlessModule a predicate function that takes a module name, and returns true if the module does not need vendor consent * @returns {boolean} */ -export function validateRules(rule, consentData, currentModule, gvlId) { +export function validateRules(rule, consentData, currentModule, gvlId, vendorlessModule = VENDORLESS_MODULES.has.bind(VENDORLESS_MODULES)) { const purposeId = TCF2[Object.keys(TCF2).filter(purposeName => TCF2[purposeName].name === rule.purpose)[0]].id; // return 'true' if vendor present in 'vendorExceptions' @@ -143,7 +166,7 @@ export function validateRules(rule, consentData, currentModule, gvlId) { or the user has consented. Similar with vendors. */ const purposeAllowed = rule.enforcePurpose === false || purposeConsent === true; - const vendorAllowed = rule.enforceVendor === false || vendorConsent === true; + const vendorAllowed = vendorlessModule(currentModule) || rule.enforceVendor === false || vendorConsent === true; /* Few if any vendors should be declaring Legitimate Interest for Device Access (Purpose 1), but some are claiming @@ -160,20 +183,24 @@ export function validateRules(rule, consentData, currentModule, gvlId) { /** * This hook checks whether module has permission to access device or not. Device access include cookie and local storage * @param {Function} fn reference to original function (used by hook logic) + * @param isVendorless if true, do not require vendor consent (for e.g. core modules) * @param {Number=} gvlid gvlid of the module * @param {string=} moduleName name of the module + * @param result */ -export function deviceAccessHook(fn, gvlid, moduleName, result) { +export function deviceAccessHook(fn, isVendorless, gvlid, moduleName, result, {validate = validateRules} = {}) { result = Object.assign({}, { hasEnforcementHook: true }); if (!hasDeviceAccess()) { logWarn('Device access is disabled by Publisher'); result.valid = false; - fn.call(this, gvlid, moduleName, result); + } else if (isVendorless && !strictStorageEnforcement) { + // for vendorless (core) storage, do not enforce rules unless strictStorageEnforcement is set + result.valid = true; } else { const consentData = gdprDataHandler.getConsentData(); - if (consentData && consentData.gdprApplies) { + if (shouldEnforce(consentData, 1, moduleName)) { const curBidder = config.getCurrentBidder(); // Bidders have a copy of storage object with bidder code binded. Aliases will also pass the same bidder code when invoking storage functions and hence if alias tries to access device we will try to grab the gvl id for alias instead of original bidder if (curBidder && (curBidder != moduleName) && adapterManager.aliasRegistry[curBidder] === moduleName) { @@ -182,21 +209,19 @@ export function deviceAccessHook(fn, gvlid, moduleName, result) { gvlid = getGvlid(moduleName) || gvlid; } const curModule = moduleName || curBidder; - let isAllowed = validateRules(purpose1Rule, consentData, curModule, gvlid); + let isAllowed = validate(purpose1Rule, consentData, curModule, gvlid, isVendorless ? () => true : undefined); if (isAllowed) { result.valid = true; - fn.call(this, gvlid, moduleName, result); } else { curModule && logWarn(`TCF2 denied device access for ${curModule}`); result.valid = false; storageBlocked.push(curModule); - fn.call(this, gvlid, moduleName, result); } } else { result.valid = true; - fn.call(this, gvlid, moduleName, result); } } + fn.call(this, isVendorless, gvlid, moduleName, result); } /** @@ -206,8 +231,8 @@ export function deviceAccessHook(fn, gvlid, moduleName, result) { */ export function userSyncHook(fn, ...args) { const consentData = gdprDataHandler.getConsentData(); - if (consentData && consentData.gdprApplies) { - const curBidder = config.getCurrentBidder(); + const curBidder = config.getCurrentBidder(); + if (shouldEnforce(consentData, 1, curBidder)) { const gvlid = getGvlid(curBidder); let isAllowed = validateRules(purpose1Rule, consentData, curBidder, gvlid); if (isAllowed) { @@ -228,7 +253,7 @@ export function userSyncHook(fn, ...args) { * @param {Object} consentData GDPR consent data */ export function userIdHook(fn, submodules, consentData) { - if (consentData && consentData.gdprApplies) { + if (shouldEnforce(consentData, 1, 'User ID')) { let userIdModules = submodules.map((submodule) => { const gvlid = getGvlid(submodule.submodule); const moduleName = submodule.submodule.name; @@ -255,7 +280,7 @@ export function userIdHook(fn, submodules, consentData) { */ export function makeBidRequestsHook(fn, adUnits, ...args) { const consentData = gdprDataHandler.getConsentData(); - if (consentData && consentData.gdprApplies) { + if (shouldEnforce(consentData, 2)) { adUnits.forEach(adUnit => { adUnit.bids = adUnit.bids.filter(bid => { const currBidder = bid.bidder; @@ -283,7 +308,7 @@ export function makeBidRequestsHook(fn, adUnits, ...args) { */ export function enableAnalyticsHook(fn, config) { const consentData = gdprDataHandler.getConsentData(); - if (consentData && consentData.gdprApplies) { + if (shouldEnforce(consentData, 7, 'Analytics')) { if (!isArray(config)) { config = [config] } @@ -341,6 +366,7 @@ export function setEnforcementConfig(config) { } else { enforcementRules = rules; } + strictStorageEnforcement = !!deepAccess(config, STRICT_STORAGE_ENFORCEMENT); purpose1Rule = find(enforcementRules, hasPurpose1); purpose2Rule = find(enforcementRules, hasPurpose2); diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index d4ac5208413..62ffba3d8dc 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -5,7 +5,6 @@ import { deepClone, deepSetValue, getGptSlotInfoForAdUnitCode, - hasDeviceAccess, inIframe, isArray, isEmpty, @@ -20,7 +19,7 @@ import { import {BANNER, VIDEO, NATIVE} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; import CONSTANTS from '../src/constants.json'; -import {getStorageManager, validateStorageEnforcement} from '../src/storageManager.js'; +import {getStorageManager} from '../src/storageManager.js'; import * as events from '../src/events.js'; import {find} from '../src/polyfill.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; @@ -1474,15 +1473,7 @@ function storeErrorEventData(data) { */ function localStorageHandler(data) { if (data.type === 'ERROR' && data.arguments && data.arguments[1] && data.arguments[1].bidder === BIDDER_CODE) { - const DEFAULT_ENFORCEMENT_SETTINGS = { - hasEnforcementHook: false, - valid: hasDeviceAccess() - }; - validateStorageEnforcement(GLOBAL_VENDOR_ID, BIDDER_CODE, DEFAULT_ENFORCEMENT_SETTINGS, (permissions) => { - if (permissions.valid) { - storeErrorEventData(data); - } - }); + storeErrorEventData(data); } } diff --git a/modules/pubCommonId.js b/modules/pubCommonId.js index faca59cce1c..e3fd21e7260 100644 --- a/modules/pubCommonId.js +++ b/modules/pubCommonId.js @@ -9,7 +9,7 @@ import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; import { getStorageManager } from '../src/storageManager.js'; -const storage = getStorageManager(); +const storage = getStorageManager({moduleName: 'pubCommonId'}); const ID_NAME = '_pubcid'; const OPTOUT_NAME = '_pubcid_optout'; diff --git a/modules/sharedIdSystem.js b/modules/sharedIdSystem.js index 656b62815c7..99a34ae17f4 100644 --- a/modules/sharedIdSystem.js +++ b/modules/sharedIdSystem.js @@ -10,8 +10,7 @@ import {submodule} from '../src/hook.js'; import { coppaDataHandler } from '../src/adapterManager.js'; import {getStorageManager} from '../src/storageManager.js'; -const GVLID = 887; -export const storage = getStorageManager({gvlid: GVLID, moduleName: 'pubCommonId'}); +export const storage = getStorageManager({moduleName: 'pubCommonId'}); const COOKIE = 'cookie'; const LOCAL_STORAGE = 'html5'; const OPTOUT_NAME = '_pubcid_optout'; @@ -74,11 +73,6 @@ export const sharedIdSystemSubmodule = { */ name: 'sharedId', aliasName: 'pubCommonId', - /** - * Vendor id of prebid - * @type {Number} - */ - gvlid: GVLID, /** * decode the stored id value for passing to bid requests diff --git a/modules/userId/index.js b/modules/userId/index.js index ecc494846ee..f42c4c7827f 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -167,9 +167,6 @@ const CONSENT_DATA_COOKIE_STORAGE_CONFIG = { export const PBJS_USER_ID_OPTOUT_NAME = '_pbjs_id_optout'; export const coreStorage = getCoreStorageManager('userid'); -/** @type {string[]} */ -let validStorageTypes = []; - /** @type {boolean} */ let addedUserIdHook = false; @@ -834,7 +831,19 @@ function populateSubmoduleId(submodule, consentData, storedConsentData, forceRef } function initSubmodules(dest, submodules, consentData, forceRefresh = false) { - // gdpr consent with purpose one is required, otherwise exit immediately + if (!submodules.length) return []; // to simplify log messages from here on + + // filter out submodules whose storage type is not enabled + // this needs to be done here (after consent data has loaded) so that enforcement may disable storage globally + const storageTypes = getActiveStorageTypes(); + submodules = submodules.filter((submod) => !submod.config.storage || storageTypes.has(submod.config.storage.type)); + + if (!submodules.length) { + logWarn(`${MODULE_NAME} - no ID module is configured for one of the available storage types:`, Array.from(storageTypes)) + return []; + } + + // another consent check, this time each module is checked for consent with its own gvlid let { userIdModules, hasValidated } = validateGdprEnforcement(submodules, consentData); if (!hasValidated && !hasPurpose1Consent(consentData)) { logWarn(`${MODULE_NAME} - gdpr permission not valid for local storage or cookies, exit module`); @@ -885,7 +894,7 @@ function updateInitializedSubmodules(dest, submodule) { * @param {string[]} activeStorageTypes * @returns {SubmoduleConfig[]} */ -function getValidSubmoduleConfigs(configRegistry, submoduleRegistry, activeStorageTypes) { +function getValidSubmoduleConfigs(configRegistry, submoduleRegistry) { if (!Array.isArray(configRegistry)) { return []; } @@ -895,11 +904,11 @@ function getValidSubmoduleConfigs(configRegistry, submoduleRegistry, activeStora return carry; } // Validate storage config contains 'type' and 'name' properties with non-empty string values - // 'type' must be a value currently enabled in the browser + // 'type' must be one of html5, cookies if (config.storage && !isEmptyStr(config.storage.type) && !isEmptyStr(config.storage.name) && - activeStorageTypes.indexOf(config.storage.type) !== -1) { + ALL_STORAGE_TYPES.has(config.storage.type)) { carry.push(config); } else if (isPlainObject(config.value)) { carry.push(config); @@ -910,11 +919,33 @@ function getValidSubmoduleConfigs(configRegistry, submoduleRegistry, activeStora }, []); } +const ALL_STORAGE_TYPES = new Set([LOCAL_STORAGE, COOKIE]); + +function getActiveStorageTypes() { + const storageTypes = []; + let disabled = false; + if (coreStorage.localStorageIsEnabled()) { + storageTypes.push(LOCAL_STORAGE); + if (coreStorage.getDataFromLocalStorage(PBJS_USER_ID_OPTOUT_NAME)) { + logInfo(`${MODULE_NAME} - opt-out localStorage found, storage disabled`); + disabled = true; + } + } + if (coreStorage.cookiesAreEnabled()) { + storageTypes.push(COOKIE); + if (coreStorage.getCookie(PBJS_USER_ID_OPTOUT_NAME)) { + logInfo(`${MODULE_NAME} - opt-out cookie found, storage disabled`); + disabled = true; + } + } + return new Set(disabled ? [] : storageTypes) +} + /** * update submodules by validating against existing configs and storage types */ function updateSubmodules() { - const configs = getValidSubmoduleConfigs(configRegistry, submoduleRegistry, validStorageTypes); + const configs = getValidSubmoduleConfigs(configRegistry, submoduleRegistry); if (!configs.length) { return; } @@ -987,22 +1018,6 @@ export function init(config, {delay = GreedyPromise.timeout} = {}) { } submoduleRegistry = []; - // list of browser enabled storage types - validStorageTypes = [ - coreStorage.localStorageIsEnabled() ? LOCAL_STORAGE : null, - coreStorage.cookiesAreEnabled() ? COOKIE : null - ].filter(i => i !== null); - - // exit immediately if opt out cookie or local storage keys exists. - if (validStorageTypes.indexOf(COOKIE) !== -1 && coreStorage.getCookie(PBJS_USER_ID_OPTOUT_NAME)) { - logInfo(`${MODULE_NAME} - opt-out cookie found, exit module`); - return; - } - if (validStorageTypes.indexOf(LOCAL_STORAGE) !== -1 && coreStorage.getDataFromLocalStorage(PBJS_USER_ID_OPTOUT_NAME)) { - logInfo(`${MODULE_NAME} - opt-out localStorage found, exit module`); - return; - } - // listen for config userSyncs to be set configListener = config.getConfig('userSync', conf => { // Note: support for 'usersync' was dropped as part of Prebid.js 4.0 diff --git a/src/storageManager.js b/src/storageManager.js index e703e0774d3..4ab224f8d9b 100644 --- a/src/storageManager.js +++ b/src/storageManager.js @@ -1,6 +1,5 @@ import {hook} from './hook.js'; import {hasDeviceAccess, checkCookieSupport, logError, logInfo, isPlainObject} from './utils.js'; -import {includes} from './polyfill.js'; import {bidderSettings as defaultBidderSettings} from './bidderSettings.js'; const moduleTypeWhiteList = ['core', 'prebid-module']; @@ -33,13 +32,11 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} = const storageAllowed = bidderSettings.get(bidderCode, 'storageAllowed'); return storageAllowed == null ? false : storageAllowed; } + + const isVendorless = moduleTypeWhiteList.includes(moduleType); + function isValid(cb) { - if (includes(moduleTypeWhiteList, moduleType)) { - let result = { - valid: true - } - return cb(result); - } else if (!isBidderAllowed()) { + if (!isBidderAllowed()) { logInfo(`bidderSettings denied access to device storage for bidder '${bidderCode}'`); const result = {valid: false}; return cb(result); @@ -48,7 +45,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} = let hookDetails = { hasEnforcementHook: false } - validateStorageEnforcement(gvlid, bidderCode || moduleName, hookDetails, function(result) { + validateStorageEnforcement(isVendorless, gvlid, bidderCode || moduleName, hookDetails, function(result) { if (result && result.hasEnforcementHook) { value = cb(result); } else { @@ -149,11 +146,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} = const cookiesAreEnabled = function (done) { let cb = function (result) { if (result && result.valid) { - if (checkCookieSupport()) { - return true; - } - window.document.cookie = 'prebid.cookieTest'; - return window.document.cookie.indexOf('prebid.cookieTest') !== -1; + return checkCookieSupport(); } return false; } @@ -303,7 +296,7 @@ export function newStorageManager({gvlid, moduleName, bidderCode, moduleType} = /** * This hook validates the storage enforcement if gdprEnforcement module is included */ -export const validateStorageEnforcement = hook('async', function(gvlid, moduleName, hookDetails, callback) { +export const validateStorageEnforcement = hook('async', function(isVendorless, gvlid, moduleName, hookDetails, callback) { callback(hookDetails); }, 'validateStorageEnforcement'); diff --git a/src/userSync.js b/src/userSync.js index 96c3d662cad..674114e11f6 100644 --- a/src/userSync.js +++ b/src/userSync.js @@ -315,12 +315,16 @@ export function newUserSync(userSyncDependencies) { return publicApi; } -const browserSupportsCookies = !isSafariBrowser() && storage.cookiesAreEnabled(); - -export const userSync = newUserSync({ +export const userSync = newUserSync(Object.defineProperties({ config: config.getConfig('userSync'), - browserSupportsCookies: browserSupportsCookies -}); +}, { + browserSupportsCookies: { + get: function() { + // call storage lazily to give time for consent data to be available + return !isSafariBrowser() && storage.cookiesAreEnabled(); + } + } +})); /** * @typedef {Object} UserSyncDependencies diff --git a/test/helpers/consentData.js b/test/helpers/consentData.js index b59388d67f2..c708e397bd6 100644 --- a/test/helpers/consentData.js +++ b/test/helpers/consentData.js @@ -2,6 +2,11 @@ import {gdprDataHandler} from 'src/adapterManager.js'; import {GreedyPromise} from '../../src/utils/promise.js'; export function mockGdprConsent(sandbox, getConsentData = () => null) { + sandbox.stub(gdprDataHandler, 'enabled').get(() => true) sandbox.stub(gdprDataHandler, 'promise').get(() => GreedyPromise.resolve(getConsentData())); sandbox.stub(gdprDataHandler, 'getConsentData').callsFake(getConsentData) } + +beforeEach(() => { + gdprDataHandler.reset(); +}) diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index 307830dd4ef..403ba63d7d1 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -11,8 +11,11 @@ describe('adnuntiusBidAdapter', function () { const GVLID = 855; const usi = utils.generateUUID() const meta = [{ key: 'usi', value: usi }] - const storage = getStorageManager({ gvlid: GVLID, moduleName: 'adnuntius' }) - storage.setDataInLocalStorage('adn.metaData', JSON.stringify(meta)) + + before(() => { + const storage = getStorageManager({gvlid: GVLID, moduleName: 'adnuntius'}) + storage.setDataInLocalStorage('adn.metaData', JSON.stringify(meta)) + }); beforeEach(function () { $$PREBID_GLOBAL$$.bidderSettings = { diff --git a/test/spec/modules/gdprEnforcement_spec.js b/test/spec/modules/gdprEnforcement_spec.js index a78f5ac948e..ddd522decab 100644 --- a/test/spec/modules/gdprEnforcement_spec.js +++ b/test/spec/modules/gdprEnforcement_spec.js @@ -10,13 +10,16 @@ import { purpose2Rule, enableAnalyticsHook, getGvlid, - internal + internal, STRICT_STORAGE_ENFORCEMENT } from 'modules/gdprEnforcement.js'; import { config } from 'src/config.js'; import adapterManager, { gdprDataHandler } from 'src/adapterManager.js'; import * as utils from 'src/utils.js'; import { validateStorageEnforcement } from 'src/storageManager.js'; import * as events from 'src/events.js'; +import 'modules/appnexusBidAdapter.js'; // some tests expect this to be in the adapter registry +import 'src/prebid.js' +import {hook} from '../../../src/hook.js'; describe('gdpr enforcement', function () { let nextFnSpy; @@ -97,6 +100,10 @@ describe('gdpr enforcement', function () { } }; + before(() => { + hook.ready(); + }); + after(function () { validateStorageEnforcement.getHooks({ hook: deviceAccessHook }).remove(); $$PREBID_GLOBAL$$.requestBids.getHooks().remove(); @@ -143,13 +150,13 @@ describe('gdpr enforcement', function () { } }); - deviceAccessHook(nextFnSpy); + deviceAccessHook(nextFnSpy, false); expect(nextFnSpy.calledOnce).to.equal(true); let result = { hasEnforcementHook: true, valid: false } - sinon.assert.calledWith(nextFnSpy, undefined, undefined, result); + sinon.assert.calledWith(nextFnSpy, false, undefined, undefined, result); }); it('should only check for consent for vendor exceptions when enforcePurpose and enforceVendor are false', function () { @@ -171,8 +178,8 @@ describe('gdpr enforcement', function () { consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); - deviceAccessHook(nextFnSpy, 1, 'appnexus'); - deviceAccessHook(nextFnSpy, 5, 'rubicon'); + deviceAccessHook(nextFnSpy, false, 1, 'appnexus'); + deviceAccessHook(nextFnSpy, false, 5, 'rubicon'); expect(logWarnSpy.callCount).to.equal(0); }); @@ -194,8 +201,8 @@ describe('gdpr enforcement', function () { consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); - deviceAccessHook(nextFnSpy, 1, 'appnexus'); - deviceAccessHook(nextFnSpy, 3, 'rubicon'); + deviceAccessHook(nextFnSpy, false, 1, 'appnexus'); + deviceAccessHook(nextFnSpy, false, 3, 'rubicon'); expect(logWarnSpy.callCount).to.equal(1); }); @@ -217,13 +224,13 @@ describe('gdpr enforcement', function () { consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); - deviceAccessHook(nextFnSpy, 1, 'appnexus'); + deviceAccessHook(nextFnSpy, false, 1, 'appnexus'); expect(nextFnSpy.calledOnce).to.equal(true); let result = { hasEnforcementHook: true, valid: true } - sinon.assert.calledWith(nextFnSpy, 1, 'appnexus', result); + sinon.assert.calledWith(nextFnSpy, false, 1, 'appnexus', result); }); it('should use gvlMapping set by publisher', function() { @@ -248,13 +255,13 @@ describe('gdpr enforcement', function () { consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); - deviceAccessHook(nextFnSpy, 1, 'appnexus'); + deviceAccessHook(nextFnSpy, false, 1, 'appnexus'); expect(nextFnSpy.calledOnce).to.equal(true); let result = { hasEnforcementHook: true, valid: true } - sinon.assert.calledWith(nextFnSpy, 4, 'appnexus', result); + sinon.assert.calledWith(nextFnSpy, false, 4, 'appnexus', result); config.resetConfig(); }); @@ -283,16 +290,43 @@ describe('gdpr enforcement', function () { consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); - deviceAccessHook(nextFnSpy, 1, 'appnexus'); + deviceAccessHook(nextFnSpy, false, 1, 'appnexus'); expect(nextFnSpy.calledOnce).to.equal(true); let result = { hasEnforcementHook: true, valid: true } - sinon.assert.calledWith(nextFnSpy, 4, 'appnexus', result); + sinon.assert.calledWith(nextFnSpy, false, 4, 'appnexus', result); config.resetConfig(); curBidderStub.restore(); }); + + it(`should mark module as vendorless for rule validation when isVendorless = true and ${STRICT_STORAGE_ENFORCEMENT} is set`, () => { + setEnforcementConfig({ + [STRICT_STORAGE_ENFORCEMENT]: true + }); + let consentData = { + vendorData: staticConfig.consentData.getTCData, + gdprApplies: true + } + gdprDataHandlerStub.returns(consentData); + const validate = sinon.stub().callsFake(() => true); + deviceAccessHook(nextFnSpy, true, 123, 'mockModule', undefined, {validate}); + expect(validate.args[0][4]('mockModule')).to.be.true; + }); + + it(`should not enforce consent for vendorless modules if ${STRICT_STORAGE_ENFORCEMENT} is not set`, () => { + setEnforcementConfig({}); + let consentData = { + vendorData: staticConfig.consentData.getTCData, + gdprApplies: true + } + gdprDataHandlerStub.returns(consentData); + const validate = sinon.stub().callsFake(() => false); + deviceAccessHook(nextFnSpy, true, 123, 'mockModule', undefined, {validate}); + sinon.assert.callCount(validate, 0); + sinon.assert.calledWith(nextFnSpy, true, 123, 'mockModule', {hasEnforcementHook: true, valid: true}); + }) }); describe('userSyncHook', function () { @@ -888,6 +922,33 @@ describe('gdpr enforcement', function () { expect(isAllowed).to.equal(true); }); + describe('when module does not need vendor consent', () => { + Object.entries({ + 'storage': 1, + 'basicAds': 2, + 'measurement': 7 + }).forEach(([purpose, purposeNo]) => { + describe(`for purpose ${purpose}`, () => { + const rule = createGdprRule(purpose); + Object.entries({ + 'allowed': true, + 'not allowed': false + }).forEach(([t, consentGiven]) => { + it(`should be ${t} when purpose is ${t}`, () => { + const consent = utils.deepClone(consentData); + consent.vendorData.purpose.consents[purposeNo] = consentGiven; + // vendor consent (and gvlid) should be ignored + consent.vendorData.vendor.consents[123] = !consentGiven; + // take legitimate interest out of the picture for this test + consent.vendorData.purpose.legitimateInterests = {}; + const actual = validateRules(rule, consent, 'mockModule', 123, () => true); + expect(actual).to.equal(consentGiven); + }) + }) + }) + }) + }) + describe('Purpose 2 special case', function () { const consentDataWithLIFalse = utils.deepClone(consentData); consentDataWithLIFalse.vendorData.purpose.legitimateInterests['2'] = false; diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 086ec305ccc..243b702f03d 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -3462,6 +3462,7 @@ describe('IndexexchangeAdapter', function () { it('should not save error data into localstorage if consent is not given', () => { config.setConfig({ deviceAccess: false }); + storage.localStorageIsEnabled.restore(); // let core manage device access const bid = utils.deepClone(DEFAULT_MULTIFORMAT_VIDEO_VALID_BID[0]); bid.params.size = ['400', 100]; expect(spec.isBidRequestValid(bid)).to.be.false; diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 486018d41c4..77ba1396afb 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -698,11 +698,15 @@ describe('User ID', function () { config.resetConfig(); }); - it('fails initialization if opt out cookie exists', function () { + it('does not fetch ids if opt out cookie exists', function () { init(config); setSubmoduleRegistry([sharedIdSystemSubmodule]); - config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); - expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - opt-out cookie found, exit module'); + const cfg = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); + cfg.userSync.auctionDelay = 1; // to let init complete without an auction + config.setConfig(cfg); + return getGlobal().getUserIdsAsync().then((uid) => { + expect(uid).to.eql({}); + }) }); it('initializes if no opt out cookie exists', function () { diff --git a/test/spec/unit/core/storageManager_spec.js b/test/spec/unit/core/storageManager_spec.js index dacde5491b7..cb38aed9e47 100644 --- a/test/spec/unit/core/storageManager_spec.js +++ b/test/spec/unit/core/storageManager_spec.js @@ -3,7 +3,7 @@ import { getCoreStorageManager, storageCallbacks, getStorageManager, - newStorageManager + newStorageManager, validateStorageEnforcement } from 'src/storageManager.js'; import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; @@ -55,6 +55,33 @@ describe('storage manager', function() { deviceAccessSpy.restore(); }); + describe(`core storage`, () => { + let storage, validateHook; + + beforeEach(() => { + storage = getCoreStorageManager(); + validateHook = sinon.stub().callsFake(function (next, ...args) { + next.apply(this, args); + }); + validateStorageEnforcement.before(validateHook, 99); + }); + + afterEach(() => { + validateStorageEnforcement.getHooks({hook: validateHook}).remove(); + config.resetConfig(); + }) + + it('should respect (vendorless) consent enforcement', () => { + storage.localStorageIsEnabled(); + expect(validateHook.args[0][1]).to.eql(true); // isVendorless should be set to true + }); + + it('should respect the deviceAccess flag', () => { + config.setConfig({deviceAccess: false}); + expect(storage.localStorageIsEnabled()).to.be.false + }) + }) + describe('localstorage forbidden access in 3rd-party context', function() { let errorLogSpy; let originalLocalStorage; diff --git a/test/test_deps.js b/test/test_deps.js index 77fbad93e1c..35713106f8c 100644 --- a/test/test_deps.js +++ b/test/test_deps.js @@ -4,6 +4,7 @@ window.process = { } }; +require('test/helpers/consentData.js'); require('test/helpers/prebidGlobal.js'); require('test/mocks/adloaderStub.js'); require('test/mocks/xhr.js'); From 95ee4932dfbf699029cc9ebf3fa16e6fe723e8ac Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 28 Jul 2022 06:03:34 -0700 Subject: [PATCH 008/246] Topics FPD module: initial release (#8646) * Topics FPD module * Small improvements * Map taxonomyVersion to segtax, modelVersion to segclass * Convert fpdModule & topicsFpdModule to use GreedyPromise --- modules/.submodules.json | 3 +- modules/fpdModule/index.js | 22 +- modules/topicsFpdModule.js | 80 ++++++++ test/spec/modules/fpdModule_spec.js | 127 ++++++------ test/spec/modules/topicsFpdModule_spec.js | 239 ++++++++++++++++++++++ 5 files changed, 406 insertions(+), 65 deletions(-) create mode 100644 modules/topicsFpdModule.js create mode 100644 test/spec/modules/topicsFpdModule_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index d24e7ff96f5..b031a6bac02 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -70,7 +70,8 @@ ], "fpdModule": [ "enrichmentFpdModule", - "validationFpdModule" + "validationFpdModule", + "topicsFpdModule" ] }, "libraries": { diff --git a/modules/fpdModule/index.js b/modules/fpdModule/index.js index bd735e215a4..553cab47b66 100644 --- a/modules/fpdModule/index.js +++ b/modules/fpdModule/index.js @@ -4,6 +4,8 @@ */ import { config } from '../../src/config.js'; import { module, getHook } from '../../src/hook.js'; +import {logError} from '../../src/utils.js'; +import {GreedyPromise} from '../../src/utils/promise.js'; let submodules = []; @@ -17,19 +19,27 @@ export function reset() { export function processFpd({global = {}, bidder = {}} = {}) { let modConf = config.getConfig('firstPartyData') || {}; - + let result = GreedyPromise.resolve({global, bidder}); submodules.sort((a, b) => { return ((a.queue || 1) - (b.queue || 1)); }).forEach(submodule => { - ({global = global, bidder = bidder} = submodule.processFpd(modConf, {global, bidder})); + result = result.then( + ({global, bidder}) => GreedyPromise.resolve(submodule.processFpd(modConf, {global, bidder})) + .catch((err) => { + logError(`Error in FPD module ${submodule.name}`, err); + return {}; + }) + .then((result) => ({global: result.global || global, bidder: result.bidder || bidder})) + ); }); - - return {global, bidder}; + return result; } export function startAuctionHook(fn, req) { - Object.assign(req.ortb2Fragments, processFpd(req.ortb2Fragments)); - fn.call(this, req); + processFpd(req.ortb2Fragments).then((ortb2Fragments) => { + Object.assign(req.ortb2Fragments, ortb2Fragments); + fn.call(this, req); + }) } function setupHook() { diff --git a/modules/topicsFpdModule.js b/modules/topicsFpdModule.js new file mode 100644 index 00000000000..dbd1d3e90e9 --- /dev/null +++ b/modules/topicsFpdModule.js @@ -0,0 +1,80 @@ +import {logError, logWarn, mergeDeep} from '../src/utils.js'; +import {getRefererInfo} from '../src/refererDetection.js'; +import {submodule} from '../src/hook.js'; +import {GreedyPromise} from '../src/utils/promise.js'; + +const TAXONOMIES = { + // map from topic taxonomyVersion to IAB segment taxonomy + '1': 600 +} + +function partitionBy(field, items) { + return items.reduce((partitions, item) => { + const key = item[field]; + if (!partitions.hasOwnProperty(key)) partitions[key] = []; + partitions[key].push(item); + return partitions; + }, {}); +} + +export function getTopicsData(name, topics, taxonomies = TAXONOMIES) { + return Object.entries(partitionBy('taxonomyVersion', topics)) + .filter(([taxonomyVersion]) => { + if (!taxonomies.hasOwnProperty(taxonomyVersion)) { + logWarn(`Unrecognized taxonomyVersion from Topics API: "${taxonomyVersion}"; topic will be ignored`); + return false; + } + return true; + }).flatMap(([taxonomyVersion, topics]) => + Object.entries(partitionBy('modelVersion', topics)) + .map(([modelVersion, topics]) => { + const datum = { + ext: { + segtax: taxonomies[taxonomyVersion], + segclass: modelVersion + }, + segment: topics.map((topic) => ({id: topic.topic.toString()})) + }; + if (name != null) { + datum.name = name; + } + return datum; + }) + ); +} + +export function getTopics(doc = document) { + let topics = null; + try { + if ('browsingTopics' in doc && doc.featurePolicy.allowsFeature('browsing-topics')) { + topics = GreedyPromise.resolve(doc.browsingTopics()); + } + } catch (e) { + logError('Could not call topics API', e); + } + if (topics == null) { + topics = GreedyPromise.resolve([]); + } + return topics; +} + +const topicsData = getTopics().then((topics) => getTopicsData(getRefererInfo().domain, topics)); + +export function processFpd(config, {global}, {data = topicsData} = {}) { + return data.then((data) => { + if (data.length) { + mergeDeep(global, { + user: { + data + } + }); + } + return {global}; + }); +} + +submodule('firstPartyData', { + name: 'topics', + queue: 1, + processFpd +}); diff --git a/test/spec/modules/fpdModule_spec.js b/test/spec/modules/fpdModule_spec.js index d43ae8309bd..498bed29243 100644 --- a/test/spec/modules/fpdModule_spec.js +++ b/test/spec/modules/fpdModule_spec.js @@ -5,9 +5,6 @@ import {processFpd, registerSubmodules, startAuctionHook, reset} from 'modules/f import * as enrichmentModule from 'modules/enrichmentFpdModule.js'; import * as validationModule from 'modules/validationFpdModule/index.js'; -let enrichments = {...enrichmentModule}; -let validations = {...validationModule}; - describe('the first party data module', function () { afterEach(function () { config.resetConfig(); @@ -18,21 +15,37 @@ describe('the first party data module', function () { global: {key: 'value'}, bidder: {A: {bkey: 'bvalue'}} } - before(() => { + beforeEach(() => { reset(); + }); + + it('should run ortb2Fragments through fpd submodules', () => { registerSubmodules({ name: 'test', - queue: 2, processFpd: function () { return mockFpd; } }); - }) + const req = {ortb2Fragments: {}}; + return new Promise((resolve) => startAuctionHook(resolve, req)) + .then(() => { + expect(req.ortb2Fragments).to.eql(mockFpd); + }) + }); - it('should run ortb2Fragments through fpd submodules', () => { + it('should work with fpd submodules that return promises', () => { + registerSubmodules({ + name: 'test', + processFpd: function () { + return Promise.resolve(mockFpd); + } + }); const req = {ortb2Fragments: {}}; - startAuctionHook(() => null, req); - expect(req.ortb2Fragments).to.eql(mockFpd); + return new Promise((resolve) => { + startAuctionHook(resolve, req); + }).then(() => { + expect(req.ortb2Fragments).to.eql(mockFpd); + }); }); }); @@ -79,7 +92,6 @@ describe('the first party data module', function () { }); it('filters ortb2 data that is set', function () { - let validated; const global = { user: { data: {}, @@ -113,42 +125,42 @@ describe('the first party data module', function () { width = 1120; height = 750; - ({global: validated} = processFpd({global})); - expect(validated.site.ref).to.equal(getRefererInfo().ref || undefined); - expect(validated.site.page).to.equal('https://www.domain.com/path?query=12345'); - expect(validated.site.domain).to.equal('domain.com'); - expect(validated.site.content.data).to.deep.equal([{segment: [{id: 'test'}], name: 'bar'}]); - expect(validated.user.data).to.be.undefined; - expect(validated.device).to.deep.to.equal({w: 1, h: 1}); - expect(validated.site.keywords).to.be.undefined; + return processFpd({global}).then(({global: validated}) => { + expect(validated.site.ref).to.equal(getRefererInfo().ref || undefined); + expect(validated.site.page).to.equal('https://www.domain.com/path?query=12345'); + expect(validated.site.domain).to.equal('domain.com'); + expect(validated.site.content.data).to.deep.equal([{segment: [{id: 'test'}], name: 'bar'}]); + expect(validated.user.data).to.be.undefined; + expect(validated.device).to.deep.to.equal({w: 1, h: 1}); + expect(validated.site.keywords).to.be.undefined; + }); }); it('should not overwrite existing data with default settings', function () { - let validated; const global = { site: { ref: 'https://referer.com' } }; - ({global: validated} = processFpd({global})); - expect(validated.site.ref).to.equal('https://referer.com'); + return processFpd({global}).then(({global: validated}) => { + expect(validated.site.ref).to.equal('https://referer.com'); + }); }); it('should allow overwrite default data with setConfig', function () { - let validated; const global = { site: { ref: 'https://referer.com' } }; - ({global: validated} = processFpd({global})); - expect(validated.site.ref).to.equal('https://referer.com'); + return processFpd({global}).then(({global: validated}) => { + expect(validated.site.ref).to.equal('https://referer.com'); + }); }); it('should filter all data', function () { - let validated; let global = { imp: [], site: { @@ -179,15 +191,13 @@ describe('the first party data module', function () { adServerCurrency: 'USD' } }; - config.setConfig({'firstPartyData': {skipEnrichments: true}}); - - ({global: validated} = processFpd({global})); - expect(validated).to.deep.equal({}); + return processFpd({global}).then(({global: validated}) => { + expect(validated).to.deep.equal({}); + }); }); it('should add enrichments but not alter any arbitrary ortb2 data', function () { - let validated; let global = { site: { ext: { @@ -205,12 +215,12 @@ describe('the first party data module', function () { }, cur: ['USD'] }; - - ({global: validated} = processFpd({global})); - expect(validated.site.ref).to.equal(getRefererInfo().referer); - expect(validated.site.ext.data).to.deep.equal({inventory: ['value1']}); - expect(validated.user.ext.data).to.deep.equal({visitor: ['value2']}); - expect(validated.cur).to.deep.equal(['USD']); + return processFpd({global}).then(({global: validated}) => { + expect(validated.site.ref).to.equal(getRefererInfo().referer); + expect(validated.site.ext.data).to.deep.equal({inventory: ['value1']}); + expect(validated.user.ext.data).to.deep.equal({visitor: ['value2']}); + expect(validated.cur).to.deep.equal(['USD']); + }) }); it('should filter bidderConfig data', function () { @@ -230,12 +240,13 @@ describe('the first party data module', function () { } }; - const {bidder: validated} = processFpd({bidder}); - expect(validated.bidderA).to.not.be.undefined; - expect(validated.bidderA.user.data).to.be.undefined; - expect(validated.bidderA.user.keywords).to.equal('test'); - expect(validated.bidderA.site.keywords).to.equal('other'); - expect(validated.bidderA.site.ref).to.equal('https://domain.com'); + return processFpd({bidder}).then(({bidder: validated}) => { + expect(validated.bidderA).to.not.be.undefined; + expect(validated.bidderA.user.data).to.be.undefined; + expect(validated.bidderA.user.keywords).to.equal('test'); + expect(validated.bidderA.site.keywords).to.equal('other'); + expect(validated.bidderA.site.ref).to.equal('https://domain.com'); + }) }); it('should not filter bidderConfig data as it is valid', function () { @@ -255,17 +266,16 @@ describe('the first party data module', function () { } }; - const {bidder: validated} = processFpd({bidder}); - - expect(validated.bidderA).to.not.be.undefined; - expect(validated.bidderA.user.data).to.deep.equal([{segment: [{id: 'data1_id'}], name: 'data1'}]); - expect(validated.bidderA.user.keywords).to.equal('test'); - expect(validated.bidderA.site.keywords).to.equal('other'); - expect(validated.bidderA.site.ref).to.equal('https://domain.com'); + return processFpd({bidder}).then(({bidder: validated}) => { + expect(validated.bidderA).to.not.be.undefined; + expect(validated.bidderA.user.data).to.deep.equal([{segment: [{id: 'data1_id'}], name: 'data1'}]); + expect(validated.bidderA.user.keywords).to.equal('test'); + expect(validated.bidderA.site.keywords).to.equal('other'); + expect(validated.bidderA.site.ref).to.equal('https://domain.com'); + }); }); it('should not set default values if skipEnrichments is turned on', function () { - let validated; config.setConfig({'firstPartyData': {skipEnrichments: true}}); let global = { @@ -281,15 +291,15 @@ describe('the first party data module', function () { } }; - ({global: validated} = processFpd({global})); - expect(validated.device).to.be.undefined; - expect(validated.site.ref).to.be.undefined; - expect(validated.site.page).to.be.undefined; - expect(validated.site.domain).to.be.undefined; + return processFpd({global}).then(({global: validated}) => { + expect(validated.device).to.be.undefined; + expect(validated.site.ref).to.be.undefined; + expect(validated.site.page).to.be.undefined; + expect(validated.site.domain).to.be.undefined; + }); }); it('should not validate ortb2 data if skipValidations is turned on', function () { - let validated; config.setConfig({'firstPartyData': {skipValidations: true}}); let global = { @@ -304,8 +314,9 @@ describe('the first party data module', function () { } }; - ({global: validated} = processFpd({global})); - expect(validated.user.data).to.deep.equal([{segment: [{id: 'nonfiltered'}]}]); + return processFpd({global}).then(({global: validated}) => { + expect(validated.user.data).to.deep.equal([{segment: [{id: 'nonfiltered'}]}]); + }); }); }); }); diff --git a/test/spec/modules/topicsFpdModule_spec.js b/test/spec/modules/topicsFpdModule_spec.js new file mode 100644 index 00000000000..3781768497b --- /dev/null +++ b/test/spec/modules/topicsFpdModule_spec.js @@ -0,0 +1,239 @@ +import {getTopics, getTopicsData, processFpd} from '../../../modules/topicsFpdModule.js'; +import {deepClone} from '../../../src/utils.js'; + +describe('getTopicsData', () => { + function makeTopic(topic, modelv, taxv = '1') { + return { + topic, + taxonomyVersion: taxv, + modelVersion: modelv + } + } + + function byTaxClass(segments) { + return segments.reduce((memo, segment) => { + memo[`${segment.ext.segtax}:${segment.ext.segclass}`] = segment; + return memo; + }, {}) + } + + [ + { + t: 'no topics', + topics: [], + expected: [] + }, + { + t: 'single topic', + topics: [makeTopic(123, 'm1')], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '123'} + ] + } + ] + }, + { + t: 'multiple topics with the same model version', + topics: [makeTopic(123, 'm1'), makeTopic(321, 'm1')], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '123'}, + {id: '321'} + ] + } + ] + }, + { + t: 'multiple topics with different model versions', + topics: [makeTopic(1, 'm1'), makeTopic(2, 'm1'), makeTopic(3, 'm2')], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '1'}, + {id: '2'} + ] + }, + { + ext: { + segtax: 600, + segclass: 'm2' + }, + segment: [ + {id: '3'} + ] + } + ] + }, + { + t: 'multiple topics, some with a taxonomy version other than "1"', + topics: [makeTopic(123, 'm1'), makeTopic(321, 'm1', 'other')], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '123'} + ] + } + ] + }, + { + t: 'multiple topics in multiple taxonomies', + taxonomies: { + '1': 600, + '2': 601 + }, + topics: [ + makeTopic(123, 'm1', '1'), + makeTopic(321, 'm1', '2'), + makeTopic(213, 'm2', '1'), + ], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '123'} + ] + }, + { + ext: { + segtax: 601, + segclass: 'm1', + }, + segment: [ + {id: '321'} + ] + }, + { + ext: { + segtax: 600, + segclass: 'm2' + }, + segment: [ + {id: '213'} + ] + } + ] + } + ].forEach(({t, topics, expected, taxonomies}) => { + describe(`on ${t}`, () => { + it('should convert topics to user.data segments correctly', () => { + const actual = getTopicsData('mockName', topics, taxonomies); + expect(actual.length).to.eql(expected.length); + expected = byTaxClass(expected); + Object.entries(byTaxClass(actual)).forEach(([key, datum]) => { + sinon.assert.match(datum, expected[key]); + expect(datum.name).to.equal('mockName'); + }) + }); + + it('should not set name if null', () => { + getTopicsData(null, topics).forEach((data) => { + expect(data.hasOwnProperty('name')).to.be.false; + }) + }) + }) + }) +}); + +describe('getTopics', () => { + Object.entries({ + 'document with no browsingTopics': {}, + 'document that disallows topics': { + featurePolicy: { + allowsFeature: sinon.stub().returns(false) + } + }, + 'document that throws on featurePolicy': { + browsingTopics: sinon.stub(), + get featurePolicy() { + throw new Error() + } + }, + 'document that throws on browsingTopics': { + browsingTopics: sinon.stub().callsFake(() => { throw new Error(); }), + featurePolicy: { + allowsFeature: sinon.stub().returns(true) + } + }, + }).forEach(([t, doc]) => { + it(`should resolve to an empty list on ${t}`, () => { + return getTopics(doc).then((topics) => { + expect(topics).to.eql([]); + }); + }) + }); + + it('should call `document.browsingTopics` when allowed', () => { + const topics = ['t1', 't2'] + return getTopics({ + browsingTopics: sinon.stub().returns(Promise.resolve(topics)), + featurePolicy: { + allowsFeature: sinon.stub().returns(true) + } + }).then((actual) => { + expect(actual).to.eql(topics); + }) + }) +}) + +describe('processFpd', () => { + const mockData = [ + { + name: 'domain', + segment: [{id: 123}] + }, + { + name: 'domain', + segment: [{id: 321}] + } + ]; + + it('should add topics data', () => { + return processFpd({}, {global: {}}, {data: Promise.resolve(mockData)}) + .then(({global}) => { + expect(global.user.data).to.eql(mockData); + }); + }); + + it('should apppend to existing user.data', () => { + const global = { + user: { + data: [ + {name: 'preexisting'}, + ] + } + }; + return processFpd({}, {global: deepClone(global)}, {data: Promise.resolve(mockData)}) + .then((data) => { + expect(data.global.user.data).to.eql(global.user.data.concat(mockData)); + }); + }); + + it('should not modify fpd when there is no data', () => { + return processFpd({}, {global: {}}, {data: Promise.resolve([])}) + .then((data) => { + expect(data.global).to.eql({}); + }); + }); +}); From 45c33129674e3015ebfee45aa6f8b06410fab0c6 Mon Sep 17 00:00:00 2001 From: relaido <63339139+relaido@users.noreply.github.com> Date: Thu, 28 Jul 2022 22:08:06 +0900 Subject: [PATCH 009/246] Relaido Bid Adapter: Add params for hashed canonical url. (#8743) * add relaido adapter * remove event listener * fixed UserSyncs and e.data * fix conflicts * Add params for hashed canonical url. * Add params for hashed canonical url. removed review comment. Co-authored-by: ishigami_shingo Co-authored-by: cmertv-sishigami Co-authored-by: t_bun Co-authored-by: n.maeura --- modules/relaidoBidAdapter.js | 15 ++++++++++++--- test/spec/modules/relaidoBidAdapter_spec.js | 4 +++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/modules/relaidoBidAdapter.js b/modules/relaidoBidAdapter.js index 880e826d75b..c9e9fc00eb7 100644 --- a/modules/relaidoBidAdapter.js +++ b/modules/relaidoBidAdapter.js @@ -3,10 +3,11 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { Renderer } from '../src/Renderer.js'; import { getStorageManager } from '../src/storageManager.js'; +import sha1 from 'crypto-js/sha1'; const BIDDER_CODE = 'relaido'; const BIDDER_DOMAIN = 'api.relaido.jp'; -const ADAPTER_VERSION = '1.0.8'; +const ADAPTER_VERSION = '1.0.9'; const DEFAULT_TTL = 300; const UUID_KEY = 'relaido_uuid'; @@ -104,9 +105,9 @@ function buildRequests(validBidRequests, bidderRequest) { uuid: getUuid(), pv: '$prebid.version$', imuid: imuid, - // TODO: is 'page' the right value here? + canonical_url_hash: getCanonicalUrlHash(bidderRequest.refererInfo), ref: bidderRequest.refererInfo.page - }) + }); return { method: 'POST', @@ -277,6 +278,14 @@ function getUuid() { return newId; } +function getCanonicalUrlHash(refererInfo) { + const canonicalUrl = refererInfo.canonicalUrl || null; + if (!canonicalUrl) { + return null; + } + return sha1(canonicalUrl).toString(); +} + export function isMobile() { const ua = navigator.userAgent; if (ua.indexOf('iPhone') > -1 || ua.indexOf('iPod') > -1 || (ua.indexOf('Android') > -1 && ua.indexOf('Tablet') == -1)) { diff --git a/test/spec/modules/relaidoBidAdapter_spec.js b/test/spec/modules/relaidoBidAdapter_spec.js index ead44f96bc9..9aec1f179a4 100644 --- a/test/spec/modules/relaidoBidAdapter_spec.js +++ b/test/spec/modules/relaidoBidAdapter_spec.js @@ -51,7 +51,8 @@ describe('RelaidoAdapter', function () { bidderRequest = { timeout: 1000, refererInfo: { - page: 'https://publisher.com/home' + page: 'https://publisher.com/home?aaa=test1&bbb=test2', + canonicalUrl: 'https://publisher.com/home' } }; serverResponse = { @@ -238,6 +239,7 @@ describe('RelaidoAdapter', function () { const request = data.bids[0]; expect(bidRequests.method).to.equal('POST'); expect(bidRequests.url).to.equal('https://api.relaido.jp/bid/v1/sprebid'); + expect(data.canonical_url_hash).to.equal('e6092f44a0044903ae3764126eedd6187c1d9f04'); expect(data.ref).to.equal(bidderRequest.refererInfo.page); expect(data.timeout_ms).to.equal(bidderRequest.timeout); expect(request.ad_unit_code).to.equal(bidRequest.adUnitCode); From 93fc10b6deb5c87dc5f7880baa2c3971ed79024e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Jul 2022 11:41:30 -0400 Subject: [PATCH 010/246] Bump terser from 5.12.0 to 5.14.2 (#8719) Bumps [terser](https://github.com/terser/terser) from 5.12.0 to 5.14.2. - [Release notes](https://github.com/terser/terser/releases) - [Changelog](https://github.com/terser/terser/blob/master/CHANGELOG.md) - [Commits](https://github.com/terser/terser/commits) --- updated-dependencies: - dependency-name: terser dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 103 +++++++++++++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index ccf4d92a711..ef3ab0394fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1922,6 +1922,20 @@ "node": ">=8" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", @@ -1930,15 +1944,34 @@ "node": ">=6.0.0" } }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.11", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -21266,14 +21299,14 @@ } }, "node_modules/terser": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.0.tgz", - "integrity": "sha512-R3AUhNBGWiFc77HXag+1fXpAxTAFRQTJemlJKjAgD9r8xXTpjNKqIXwHM/o7Rh+O0kUJtS3WQVdBeMKFk5sw9A==", + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", "dev": true, "dependencies": { + "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", "commander": "^2.20.0", - "source-map": "~0.7.2", "source-map-support": "~0.5.20" }, "bin": { @@ -21387,15 +21420,6 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "node_modules/terser/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/text-extensions": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", @@ -24729,20 +24753,47 @@ } } }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, "@jridgewell/resolve-uri": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==" }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, "@jridgewell/sourcemap-codec": { "version": "1.4.11", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==" }, "@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", "requires": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -40160,14 +40211,14 @@ } }, "terser": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.0.tgz", - "integrity": "sha512-R3AUhNBGWiFc77HXag+1fXpAxTAFRQTJemlJKjAgD9r8xXTpjNKqIXwHM/o7Rh+O0kUJtS3WQVdBeMKFk5sw9A==", + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", "dev": true, "requires": { + "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", "commander": "^2.20.0", - "source-map": "~0.7.2", "source-map-support": "~0.5.20" }, "dependencies": { @@ -40182,12 +40233,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true } } }, From 7e203b39f0395ee35b37de1510052cfe59bd7bdb Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 28 Jul 2022 10:37:38 -0700 Subject: [PATCH 011/246] Prebid core: fix native trackers for Prebid Server; simplify native ORTB logic (#8748) * Prebid core: simplify native ORTB logic * Remove nativeMapper; do not mix ORTB request and response * fix lint * set bidRequest.nativeParams after ortb -> legacy conversion * Fix native trackers for native ortb responses (including Prebid Server) * remove redundant native request generation from PBS adapter * use includes instead of in --- modules/prebidServerBidAdapter/index.js | 118 +-------- src/adapterManager.js | 1 + src/native.js | 235 ++++++++++-------- .../modules/prebidServerBidAdapter_spec.js | 99 ++++---- test/spec/native_spec.js | 145 ++++++++++- test/spec/unit/core/bidderFactory_spec.js | 3 + 6 files changed, 336 insertions(+), 265 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 9c240615c04..9c7c3c921b0 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -2,7 +2,6 @@ import Adapter from '../../src/adapter.js'; import {createBid} from '../../src/bidfactory.js'; import { bind, - cleanObj, createTrackPixelHtml, deepAccess, deepClone, @@ -30,17 +29,16 @@ import { } from '../../src/utils.js'; import CONSTANTS from '../../src/constants.json'; import adapterManager from '../../src/adapterManager.js'; -import { config } from '../../src/config.js'; -import { VIDEO, NATIVE } from '../../src/mediaTypes.js'; -import { isValid } from '../../src/adapters/bidderFactory.js'; +import {config} from '../../src/config.js'; +import {NATIVE, VIDEO} from '../../src/mediaTypes.js'; +import {isValid} from '../../src/adapters/bidderFactory.js'; import * as events from '../../src/events.js'; import {find, includes} from '../../src/polyfill.js'; -import { S2S_VENDORS } from './config.js'; -import { ajax } from '../../src/ajax.js'; +import {S2S_VENDORS} from './config.js'; +import {ajax} from '../../src/ajax.js'; import {hook} from '../../src/hook.js'; import {getGlobal} from '../../src/prebidGlobal.js'; import {hasPurpose1Consent} from '../../src/utils/gpdr.js'; -import { nativeMapper } from '../../src/native.js'; const getConfig = config.getConfig; @@ -446,11 +444,6 @@ if (FEATURES.NATIVE) { }); }); } -/* - * Protocol spec for OpenRTB endpoint - * e.g., https:///v1/openrtb2/auction - */ -let nativeAssetCache = {}; // store processed native params to preserve /** * map wurl to auction id and adId for use in the BID_WON event @@ -552,78 +545,6 @@ Object.assign(ORTB2.prototype, { impIds.add(impressionId); this.adUnitsByImp[impressionId] = adUnit; - const nativeParams = adUnit.nativeParams; - let nativeAssets = nativeAssetCache[impressionId] = deepAccess(nativeParams, 'ortb.assets'); - if (FEATURES.NATIVE && nativeParams && !nativeAssets) { - let idCounter = -1; - try { - nativeAssets = nativeAssetCache[impressionId] = Object.keys(nativeParams).reduce((assets, type) => { - let params = nativeParams[type]; - - function newAsset(obj) { - idCounter++; - return Object.assign({ - required: params.required ? 1 : 0, - id: (isNumber(params.id)) ? idCounter = params.id : idCounter - }, obj ? cleanObj(obj) : {}); - } - - switch (type) { - case 'image': - case 'icon': - let imgTypeId = nativeImgIdMap[type]; - let asset = cleanObj({ - type: imgTypeId, - w: deepAccess(params, 'sizes.0'), - h: deepAccess(params, 'sizes.1'), - wmin: deepAccess(params, 'aspect_ratios.0.min_width'), - hmin: deepAccess(params, 'aspect_ratios.0.min_height') - }); - if (!((asset.w && asset.h) || (asset.hmin && asset.wmin))) { - throw 'invalid img sizes (must provide sizes or min_height & min_width if using aspect_ratios)'; - } - if (Array.isArray(params.aspect_ratios)) { - // pass aspect_ratios as ext data I guess? - const aspectRatios = params.aspect_ratios - .filter((ar) => ar.ratio_width && ar.ratio_height) - .map(ratio => `${ratio.ratio_width}:${ratio.ratio_height}`); - if (aspectRatios.length > 0) { - asset.ext = { - aspectratios: aspectRatios - } - } - } - assets.push(newAsset({ - img: asset - })); - break; - case 'title': - if (!params.len) { - throw 'invalid title.len'; - } - assets.push(newAsset({ - title: { - len: params.len - } - })); - break; - default: - let dataAssetTypeId = nativeDataIdMap[type]; - if (dataAssetTypeId) { - assets.push(newAsset({ - data: { - type: dataAssetTypeId, - len: params.len - } - })) - } - } - return assets; - }, []); - } catch (e) { - logError('error creating native request: ' + String(e)) - } - } const videoParams = deepAccess(adUnit, 'mediaTypes.video'); const bannerParams = deepAccess(adUnit, 'mediaTypes.banner'); @@ -678,8 +599,8 @@ Object.assign(ORTB2.prototype, { }, {}); } } - - if (FEATURES.NATIVE && nativeAssets) { + const nativeReq = deepAccess(adUnit, 'nativeOrtbRequest') + if (FEATURES.NATIVE && nativeReq) { const defaultRequest = { // TODO: determine best way to pass these and if we allow defaults context: 1, @@ -689,18 +610,13 @@ Object.assign(ORTB2.prototype, { ], // TODO: figure out how to support privacy field // privacy: int - assets: nativeAssets }; - const ortbRequest = deepAccess(nativeParams, 'ortb'); try { - const request = ortbRequest ? Object.assign(defaultRequest, ortbRequest) : defaultRequest; + const request = Object.assign(defaultRequest, nativeReq); mediaTypes[NATIVE] = { request: JSON.stringify(request), ver: '1.2' }; - // saving the converted ortb native request into the native mapper, so the Universal Creative - // can render the native ad directly. - adUnit.bids.forEach(bid => nativeMapper.set(bid.bid_id, request)); } catch (e) { logError('error creating native request: ' + String(e)); } @@ -1091,24 +1007,6 @@ Object.assign(ORTB2.prototype, { ortb = bidObject.adm = bid.adm; } - // ortb.imptrackers and ortb.jstracker are going to be deprecated. So, when we find - // those properties, we're creating the equivalent eventtrackers and let prebid universal - // creative deal with it - for (const imptracker of ortb.imptrackers || []) { - ortb.eventtrackers.push({ - event: nativeEventTrackerEventMap.impression, - method: nativeEventTrackerMethodMap.img, - url: imptracker - }) - } - if (ortb.jstracker) { - ortb.eventtrackers.push({ - event: nativeEventTrackerEventMap.impression, - method: nativeEventTrackerMethodMap.js, - url: ortb.jstracker - }) - } - if (isPlainObject(ortb) && Array.isArray(ortb.assets)) { bidObject.native = { ortb, diff --git a/src/adapterManager.js b/src/adapterManager.js index a707cc473d6..03f3848e81b 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -66,6 +66,7 @@ function getBids({bidderCode, auctionId, bidderRequestId, adUnits, src}) { .reduce((bids, bid) => { bid = Object.assign({}, bid, getDefinedParams(adUnit, [ 'nativeParams', + 'nativeOrtbRequest', 'ortb2Imp', 'mediaType', 'renderer' diff --git a/src/native.js b/src/native.js index 11fb0b54b90..4ba4505d441 100644 --- a/src/native.js +++ b/src/native.js @@ -1,9 +1,21 @@ -import { deepAccess, getKeyByValue, insertHtmlIntoIframe, isInteger, isNumber, isPlainObject, logError, triggerPixel, isBoolean, isArray, deepClone } from './utils.js'; +import { + deepAccess, + deepClone, + getKeyByValue, + insertHtmlIntoIframe, + isArray, + isBoolean, + isInteger, + isNumber, + isPlainObject, + logError, + triggerPixel, + pick +} from './utils.js'; import {includes} from './polyfill.js'; import {auctionManager} from './auctionManager.js'; import CONSTANTS from './constants.json'; -import { NATIVE } from './mediaTypes.js'; -import { filters } from './targeting.js'; +import {NATIVE} from './mediaTypes.js'; export const nativeAdapters = []; @@ -74,7 +86,20 @@ const { NATIVE_ASSET_TYPES, NATIVE_IMAGE_TYPES, PREBID_NATIVE_DATA_KEYS_TO_ORTB, const PREBID_NATIVE_DATA_KEYS_TO_ORTB_INVERSE = inverse(PREBID_NATIVE_DATA_KEYS_TO_ORTB); const NATIVE_ASSET_TYPES_INVERSE = inverse(NATIVE_ASSET_TYPES); -export const nativeMapper = new Map(); +const TRACKER_METHODS = { + img: 1, + js: 2, + 1: 'img', + 2: 'js' +} + +const TRACKER_EVENTS = { + impression: 1, + 'viewable-mrc50': 2, + 'viewable-mrc100': 3, + 'viewable-video50': 4, +} + /** * Recieves nativeParams from an adUnit. If the params were not of type 'type', * passes them on directly. If they were of type 'type', translate @@ -98,6 +123,9 @@ export function decorateAdUnitsWithNativeParams(adUnits) { if (nativeParams) { adUnit.nativeParams = processNativeAdUnitParams(nativeParams); } + if (adUnit.nativeParams) { + adUnit.nativeOrtbRequest = adUnit.nativeParams.ortb || toOrtbNativeRequest(adUnit.nativeParams); + } }); } export function isOpenRTBBidRequestValid(ortb) { @@ -192,10 +220,8 @@ export const hasNonNativeBidder = adUnit => export function nativeBidIsValid(bid, {index = auctionManager.index} = {}) { const adUnit = index.getAdUnit(bid); if (!adUnit) { return false; } - let ortbRequest = - adUnit?.nativeParams?.ortb || nativeMapper.get(bid.requestId) || toOrtbNativeRequest(adUnit.nativeParams); - let ortbResponse = - bid.native?.ortb || toOrtbNativeResponse(bid.native, ortbRequest); + let ortbRequest = adUnit.nativeOrtbRequest + let ortbResponse = bid.native?.ortb || toOrtbNativeResponse(bid.native, ortbRequest); return isNativeOpenRTBBidValid(ortbResponse, ortbRequest); } @@ -242,20 +268,45 @@ export function isNativeOpenRTBBidValid(bidORTB, bidRequestORTB) { * fireTrackers(); // fires impressions when creative is loaded * */ -export function fireNativeTrackers(message, adObject) { - let trackers; +export function fireNativeTrackers(message, bidResponse) { + const nativeResponse = bidResponse.native.ortb || legacyPropertiesToOrtbNative(bidResponse.native); + if (message.action === 'click') { - trackers = adObject['native'] && adObject['native'].clickTrackers; + fireClickTrackers(nativeResponse); } else { - trackers = adObject['native'] && adObject['native'].impressionTrackers; + fireImpressionTrackers(nativeResponse); + } + return message.action; +} - if (adObject['native'] && adObject['native'].javascriptTrackers) { - insertHtmlIntoIframe(adObject['native'].javascriptTrackers); +export function fireImpressionTrackers(nativeResponse, {runMarkup = (mkup) => insertHtmlIntoIframe(mkup), fetchURL = triggerPixel} = {}) { + const impTrackers = (nativeResponse.eventtrackers || []) + .filter(tracker => tracker.event === TRACKER_EVENTS.impression); + + let {img, js} = impTrackers.reduce((tally, tracker) => { + if (TRACKER_METHODS.hasOwnProperty(tracker.method)) { + tally[TRACKER_METHODS[tracker.method]].push(tracker.url) } + return tally; + }, {img: [], js: []}); + + if (nativeResponse.imptrackers) { + img = img.concat(nativeResponse.imptrackers); } + img.forEach(url => fetchURL(url)); - (trackers || []).forEach(triggerPixel); - return message.action; + js = js.map(url => ``); + if (nativeResponse.jstracker) { + // jstracker is already HTML markup + js = js.concat([nativeResponse.jstracker]); + } + if (js.length) { + runMarkup(js.join('\n')); + } +} + +export function fireClickTrackers(nativeResponse, {fetchURL = triggerPixel} = {}) { + (nativeResponse.link?.clicktrackers || []).forEach(url => fetchURL(url)); } /** @@ -342,16 +393,15 @@ export function getAssetMessage(data, adObject) { return message; } -export function getAllAssetsMessage(data, adObject) { +export function getAllAssetsMessage(data, adObject, {getNativeReq = (bidResponse) => auctionManager.index.getAdUnit(bidResponse).nativeOrtbRequest} = {}) { const message = { message: 'assetResponse', adId: data.adId, }; // Pass to Prebid Universal Creative all assets, the legacy ones + the ortb ones (under ortb property) - const ortbRequest = nativeMapper.get(adObject.requestId); + const ortbRequest = getNativeReq(adObject); let nativeReq = adObject.native; - nativeMapper.delete(adObject.requestId); const ortbResponse = adObject.native?.ortb; let legacyResponse = {}; if (ortbRequest && ortbResponse) { @@ -384,7 +434,6 @@ export function getAllAssetsMessage(data, adObject) { message.assets.push({ key, value }); } }); - removeExpiredBidsFromNativeMapper(); return message; } @@ -431,7 +480,7 @@ export function toOrtbNativeRequest(legacyNativeAssets) { }; for (let key in legacyNativeAssets) { // skip conversion for non-asset keys - if (key in NATIVE_KEYS_THAT_ARE_NOT_ASSETS) continue; + if (NATIVE_KEYS_THAT_ARE_NOT_ASSETS.includes(key)) continue; const asset = legacyNativeAssets[key]; let required = 0; @@ -459,7 +508,7 @@ export function toOrtbNativeRequest(legacyNativeAssets) { if (asset.aspect_ratios) { if (!isArray(asset.aspect_ratios)) { logError("image.aspect_ratios was passed, but it's not a an array:", asset.aspect_ratios); - } else if (asset.aspect_ratios.length != 1) { + } else if (!asset.aspect_ratios.length) { logError("image.aspect_ratios was passed, but it's empty:", asset.aspect_ratios); } else { const { min_width: minWidth, min_height: minHeight } = asset.aspect_ratios[0]; @@ -469,12 +518,20 @@ export function toOrtbNativeRequest(legacyNativeAssets) { ortbAsset.img.wmin = minWidth; ortbAsset.img.hmin = minHeight; } + const aspectRatios = asset.aspect_ratios + .filter((ar) => ar.ratio_width && ar.ratio_height) + .map(ratio => `${ratio.ratio_width}:${ratio.ratio_height}`); + if (aspectRatios.length > 0) { + ortbAsset.img.ext = { + aspectratios: aspectRatios + } + } } } // if asset.sizes exist, by OpenRTB spec we should remove wmin and hmin if (asset.sizes) { - if (asset.sizes.length != 2 || !isInteger(asset.sizes[0]) || !isInteger(asset.sizes[1])) { + if (asset.sizes.length !== 2 || !isInteger(asset.sizes[0]) || !isInteger(asset.sizes[1])) { logError('image.sizes was passed, but its value is not an array of integers:', asset.sizes); } else { ortbAsset.img.w = asset.sizes[0]; @@ -492,9 +549,7 @@ export function toOrtbNativeRequest(legacyNativeAssets) { } // all extensions to the native bid request are passed as is } else if (key === 'ext') { - ortbAsset.ext = { - asset - }; + ortbAsset.ext = asset; // in `ext` case, required field is not needed delete ortbAsset.required; } @@ -572,30 +627,21 @@ export function fromOrtbNativeRequest(openRTBRequest) { * @returns an array of valid bid requests where the openRTB bids are converted to proprietary format. */ export function convertOrtbRequestToProprietaryNative(bidRequests) { - let needsToBeCopied = false; - if (!bidRequests || !isArray(bidRequests)) return bidRequests; if (FEATURES.NATIVE) { + if (!bidRequests || !isArray(bidRequests)) return bidRequests; // check if a conversion is needed - needsToBeCopied = bidRequests.some(bidRequest => bidRequest.mediaTypes && bidRequest.mediaTypes[NATIVE] && bidRequest.mediaTypes[NATIVE].ortb); - if (!needsToBeCopied) return bidRequests; + if (!bidRequests.some(bidRequest => (bidRequest?.mediaTypes || {})[NATIVE]?.ortb)) { + return bidRequests; + } let bidRequestsCopy = deepClone(bidRequests); // convert Native ORTB definition to old-style prebid native definition for (const bidRequest of bidRequestsCopy) { if (bidRequest.mediaTypes && bidRequest.mediaTypes[NATIVE] && bidRequest.mediaTypes[NATIVE].ortb) { - bidRequest.mediaTypes[NATIVE] = { - // to keep other keywords like sendTargetingKeys, rendererUrl... - ...Object.keys(bidRequest.mediaTypes[NATIVE]) - .filter(key => NATIVE_KEYS_THAT_ARE_NOT_ASSETS.includes(key)) - .reduce((obj, key) => ({ - ...obj, - [key]: bidRequest.mediaTypes[NATIVE][key] - }), {}), - ...fromOrtbNativeRequest(bidRequest.mediaTypes[NATIVE].ortb) - } - bidRequest.nativeParams = bidRequest.mediaTypes[NATIVE]; - if (bidRequest.nativeParams) { - processNativeAdUnitParams(bidRequest.nativeParams); - } + bidRequest.mediaTypes[NATIVE] = Object.assign( + pick(bidRequest.mediaTypes[NATIVE], NATIVE_KEYS_THAT_ARE_NOT_ASSETS), + fromOrtbNativeRequest(bidRequest.mediaTypes[NATIVE].ortb) + ); + bidRequest.nativeParams = processNativeAdUnitParams(bidRequest.mediaTypes[NATIVE]); } } return bidRequestsCopy; @@ -603,11 +649,52 @@ export function convertOrtbRequestToProprietaryNative(bidRequests) { return bidRequests; } -export function toOrtbNativeResponse(legacyResponse, ortbRequest) { - const ortbResponse = { +/** + * convert PBJS proprietary native properties that are *not* assets to the ORTB native format. + * + * @param legacyNative `bidResponse.native` object as returned by adapters + */ +export function legacyPropertiesToOrtbNative(legacyNative) { + const response = { link: {}, - assets: [], eventtrackers: [] + } + Object.entries(legacyNative).forEach(([key, value]) => { + switch (key) { + case 'clickUrl': + response.link.url = value; + break; + case 'clickTrackers': + response.link.clicktrackers = Array.isArray(value) ? value : [value]; + break; + case 'impressionTrackers': + (Array.isArray(value) ? value : [value]).forEach(url => { + response.eventtrackers.push({ + event: TRACKER_EVENTS.impression, + method: TRACKER_METHODS.img, + url + }); + }); + break; + case 'javascriptTrackers': + // jstracker is deprecated, but we need to use it here since 'javascriptTrackers' is markup, not an url + // TODO: at the time of writing this, core expected javascriptTrackers to be a string (despite the name), + // but many adapters are passing an array. It's possible that some of them are, in fact, passing URLs and not markup + // in general, native trackers seem to be neglected and/or broken + response.jstracker = Array.isArray(value) ? value.join('') : value; + break; + } + }) + return response; +} + +export function toOrtbNativeResponse(legacyResponse, ortbRequest) { + // copy the request, so we don't pollute it with response data below + ortbRequest = deepClone(ortbRequest); + + const ortbResponse = { + ...legacyPropertiesToOrtbNative(legacyResponse), + assets: [] }; Object.keys(legacyResponse).filter(key => !!legacyResponse[key]).forEach(key => { const value = legacyResponse[key]; @@ -629,20 +716,6 @@ export function toOrtbNativeResponse(legacyResponse, ortbRequest) { }; ortbResponse.assets.push(imageAsset); break; - case 'clickUrl': - ortbResponse.link.url = value; - break; - case 'clickTrackers': - ortbResponse.link.clicktrackers = value; - break; - case 'impressionTrackers': - case 'javascriptTrackers': - ortbResponse.eventtrackers.push({ - event: 1, - method: key == 'impressionTrackers' ? 1 : 2, - url: value, - }); - break; default: if (key in PREBID_NATIVE_DATA_KEYS_TO_ORTB) { const dataAsset = ortbRequest.assets.find(asset => asset.data != null && asset.data.type === NATIVE_ASSET_TYPES[PREBID_NATIVE_DATA_KEYS_TO_ORTB[key]]); @@ -680,38 +753,6 @@ function toLegacyResponse(ortbResponse, ortbRequest) { return legacyResponse; } -/** - * Converts a Legacy native request to OpenRTB. - * The proprietary Prebid format has many limitations and will be dropped in - * the future; adapters are encouraged to stop using it in favour of OpenRTB format. - * @param {BidRequest[]} bidRequests an array of valid bid requests - * @returns an array of valid bid requests where the legacy format is converted to OpenRTB. - */ -export function convertLegacyNativeRequestToOrtb(bidRequests) { - if (!bidRequests || !isArray(bidRequests)) return bidRequests; - // convert Native ORTB definition to old-style prebid native definition - for (const bidRequest of bidRequests) { - if (bidRequest.mediaTypes && bidRequest.mediaTypes[NATIVE]) { - if (bidRequest.mediaTypes[NATIVE].ortb) continue; - // legacy case - const ortbRequest = toOrtbNativeRequest(bidRequest.mediaTypes[NATIVE]); - bidRequest.mediaTypes[NATIVE] = { - ...Object.entries(bidRequest.mediaTypes['native']) - .filter(([key, value]) => NATIVE_KEYS_THAT_ARE_NOT_ASSETS.includes(key)) - .reduce((acc, curr) => { acc[curr[0]] = curr[1]; return acc }, {}), - ortb: ortbRequest - } - nativeMapper.set(bidRequest.bidId, ortbRequest); - // to keep other keywords like sendTargetingKeys, rendererUrl... - bidRequest.nativeParams = bidRequest.mediaTypes[NATIVE]; - if (bidRequest.nativeParams) { - processNativeAdUnitParams(bidRequest.nativeParams); - } - } - } - return bidRequests; -} - /** * Inverts key-values of an object. */ @@ -722,11 +763,3 @@ function inverse(obj) { } return retobj; } - -// to avoid memory leaks, this function will try to remove expired bids from the native wrapper. -function removeExpiredBidsFromNativeMapper() { - const expiredBids = auctionManager.getBidsReceived().filter((bid) => !filters.isBidNotExpired(bid)); - for (const expiredBid of expiredBids) { - nativeMapper.delete(expiredBid.requestId); - } -} diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 0495c7000d1..ff600d4ef93 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1316,55 +1316,54 @@ describe('S2S Adapter', function () { config.setConfig(_config); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); - - expect(requestBid.imp[0].native).to.deep.equal({ - request: JSON.stringify({ - 'context': 1, - 'plcmttype': 1, - 'eventtrackers': [{ - event: 1, - methods: [1] - }], - 'assets': [ - { - 'required': 1, - 'id': 0, - 'title': { - 'len': 800 - } - }, - { - 'required': 1, - 'id': 1, - 'img': { - 'type': 3, - 'w': 989, - 'h': 742 - } - }, - { - 'required': 1, - 'id': 2, - 'img': { - 'type': 1, - 'wmin': 10, - 'hmin': 10, - 'ext': { - 'aspectratios': ['1:1'] - } - } - }, - { - 'required': 1, - 'id': 3, - 'data': { - 'type': 1 + const ortbReq = JSON.parse(requestBid.imp[0].native.request); + expect(ortbReq).to.deep.equal({ + 'ver': '1.2', + 'context': 1, + 'plcmttype': 1, + 'eventtrackers': [{ + event: 1, + methods: [1] + }], + 'assets': [ + { + 'required': 1, + 'id': 0, + 'title': { + 'len': 800 + } + }, + { + 'required': 1, + 'id': 1, + 'img': { + 'type': 3, + 'w': 989, + 'h': 742 + } + }, + { + 'required': 1, + 'id': 2, + 'img': { + 'type': 1, + 'wmin': 10, + 'hmin': 10, + 'ext': { + 'aspectratios': ['1:1'] } } - ] - }), - ver: '1.2' + }, + { + 'required': 1, + 'id': 3, + 'data': { + 'type': 1 + } + } + ] }); + expect(requestBid.imp[0].native.ver).to.equal('1.2'); }); it('adds native ortb request for OpenRTB', function () { @@ -1382,11 +1381,9 @@ describe('S2S Adapter', function () { config.setConfig(_config); adapter.callBids(openRtbNativeRequest, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); - - expect(requestBid.imp[0].native).to.deep.equal({ - request: JSON.stringify(NATIVE_ORTB_MTO.ortb), - ver: '1.2' - }); + const nativeReq = JSON.parse(requestBid.imp[0].native.request); + expect(nativeReq).to.deep.equal(NATIVE_ORTB_MTO.ortb); + expect(requestBid.imp[0].native.ver).to.equal('1.2'); }); it('should not include ext.aspectratios if adunit\'s aspect_ratios do not define radio_width and ratio_height', () => { diff --git a/test/spec/native_spec.js b/test/spec/native_spec.js index ce69c299697..a6f8e7ae891 100644 --- a/test/spec/native_spec.js +++ b/test/spec/native_spec.js @@ -8,7 +8,7 @@ import { decorateAdUnitsWithNativeParams, isOpenRTBBidRequestValid, isNativeOpenRTBBidValid, - toOrtbNativeRequest, + toOrtbNativeRequest, toOrtbNativeResponse, legacyPropertiesToOrtbNative, fireImpressionTrackers, fireClickTrackers, } from 'src/native.js'; import CONSTANTS from 'src/constants.json'; import { stubAuctionIndex } from '../helpers/indexStub.js'; @@ -332,7 +332,7 @@ describe('native.js', function () { adId: '123', }; - const message = getAllAssetsMessage(messageRequest, bid); + const message = getAllAssetsMessage(messageRequest, bid, {getNativeReq: () => null}); expect(message.assets.length).to.equal(9); expect(message.assets).to.deep.include({ @@ -380,7 +380,7 @@ describe('native.js', function () { adId: '123', }; - const message = getAllAssetsMessage(messageRequest, bidWithUndefinedFields); + const message = getAllAssetsMessage(messageRequest, bidWithUndefinedFields, {getNativeReq: () => null}); expect(message.assets.length).to.equal(4); expect(message.assets).to.deep.include({ @@ -883,3 +883,142 @@ describe('validate native', function () { }); } }); + +describe('legacyPropertiesToOrtbNative', () => { + describe('click trakckers', () => { + it('should convert clickUrl to link.url', () => { + const native = legacyPropertiesToOrtbNative({clickUrl: 'some-url'}); + expect(native.link.url).to.eql('some-url'); + }); + it('should convert single clickTrackers to link.clicktrackers', () => { + const native = legacyPropertiesToOrtbNative({clickTrackers: 'some-url'}); + expect(native.link.clicktrackers).to.eql([ + 'some-url' + ]) + }); + it('should convert multiple clickTrackers into link.clicktrackers', () => { + const native = legacyPropertiesToOrtbNative({clickTrackers: ['url1', 'url2']}); + expect(native.link.clicktrackers).to.eql([ + 'url1', + 'url2' + ]) + }) + }); + describe('impressionTrackers', () => { + it('should convert a single tracker into an eventtracker entry', () => { + const native = legacyPropertiesToOrtbNative({impressionTrackers: 'some-url'}); + expect(native.eventtrackers).to.eql([ + { + event: 1, + method: 1, + url: 'some-url' + } + ]); + }); + + it('should convert an array into corresponding eventtracker entries', () => { + const native = legacyPropertiesToOrtbNative({impressionTrackers: ['url1', 'url2']}); + expect(native.eventtrackers).to.eql([ + { + event: 1, + method: 1, + url: 'url1' + }, + { + event: 1, + method: 1, + url: 'url2' + } + ]) + }) + }); + describe('javascriptTrackers', () => { + it('should convert a single value into jstracker', () => { + const native = legacyPropertiesToOrtbNative({javascriptTrackers: 'some-markup'}); + expect(native.jstracker).to.eql('some-markup'); + }) + it('should merge multiple values into a single jstracker', () => { + const native = legacyPropertiesToOrtbNative({javascriptTrackers: ['some-markup', 'some-other-markup']}); + expect(native.jstracker).to.eql('some-markupsome-other-markup'); + }) + }); +}); + +describe('fireImpressionTrackers', () => { + let runMarkup, fetchURL; + beforeEach(() => { + runMarkup = sinon.stub(); + fetchURL = sinon.stub(); + }) + + function runTrackers(resp) { + fireImpressionTrackers(resp, {runMarkup, fetchURL}) + } + + it('should run markup in jstracker', () => { + runTrackers({ + jstracker: 'some-markup' + }); + sinon.assert.calledWith(runMarkup, 'some-markup'); + }); + + it('should fetch each url in imptrackers', () => { + const urls = ['url1', 'url2']; + runTrackers({ + imptrackers: urls + }); + urls.forEach(url => sinon.assert.calledWith(fetchURL, url)); + }); + + it('should fetch each url in eventtrackers that use the image method', () => { + const urls = ['url1', 'url2']; + runTrackers({ + eventtrackers: urls.map(url => ({event: 1, method: 1, url})) + }); + urls.forEach(url => sinon.assert.calledWith(fetchURL, url)) + }); + + it('should load as a script each url in eventtrackers that use the js method', () => { + const urls = ['url1', 'url2']; + runTrackers({ + eventtrackers: urls.map(url => ({event: 1, method: 2, url})) + }); + urls.forEach(url => sinon.assert.calledWith(runMarkup, sinon.match(`script async src="${url}"`))) + }); + + it('should not fire trackers that are not impression trakcers', () => { + runTrackers({ + link: { + clicktrackers: ['click-url'] + }, + eventtrackers: [{ + event: 2, // not imp + method: 1, + url: 'some-url' + }] + }); + sinon.assert.notCalled(fetchURL); + sinon.assert.notCalled(runMarkup); + }) +}) + +describe('fireClickTrackers', () => { + let fetchURL; + beforeEach(() => { + fetchURL = sinon.stub(); + }); + + function runTrackers(resp) { + fireClickTrackers(resp, {fetchURL}); + } + + it('should load each URL in link.clicktrackers', () => { + const urls = ['url1', 'url2']; + runTrackers({ + link: { + clicktrackers: urls + } + }); + urls.forEach(url => sinon.assert.calledWith(fetchURL, url)); + }) +}) diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index 646791c7e1f..c040bba4eec 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -12,6 +12,7 @@ import {hook} from '../../../../src/hook.js'; import {auctionManager} from '../../../../src/auctionManager.js'; import {stubAuctionIndex} from '../../../helpers/indexStub.js'; import { bidderSettings } from '../../../../src/bidderSettings.js'; +import {decorateAdUnitsWithNativeParams} from '../../../../src/native.js'; const CODE = 'sampleBidder'; const MOCK_BIDS_REQUEST = { @@ -882,6 +883,7 @@ describe('validate bid response: ', function () { title: {'required': true}, } }] + decorateAdUnitsWithNativeParams(adUnits); let bidRequest = { bids: [{ bidId: '1', @@ -923,6 +925,7 @@ describe('validate bid response: ', function () { title: {'required': true}, }, }]; + decorateAdUnitsWithNativeParams(adUnits); let bidRequest = { bids: [{ bidId: '1', From d0a729b04237d8106b6d1afb400c2274fe0c9ade Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 28 Jul 2022 17:49:13 +0000 Subject: [PATCH 012/246] Prebid 7.8.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index ef3ab0394fb..bbd66c24b7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.8.0-pre", + "version": "7.8.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 9a2e223fe9d..4d737ec389c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.8.0-pre", + "version": "7.8.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From ef86ab1d6a1bbc2fdbfc2dcccffef74d1349768f Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 28 Jul 2022 17:49:14 +0000 Subject: [PATCH 013/246] Increment version to 7.9.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index bbd66c24b7f..c29ddad3673 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.8.0", + "version": "7.9.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 4d737ec389c..3c6622adf9b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.8.0", + "version": "7.9.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 82d74d2f57ceac5511fdcfd8d35a433f07d3a2ca Mon Sep 17 00:00:00 2001 From: nllerandi3lift <75995508+nllerandi3lift@users.noreply.github.com> Date: Thu, 28 Jul 2022 18:44:10 -0400 Subject: [PATCH 014/246] Triplelift Bid Adapter: outstream support (#8709) * alt outstream support * removes consoleLog * rename video ttl * multi-imp * simplify video if statement in buildPostBody * distinguish banner from video bid; temp solution for testing * adds tests for mediatype * remove console log * individual instream/outstream tests * simplify some functions * nitpick * adds media_type check * adds placement options to outstream * checks instream placement values * TL-19850 Finished log error logic around floors functionality * deprecates getlegacyFpd * remove console log * TL-19850 Changed functionlity of tests * restore logErrorSpy aftereach * removing pub name from test Co-authored-by: Dan Goldin Co-authored-by: Patrick Loughrey Co-authored-by: Patrick Loughrey --- modules/tripleliftBidAdapter.js | 64 ++- .../spec/modules/tripleliftBidAdapter_spec.js | 388 +++++++++++++++++- 2 files changed, 423 insertions(+), 29 deletions(-) diff --git a/modules/tripleliftBidAdapter.js b/modules/tripleliftBidAdapter.js index df4f9a9ba38..a5a93024ff9 100644 --- a/modules/tripleliftBidAdapter.js +++ b/modules/tripleliftBidAdapter.js @@ -8,7 +8,7 @@ const GVLID = 28; const BIDDER_CODE = 'triplelift'; const STR_ENDPOINT = 'https://tlx.3lift.com/header/auction?'; const BANNER_TIME_TO_LIVE = 300; -const INSTREAM_TIME_TO_LIVE = 3600; +const VIDEO_TIME_TO_LIVE = 3600; let gdprApplies = true; let consentString = null; export const storage = getStorageManager({gvlid: GVLID, bidderCode: BIDDER_CODE}); @@ -120,12 +120,15 @@ function _buildPostBody(bidRequests, bidderRequest) { tagid: bidRequest.params.inventoryCode, floor: _getFloor(bidRequest) }; - // remove the else to support multi-imp - if (_isInstreamBidRequest(bidRequest)) { + // Check for video bidrequest + if (_isVideoBidRequest(bidRequest)) { imp.video = _getORTBVideo(bidRequest); - } else if (bidRequest.mediaTypes.banner) { + } + // append banner if applicable and request is not for instream + if (bidRequest.mediaTypes.banner && !_isInstream(bidRequest)) { imp.banner = { format: _sizes(bidRequest.sizes) }; - }; + } + if (!isEmpty(bidRequest.ortb2Imp)) { imp.fpd = _getAdUnitFpd(bidRequest.ortb2Imp); } @@ -153,22 +156,41 @@ function _buildPostBody(bidRequests, bidderRequest) { return data; } -function _isInstreamBidRequest(bidRequest) { - if (!bidRequest.mediaTypes.video) return false; - if (!bidRequest.mediaTypes.video.context) return false; - if (bidRequest.mediaTypes.video.context.toLowerCase() === 'instream') { - return true; - } else { - return false; - } +function _isVideoBidRequest(bidRequest) { + return _isValidVideoObject(bidRequest) && (_isInstream(bidRequest) || _isOutstream(bidRequest)); +} + +function _isOutstream(bidRequest) { + return _isValidVideoObject(bidRequest) && bidRequest.mediaTypes.video.context.toLowerCase() === 'outstream'; +} + +function _isInstream(bidRequest) { + return _isValidVideoObject(bidRequest) && bidRequest.mediaTypes.video.context.toLowerCase() === 'instream'; +} + +function _isValidVideoObject(bidRequest) { + return bidRequest.mediaTypes.video && bidRequest.mediaTypes.video.context; } function _getORTBVideo(bidRequest) { // give precedent to mediaTypes.video let video = { ...bidRequest.params.video, ...bidRequest.mediaTypes.video }; - if (!video.w) video.w = video.playerSize[0][0]; - if (!video.h) video.h = video.playerSize[0][1]; + try { + if (!video.w) video.w = video.playerSize[0][0]; + if (!video.h) video.h = video.playerSize[0][1]; + } catch (err) { + logWarn('Video size not defined', err); + } if (video.context === 'instream') video.placement = 1; + if (video.context === 'outstream') { + if (!video.placement) { + video.placement = 3 + } else if ([3, 4, 5].indexOf(video.placement) === -1) { + logMessage(`video.placement value of ${video.placement} is invalid for outstream context. Setting placement to 3`) + video.placement = 3 + } + } + // clean up oRTB object delete video.playerSize; return video; @@ -180,7 +202,7 @@ function _getFloor (bid) { try { const floorInfo = bid.getFloor({ currency: 'USD', - mediaType: _isInstreamBidRequest(bid) ? 'video' : 'banner', + mediaType: _isVideoBidRequest(bid) ? 'video' : 'banner', size: '*' }); if (typeof floorInfo === 'object' && @@ -366,10 +388,10 @@ function _buildResponseObject(bidderRequest, bid) { meta: {} }; - if (_isInstreamBidRequest(breq)) { + if (_isVideoBidRequest(breq) && bid.media_type === 'video') { bidResponse.vastXml = bid.ad; bidResponse.mediaType = 'video'; - bidResponse.ttl = INSTREAM_TIME_TO_LIVE; + bidResponse.ttl = VIDEO_TIME_TO_LIVE; }; if (bid.advertiser_name) { @@ -381,7 +403,11 @@ function _buildResponseObject(bidderRequest, bid) { } if (bid.tl_source && bid.tl_source == 'hdx') { - bidResponse.meta.mediaType = 'banner'; + if (_isVideoBidRequest(breq) && bid.media_type === 'video') { + bidResponse.meta.mediaType = 'video' + } else { + bidResponse.meta.mediaType = 'banner' + } } if (bid.tl_source && bid.tl_source == 'tlx') { diff --git a/test/spec/modules/tripleliftBidAdapter_spec.js b/test/spec/modules/tripleliftBidAdapter_spec.js index b0269aaf077..bfcfce1ccb4 100644 --- a/test/spec/modules/tripleliftBidAdapter_spec.js +++ b/test/spec/modules/tripleliftBidAdapter_spec.js @@ -345,6 +345,209 @@ describe('triplelift adapter', function () { auctionId: '1d1a030790a475', userId: {}, schain, + }, + // outstream video only + { + bidder: 'triplelift', + params: { + inventoryCode: 'outstream_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6, + w: 640, + h: 480 + } + }, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 480] + } + }, + adUnitCode: 'adunit-code-outstream', + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userId: {}, + schain, + }, + // banner and incomplete outstream (missing size) + { + bidder: 'triplelift', + params: { + inventoryCode: 'outstream_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6 + } + }, + mediaTypes: { + video: { + context: 'outstream' + }, + banner: { + sizes: [ + [970, 250], + [1, 1] + ] + } + }, + adUnitCode: 'adunit-code-instream', + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userId: {}, + schain, + }, + // outstream video; valid placement + { + bidder: 'triplelift', + params: { + inventoryCode: 'outstream_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6, + w: 640, + h: 480 + } + }, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 480], + placement: 3 + } + }, + adUnitCode: 'adunit-code-instream', + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userId: {}, + schain, + }, + // outstream video; valid placement + { + bidder: 'triplelift', + params: { + inventoryCode: 'outstream_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6, + w: 640, + h: 480 + } + }, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 480], + placement: 4 + } + }, + adUnitCode: 'adunit-code-instream', + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userId: {}, + schain, + }, + // outstream video; valid placement + { + bidder: 'triplelift', + params: { + inventoryCode: 'outstream_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6, + w: 640, + h: 480 + } + }, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 480], + placement: 5 + } + }, + adUnitCode: 'adunit-code-instream', + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userId: {}, + schain, + }, + // outstream video; undefined placement + { + bidder: 'triplelift', + params: { + inventoryCode: 'outstream_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6, + w: 640, + h: 480 + } + }, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 480] + } + }, + adUnitCode: 'adunit-code-instream', + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userId: {}, + schain, + }, + // outstream video; invalid placement + { + bidder: 'triplelift', + params: { + inventoryCode: 'outstream_test', + floor: 1.0, + video: { + mimes: ['video/mp4'], + maxduration: 30, + minduration: 6, + w: 640, + h: 480 + } + }, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 480], + placement: 6 + } + }, + adUnitCode: 'adunit-code-instream', + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + userId: {}, + schain, } ]; @@ -415,13 +618,16 @@ describe('triplelift adapter', function () { expect(payload.imp[0].tagid).to.equal('12345'); expect(payload.imp[0].floor).to.equal(1.0); expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); + // instream expect(payload.imp[1].tagid).to.equal('insteam_test'); expect(payload.imp[1].floor).to.equal(1.0); expect(payload.imp[1].video).to.exist.and.to.be.a('object'); + expect(payload.imp[1].video.placement).to.equal(1); // banner and outstream video - expect(payload.imp[2]).to.not.have.property('video'); + expect(payload.imp[2]).to.have.property('video'); expect(payload.imp[2]).to.have.property('banner'); expect(payload.imp[2].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); + expect(payload.imp[2].video).to.deep.equal({'mimes': ['video/mp4'], 'maxduration': 30, 'minduration': 6, 'w': 640, 'h': 480, 'context': 'outstream', 'placement': 3}); // banner and incomplete video expect(payload.imp[3]).to.not.have.property('video'); expect(payload.imp[3]).to.have.property('banner'); @@ -434,10 +640,51 @@ describe('triplelift adapter', function () { expect(payload.imp[5]).to.not.have.property('banner'); expect(payload.imp[5]).to.have.property('video'); expect(payload.imp[5].video).to.exist.and.to.be.a('object'); + expect(payload.imp[5].video.placement).to.equal(1); // banner and outream video and native - expect(payload.imp[6]).to.not.have.property('video'); + expect(payload.imp[6]).to.have.property('video'); expect(payload.imp[6]).to.have.property('banner'); expect(payload.imp[6].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); + expect(payload.imp[6].video).to.deep.equal({'mimes': ['video/mp4'], 'maxduration': 30, 'minduration': 6, 'w': 640, 'h': 480, 'context': 'outstream', 'placement': 3}); + // outstream video only + expect(payload.imp[7]).to.have.property('video'); + expect(payload.imp[7]).to.not.have.property('banner'); + expect(payload.imp[7].video).to.deep.equal({'mimes': ['video/mp4'], 'maxduration': 30, 'minduration': 6, 'w': 640, 'h': 480, 'context': 'outstream', 'placement': 3}); + // banner and incomplete outstream (missing size); video request is permitted so banner can still monetize + expect(payload.imp[8]).to.have.property('video'); + expect(payload.imp[8]).to.have.property('banner'); + expect(payload.imp[8].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); + expect(payload.imp[8].video).to.deep.equal({'mimes': ['video/mp4'], 'maxduration': 30, 'minduration': 6, 'context': 'outstream', 'placement': 3}); + }); + + it('should check for valid outstream placement values', function () { + const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); + const payload = request.data; + // outstream video; valid placement + expect(payload.imp[9]).to.not.have.property('banner'); + expect(payload.imp[9]).to.have.property('video'); + expect(payload.imp[9].video).to.exist.and.to.be.a('object'); + expect(payload.imp[9].video.placement).to.equal(3); + // outstream video; valid placement + expect(payload.imp[10]).to.not.have.property('banner'); + expect(payload.imp[10]).to.have.property('video'); + expect(payload.imp[10].video).to.exist.and.to.be.a('object'); + expect(payload.imp[10].video.placement).to.equal(4); + // outstream video; valid placement + expect(payload.imp[11]).to.not.have.property('banner'); + expect(payload.imp[11]).to.have.property('video'); + expect(payload.imp[11].video).to.exist.and.to.be.a('object'); + expect(payload.imp[11].video.placement).to.equal(5); + // outstream video; undefined placement + expect(payload.imp[12]).to.not.have.property('banner'); + expect(payload.imp[12]).to.have.property('video'); + expect(payload.imp[12].video).to.exist.and.to.be.a('object'); + expect(payload.imp[12].video.placement).to.equal(3); + // outstream video; invalid placement + expect(payload.imp[13]).to.not.have.property('banner'); + expect(payload.imp[13]).to.have.property('video'); + expect(payload.imp[13].video).to.exist.and.to.be.a('object'); + expect(payload.imp[13].video.placement).to.equal(3); }); it('should add tdid to the payload if included', function () { @@ -913,14 +1160,40 @@ describe('triplelift adapter', function () { iurl: 'https://s.adroll.com/a/IYR/N36/IYRN366MFVDITBAGNNT5U6.jpg', tl_source: 'tlx', advertiser_name: 'fake advertiser name', - adomain: ['basspro.com', 'internetalerts.org'] + adomain: ['basspro.com', 'internetalerts.org'], + media_type: 'banner' }, { imp_id: 1, crid: '10092_76480_i2j6qm8u', cpm: 9.99, - ad: 'The Trade Desk', - tlx_source: 'hdx' + ad: 'The Trade Desk', + tl_source: 'hdx', + media_type: 'video' + }, + // video bid on banner+outstream request + { + imp_id: 2, + crid: '5989_33264_352817187', + cpm: 20, + ad: '\n \n \t', + tl_source: 'hdx', + advertiser_name: 'zennioptical.com', + adomain: ['zennioptical.com'], + media_type: 'video' + }, + // banner bid on banner+outstream request + { + imp_id: 3, + crid: '5989_33264_352817187', + cpm: 20, + width: 970, + height: 250, + ad: 'ad-markup', + tl_source: 'hdx', + advertiser_name: 'zennioptical.com', + adomain: ['zennioptical.com'], + media_type: 'banner' } ] } @@ -946,13 +1219,13 @@ describe('triplelift adapter', function () { ] } }, - bidId: '30b31c1838de1e', + bidId: '30b31c1838de1e' }, { imp_id: 1, crid: '10092_76480_i2j6qm8u', cpm: 9.99, - ad: 'The Trade Desk', + ad: 'The Trade Desk', tlx_source: 'hdx', mediaTypes: { video: { @@ -960,7 +1233,89 @@ describe('triplelift adapter', function () { playerSize: [640, 480] } }, - bidId: '30b31c1838de1e', + bidId: '30b31c1838de1e' + }, + // banner and outstream + { + bidder: 'triplelift', + params: { + inventoryCode: 'testing_desktop_outstream', + floor: 1 + }, + nativeParams: {}, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[640, 480]], + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + playbackmethod: [2], + skip: 1 + }, + banner: { + sizes: [ + [728, 90], + [970, 250], + [970, 90] + ] + }, + native: {} + }, + adUnitCode: 'video-outstream', + transactionId: '135061c3-f546-4e28-8a07-44c2fb58a958', + sizes: [ + [728, 90], + [970, 250], + [970, 90] + ], + bidId: '73edc0ba8de203', + bidderRequestId: '3d81143328560b', + auctionId: 'f6427dc0-b954-4010-a76c-d498380796a2', + src: 'client', + bidRequestsCount: 2, + bidderRequestsCount: 2, + bidderWinsCount: 0 + }, + // banner and outstream + { + bidder: 'triplelift', + params: { + inventoryCode: 'testing_desktop_outstream', + floor: 1 + }, + nativeParams: {}, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[640, 480]], + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + playbackmethod: [2], + skip: 1 + }, + banner: { + sizes: [ + [728, 90], + [970, 250], + [970, 90] + ] + }, + native: {} + }, + adUnitCode: 'video-outstream', + transactionId: '135061c3-f546-4e28-8a07-44c2fb58a958', + sizes: [ + [728, 90], + [970, 250], + [970, 90] + ], + bidId: '73edc0ba8de203', + bidderRequestId: '3d81143328560b', + auctionId: 'f6427dc0-b954-4010-a76c-d498380796a2', + src: 'client', + bidRequestsCount: 2, + bidderRequestsCount: 2, + bidderWinsCount: 0 } ], refererInfo: { @@ -1007,16 +1362,29 @@ describe('triplelift adapter', function () { } ]; let result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); - expect(result).to.have.length(2); + expect(result).to.have.length(4); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); expect(Object.keys(result[1])).to.have.members(Object.keys(expectedResponse[1])); expect(result[0].ttl).to.equal(300); expect(result[1].ttl).to.equal(3600); }); + it('should identify format of bid and respond accordingly', function() { + let result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); + expect(result[0].meta.mediaType).to.equal('native'); + expect(result[1].mediaType).to.equal('video'); + expect(result[1].meta.mediaType).to.equal('video'); + // video bid on banner+outstream request + expect(result[2].mediaType).to.equal('video'); + expect(result[2].meta.mediaType).to.equal('video'); + expect(result[2].vastXml).to.include('aid=148508128401385324170&inv_code=testing_mobile_outstream'); + // banner bid on banner+outstream request + expect(result[3].meta.mediaType).to.equal('banner'); + }) + it('should return multiple responses to support SRA', function () { let result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); - expect(result).to.have.length(2); + expect(result).to.have.length(4); }); it('should include the advertiser name in the meta field if available', function () { From a231b0da5935b474f5341a9070f7951222f2aeed Mon Sep 17 00:00:00 2001 From: Jozef Bartek <31618107+jbartek25@users.noreply.github.com> Date: Mon, 1 Aug 2022 18:40:29 +0200 Subject: [PATCH 015/246] Improve Digital adapter: support for JS trackers in native 1.2 (#8701) --- modules/improvedigitalBidAdapter.js | 3 +++ test/spec/modules/improvedigitalBidAdapter_spec.js | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js index dd2f85dcff5..a362f85654c 100644 --- a/modules/improvedigitalBidAdapter.js +++ b/modules/improvedigitalBidAdapter.js @@ -434,6 +434,9 @@ const ID_REQUEST = { return null; } const request = { + eventtrackers: [ + {event: 1, methods: [1, 2]} + ], assets: [], } for (let i of Object.keys(nativeParams)) { diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js index b44859f73a0..a882f5ca5ef 100644 --- a/test/spec/modules/improvedigitalBidAdapter_spec.js +++ b/test/spec/modules/improvedigitalBidAdapter_spec.js @@ -248,7 +248,7 @@ describe('Improve Digital Adapter Tests', function () { { id: '33e9500b21129f', native: { - request: '{"assets":[{"id":3,"required":1,"data":{"type":2}}]}', + request: '{"eventtrackers":[{"event":1,"methods":[1,2]}],"assets":[{"id":3,"required":1,"data":{"type":2}}]}', ver: '1.2' }, secure: 0, @@ -277,7 +277,7 @@ describe('Improve Digital Adapter Tests', function () { const payload = JSON.parse(spec.buildRequests([nativeBidRequest], {})[0].data); expect(payload.imp[0].native).to.deep.equal({ ver: '1.2', - request: '{\"assets\":[{\"id\":0,\"required\":1,\"title\":{\"len\":140}},{\"id\":3,\"required\":1,\"data\":{\"type\":2}}]}' + request: '{"eventtrackers":[{"event":1,"methods":[1,2]}],"assets":[{"id":0,"required":1,"title":{"len":140}},{"id":3,"required":1,"data":{"type":2}}]}' }); }); @@ -299,7 +299,7 @@ describe('Improve Digital Adapter Tests', function () { const payload = JSON.parse(spec.buildRequests([nativeBidRequest], {})[0].data); expect(payload.imp[0].native).to.deep.equal({ ver: '1.2', - request: '{\"assets\":[{\"id\":0,\"required\":1,\"title\":{\"len\":140}},{\"id\":3,\"required\":1,\"data\":{\"type\":2}}]}' + request: '{"eventtrackers":[{"event":1,"methods":[1,2]}],"assets":[{"id":0,"required":1,"title":{"len":140}},{"id":3,"required":1,"data":{"type":2}}]}' }); }); @@ -930,7 +930,7 @@ describe('Improve Digital Adapter Tests', function () { 'exp': 120, 'id': '52098fad-20c1-476b-a4fa-41e275e5a4a5', 'price': 1.8600000000000003, - 'adm': "{\"ver\":\"1.1\",\"imptrackers\":[\"https://secure.adnxs.com/imptr?id=52311&t=2\",\"https://euw-ice.360yield.com/imp_pixel?ic=hcUBlCANx1FabHBf6FR2gC7UO4xEyXahdZAn0-B5qL-bb3A74BJ1smyWIyW7IWcC0SOjSXzVpevTHXxTqJ.sf.Qhahyy6tSo.0j1QWfXlH8sM4-8vKWjMjw-x.IrJJNlwkQ0s1CdwcwTefcLXm5l2E-W19VhACuV7f3mgrZMNjiSw.SjJAfyPC3SIyAMRjYfj53UmjriQ46T7lhmkqxK8wHmksYCdbZc3PZESk8NWl28sxdjNvnYYCFMcJbeav.LOLabyTXfwy-1cEPbQs.IKMRZIKaqccTDPV3wOtzbNv0jQzatd3Nnv-PGFQcjQ-GW3i27W04Fws4kodpFSn-B6VwZAjzLzoyd5gBncyRnAyCplEbgHU5sZ1IyKHWjgCl3ZtRIK5vqrRD5D-xqgSnOi7-phG.CqZWDZ4bMDSfQg2ZnbvUTyGKcEl0WR59dW5izTMV4Fjizcrvr5T-t.zMbGwz.hGnmLIyhTqh.IcwW.GiDLVExlDlix5S1LXIWVsSyrQ==\"],\"assets\":[{\"id\":1,\"data\":{\"value\":\"ImproveDigital\",\"type\":1}},{\"id\":3,\"data\":{\"value\":\"Test content.\",\"type\":2}},{\"id\":0,\"title\":{\"text\":\"Sample Prebid Test Title\"}}],\"link\":{\"url\":\"https://euw-ice.360yield.com/click/hcUBlHOV7YhVse8RyBa0ajjyPa9Vt17e4g-1m3cRj3E67vq-RYux.SiUeAmBfNBcoOqkUc6A15AWmi4yFu5K-BdkaYjildyyk7fNLyR6hWr411kv4vrFwm5jrIBceuHS6K8oN69f.uCo8zGTdR2TbSlldwcpahQPlufZU.6VaMsu4IC53uEiUT5vb7kAw6TTlxuGBNq6zaGryiWEV2.N3YYJDTyYPh8tv-ZFyeFZFm0Gnjv.xWbC.70JcRUVU9UelQaPsTpTWYTXBhJt84YJUw1-GNtaLNVLSjjZbVoA2fsMti5p6OBmF.7u39on2OPgvseIkSmge7Pqg63pRqdP75hp.DAEk6OkcN1jGnwP2DSbvpaSbin5lVqjfO0B-wnQgfQTCUtM5v4JmkNweLhUf9Q-x.nPKLW5SccEk9ZFXzY2-1wpT3PWm8Tix3NRscLPZub9wHzL..pl6ip8cQ9hp16UjwT4H6RMAxL0R7bl-h2pAicGAzYmuO7ntRESKUoIWA==//http%3A%2F%2Fquantum-advertising.com%2Ffr%2F\"},\"jstracker\":\"\"}", + 'adm': '{"ver":"1.1","imptrackers":["https://secure.adnxs.com/imptr?id=52311&t=2","https://euw-ice.360yield.com/imp_pixel?ic=hcUBlCANx1FabHBf6FR2gC7UO4xEyXahdZAn0-B5qL-bb3A74BJ1smyWIyW7IWcC0SOjSXzVpevTHXxTqJ.sf.Qhahyy6tSo.0j1QWfXlH8sM4-8vKWjMjw-x.IrJJNlwkQ0s1CdwcwTefcLXm5l2E-W19VhACuV7f3mgrZMNjiSw.SjJAfyPC3SIyAMRjYfj53UmjriQ46T7lhmkqxK8wHmksYCdbZc3PZESk8NWl28sxdjNvnYYCFMcJbeav.LOLabyTXfwy-1cEPbQs.IKMRZIKaqccTDPV3wOtzbNv0jQzatd3Nnv-PGFQcjQ-GW3i27W04Fws4kodpFSn-B6VwZAjzLzoyd5gBncyRnAyCplEbgHU5sZ1IyKHWjgCl3ZtRIK5vqrRD5D-xqgSnOi7-phG.CqZWDZ4bMDSfQg2ZnbvUTyGKcEl0WR59dW5izTMV4Fjizcrvr5T-t.zMbGwz.hGnmLIyhTqh.IcwW.GiDLVExlDlix5S1LXIWVsSyrQ=="],"assets":[{"id":1,"data":{"value":"ImproveDigital","type":1}},{"id":3,"data":{"value":"Test content.","type":2}},{"id":0,"title":{"text":"Sample Prebid Test Title"}}],"link":{"url":"https://euw-ice.360yield.com/click/hcUBlHOV7YhVse8RyBa0ajjyPa9Vt17e4g-1m3cRj3E67vq-RYux.SiUeAmBfNBcoOqkUc6A15AWmi4yFu5K-BdkaYjildyyk7fNLyR6hWr411kv4vrFwm5jrIBceuHS6K8oN69f.uCo8zGTdR2TbSlldwcpahQPlufZU.6VaMsu4IC53uEiUT5vb7kAw6TTlxuGBNq6zaGryiWEV2.N3YYJDTyYPh8tv-ZFyeFZFm0Gnjv.xWbC.70JcRUVU9UelQaPsTpTWYTXBhJt84YJUw1-GNtaLNVLSjjZbVoA2fsMti5p6OBmF.7u39on2OPgvseIkSmge7Pqg63pRqdP75hp.DAEk6OkcN1jGnwP2DSbvpaSbin5lVqjfO0B-wnQgfQTCUtM5v4JmkNweLhUf9Q-x.nPKLW5SccEk9ZFXzY2-1wpT3PWm8Tix3NRscLPZub9wHzL..pl6ip8cQ9hp16UjwT4H6RMAxL0R7bl-h2pAicGAzYmuO7ntRESKUoIWA==//http%3A%2F%2Fquantum-advertising.com%2Ffr%2F"},"jstracker":""}', 'impid': '33e9500b21129f', 'cid': '196108' } From d46661a4aa8994d57f3bea3055bd4e4c8aa50e10 Mon Sep 17 00:00:00 2001 From: Sasikumar Dharmaraj <83545471+sasikumar-c1x@users.noreply.github.com> Date: Tue, 2 Aug 2022 20:36:50 +0530 Subject: [PATCH 016/246] c1x Bid Adapter: adding back adapter with updated pbjs compliance (#8706) * C1X Adapter GDPR Support * remove the redundant userSync * fixed keyword spacing issue * c1x adapter changes * c1x adapter latest changes * added deal id support for ssp endpoint * changes for ne bidder parameters in md file * changes * changes * test cases added * Test cases changes * test cases added * test cases changes * test cases changes * changes * changes * changes * changes * changes for log * changes suggested from PR Co-authored-by: Cathy Huang Co-authored-by: sasikumar dharmaraj Co-authored-by: Devyani Choubey Co-authored-by: devyaniChoubey --- modules/c1xBidAdapter.js | 209 ++++++++++++++++++++++++ modules/c1xBidAdapter.md | 70 ++++++++ test/spec/modules/c1xBidAdapter_spec.js | 189 +++++++++++++++++++++ 3 files changed, 468 insertions(+) create mode 100644 modules/c1xBidAdapter.js create mode 100644 modules/c1xBidAdapter.md create mode 100644 test/spec/modules/c1xBidAdapter_spec.js diff --git a/modules/c1xBidAdapter.js b/modules/c1xBidAdapter.js new file mode 100644 index 00000000000..8b579f52299 --- /dev/null +++ b/modules/c1xBidAdapter.js @@ -0,0 +1,209 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { logInfo, logError } from '../src/utils.js'; +import { BANNER } from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'c1x'; +const URL = 'https://hb-stg.c1exchange.com/ht'; +// const PIXEL_ENDPOINT = '//px.c1exchange.com/pubpixel/'; +const LOG_MSG = { + invalidBid: 'C1X: [ERROR] bidder returns an invalid bid', + noSite: 'C1X: [ERROR] no site id supplied', + noBid: 'C1X: [INFO] creating a NO bid for Adunit: ', + bidWin: 'C1X: [INFO] creating a bid for Adunit: ' +}; + +/** + * Adapter for requesting bids from C1X header tag server. + * v3.1 (c) C1X Inc., 2018 + */ + +export const c1xAdapter = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + + /** + * Determines whether or not the given bid request is valid. + * + * @param {object} bid The bid to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + // check the bids sent to c1x bidder + isBidRequestValid: function (bid) { + if (bid.bidder !== BIDDER_CODE || typeof bid.params === 'undefined') { + return false; + } + if (typeof bid.params.placementId === 'undefined') { + return false; + } + return true; + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests A non-empty list of valid bid requests that should be sent to the Server. + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests, bidderRequest) { + let payload = {}; + let tagObj = {}; + let bidRequest = []; + const adunits = validBidRequests.length; + const rnd = new Date().getTime(); + const c1xTags = validBidRequests.map(bidToTag); + const bidIdTags = validBidRequests.map(bidToShortTag); // include only adUnitCode and bidId from request obj + + // flattened tags in a tag object + tagObj = c1xTags.reduce((current, next) => Object.assign(current, next)); + + payload = { + adunits: adunits.toString(), + rnd: rnd.toString(), + response: 'json', + compress: 'gzip' + }; + + // for GDPR support + if (bidderRequest && bidderRequest.gdprConsent) { + payload['consent_string'] = bidderRequest.gdprConsent.consentString; + payload['consent_required'] = (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies.toString() : 'true' + ; + } + + Object.assign(payload, tagObj); + let payloadString = stringifyPayload(payload); + // ServerRequest object + bidRequest.push({ + method: 'GET', + url: URL, + data: payloadString, + bids: bidIdTags + }) + return bidRequest; + }, + + interpretResponse: function (serverResponse, requests) { + serverResponse = serverResponse.body; + requests = requests.bids || []; + const currency = 'USD'; + const bidResponses = []; + let netRevenue = false; + + if (!serverResponse || serverResponse.error) { + let errorMessage = serverResponse.error; + logError(LOG_MSG.invalidBid + errorMessage); + return bidResponses; + } else { + serverResponse.forEach(bid => { + logInfo(bid) + if (bid.bid) { + if (bid.bidType === 'NET_BID') { + netRevenue = !netRevenue; + } + const curBid = { + requestId: bid.bidId, + width: bid.width, + height: bid.height, + cpm: bid.cpm, + ad: bid.ad, + creativeId: bid.crid, + currency: currency, + ttl: 300, + netRevenue: netRevenue + }; + + if (bid.dealId) { + curBid['dealId'] = bid.dealId + } + + for (let i = 0; i < requests.length; i++) { + if (bid.adId === requests[i].adUnitCode) { + curBid.requestId = requests[i].bidId; + } + } + logInfo(LOG_MSG.bidWin + bid.adId + ' size: ' + curBid.width + 'x' + curBid.height); + bidResponses.push(curBid); + } else { + // no bid + logInfo(LOG_MSG.noBid + bid.adId); + } + }); + } + + return bidResponses; + } + +} + +function bidToTag(bid, index) { + const tag = {}; + const adIndex = 'a' + (index + 1).toString(); // ad unit id for c1x + const sizeKey = adIndex + 's'; + const priceKey = adIndex + 'p'; + const dealKey = adIndex + 'd'; + // TODO: Multiple Floor Prices + + const sizesArr = bid.sizes; + const floorPriceMap = getBidFloor(bid); + + const dealId = bid.params.dealId || ''; + + if (dealId) { + tag[dealKey] = dealId; + } + + tag[adIndex] = bid.adUnitCode; + tag[sizeKey] = sizesArr.reduce((prev, current) => prev + (prev === '' ? '' : ',') + current.join('x'), ''); + const newSizeArr = tag[sizeKey].split(','); + if (floorPriceMap) { + newSizeArr.forEach(size => { + if (size in floorPriceMap) { + tag[priceKey] = floorPriceMap[size].toString(); + } // we only accept one cpm price in floorPriceMap + }); + } + if (bid.params.pageurl) { + tag['pageurl'] = bid.params.pageurl; + } + + return tag; +} + +function getBidFloor(bidRequest) { + let floorInfo = {}; + + if (typeof bidRequest.getFloor === 'function') { + floorInfo = bidRequest.getFloor({ + currency: 'USD', + mediaType: 'banner', + size: '*', + }); + } + + let floor = + floorInfo.floor || + bidRequest.params.bidfloor || + bidRequest.params.floorPriceMap || + 0; + + return floor; +} + +function bidToShortTag(bid) { + const tag = {}; + tag.adUnitCode = bid.adUnitCode; + tag.bidId = bid.bidId; + return tag; +} + +function stringifyPayload(payload) { + let payloadString = []; + for (var key in payload) { + if (payload.hasOwnProperty(key)) { + payloadString.push(key + '=' + payload[key]); + } + } + return payloadString.join('&'); +} + +registerBidder(c1xAdapter); diff --git a/modules/c1xBidAdapter.md b/modules/c1xBidAdapter.md new file mode 100644 index 00000000000..9a7cef486d2 --- /dev/null +++ b/modules/c1xBidAdapter.md @@ -0,0 +1,70 @@ +# Overview + +Module Name: C1X Bidder Adapter +Module Type: Bidder Adapter +Maintainer: vishnu@c1exchange.com + +# Description + +Module that connects to C1X's demand sources + +# Test Parameters +``` + var adUnits = [{ + code: 'test-div-1', + mediaTypes: { + banner: { + sizes: [[750, 200]], + } + }, + bids: [{ + bidder: 'c1x', + params: { + placementId: 'div-gpt-ad-1654594619717-0', + 'floorPriceMap': { + '300x250': 4.35 + } + } + }] + }, { + code: 'test-div-2', + mediaTypes: { + banner: { + sizes: [[300, 250], [750, 200]], + } + }, + bids: [{ + bidder: 'c1x', + params: { + placementId: 'div-gpt-ad-1654940683355-0', + 'floorPriceMap': { + '300x250': 4.35 + } + dealId: '1233' // optional parameter + } + }] + }]; + + + pbjs.bidderSettings = { + c1x: { + siteId: 999, + adserverTargeting: [{ + key: "hb_bidder", + val: function(bidResponse) { + return bidResponse.bidderCode; + } + }, { + key: "hb_adid", + val: function(bidResponse) { + return bidResponse.adId; + } + }, { + key: "hb_pb", + val: function(bidResponse) { + return bidResponse.pbLg; + } + }] + } + } +``` \ No newline at end of file diff --git a/test/spec/modules/c1xBidAdapter_spec.js b/test/spec/modules/c1xBidAdapter_spec.js new file mode 100644 index 00000000000..315680cba26 --- /dev/null +++ b/test/spec/modules/c1xBidAdapter_spec.js @@ -0,0 +1,189 @@ +import { expect } from 'chai'; +import { newBidder } from 'src/adapters/bidderFactory'; +import { c1xAdapter } from '../../../modules/c1xBidAdapter.js'; + +const ENDPOINT = 'https://hb-stg.c1exchange.com/ht'; +const BIDDER_CODE = 'c1x'; + +describe('C1XAdapter', () => { + const adapter = newBidder(c1xAdapter); + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + describe('isBidRequestValid', () => { + let bid = { + 'bidder': BIDDER_CODE, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'params': { + 'placementId': 'div-gpt-ad-1654594619717-0' + } + }; + it('should return true when required params found', function () { + expect(c1xAdapter.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when placementId not passed correctly', function () { + bid.params.placementId = undefined; + expect(c1xAdapter.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when require params are not passed', function () { + let bid = Object.assign({}, bid); + bid.params = {}; + expect(c1xAdapter.isBidRequestValid(bid)).to.equal(false); + }); + }); + describe('buildRequests', () => { + let bidRequests = [ + { + 'bidder': BIDDER_CODE, + 'params': { + 'placementId': 'div-gpt-ad-1654594619717-0', + 'dealId': '1233' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [300, 250], [300, 600] + ] + } + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + const parseRequest = (data) => { + const parsedData = '{"' + data.replace(/=|&/g, (foundChar) => { + if (foundChar == '=') return '":"'; + else if (foundChar == '&') return '","'; + }) + '"}' + return parsedData; + }; + it('sends bid request to ENDPOINT via GET', () => { + const request = c1xAdapter.buildRequests(bidRequests); + expect(request[0].url).to.contain(ENDPOINT); + expect(request[0].method).to.equal('GET'); + }); + it('should generate correct bid Id tag', () => { + const request = c1xAdapter.buildRequests(bidRequests)[0]; + expect(request.bids[0].adUnitCode).to.equal('adunit-code'); + expect(request.bids[0].bidId).to.equal('30b31c1838de1e'); + }); + it('should convert params to proper form and attach to request', () => { + const request = c1xAdapter.buildRequests(bidRequests)[0]; + const originalPayload = parseRequest(request.data); + const payloadObj = JSON.parse(originalPayload); + expect(payloadObj.adunits).to.equal('1'); + expect(payloadObj.a1s).to.equal('300x250,300x600'); + expect(payloadObj.a1).to.equal('adunit-code'); + expect(payloadObj.a1d).to.equal('1233'); + }); + it('should convert floor price to proper form and attach to request', () => { + let bidRequest = Object.assign({}, + bidRequests[0], + { + 'params': { + 'placementId': 'div-gpt-ad-1654594619717-0', + 'dealId': '1233', + 'floorPriceMap': { + '300x250': 4.35 + } + } + }); + const request = c1xAdapter.buildRequests([bidRequest])[0]; + const originalPayload = parseRequest(request.data); + const payloadObj = JSON.parse(originalPayload); + expect(payloadObj.a1p).to.equal('4.35'); + }); + it('should convert pageurl to proper form and attach to request', () => { + let bidRequest = Object.assign({}, + bidRequests[0], + { + 'params': { + 'placementId': 'div-gpt-ad-1654594619717-0', + 'dealId': '1233', + 'pageurl': 'http://c1exchange.com/' + } + }); + + let bidderRequest = { + 'bidderCode': 'c1x' + } + bidderRequest.bids = bidRequests; + const request = c1xAdapter.buildRequests([bidRequest], bidderRequest)[0]; + const originalPayload = parseRequest(request.data); + const payloadObj = JSON.parse(originalPayload); + expect(payloadObj.pageurl).to.equal('http://c1exchange.com/'); + }); + + it('should convert GDPR Consent to proper form and attach to request', () => { + let consentString = 'BOP2gFWOQIFovABABAENBGAAAAAAMw'; + let bidderRequest = { + 'bidderCode': 'c1x', + 'gdprConsent': { + 'consentString': consentString, + 'gdprApplies': true + } + } + bidderRequest.bids = bidRequests; + + const request = c1xAdapter.buildRequests(bidRequests, bidderRequest)[0]; + const originalPayload = parseRequest(request.data); + const payloadObj = JSON.parse(originalPayload); + expect(payloadObj['consent_string']).to.equal('BOP2gFWOQIFovABABAENBGAAAAAAMw'); + expect(payloadObj['consent_required']).to.equal('true'); + }); + }); + + describe('interpretResponse', () => { + let response = { + 'bid': true, + 'cpm': 1.5, + 'ad': '', + 'width': 300, + 'height': 250, + 'crid': '8888', + 'adId': 'c1x-test', + 'bidType': 'GROSS_BID' + }; + it('should get correct bid response', () => { + let expectedResponse = [ + { + width: 300, + height: 250, + cpm: 1.5, + ad: '', + creativeId: '8888', + currency: 'USD', + ttl: 300, + netRevenue: false, + requestId: 'yyyy' + } + ]; + let bidderRequest = {}; + bidderRequest.bids = [ + { + adUnitCode: 'c1x-test', + bidId: 'yyyy' + } + ]; + let result = c1xAdapter.interpretResponse({ body: [response] }, bidderRequest); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + it('handles nobid responses', () => { + let response = { + bid: false, + adId: 'c1x-test' + }; + let bidderRequest = {}; + let result = c1xAdapter.interpretResponse({ body: [response] }, bidderRequest); + expect(result.length).to.equal(0); + }); + }); +}); From 7d127441c5df7fc760c78e92fb79fd7618fb8f15 Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Wed, 3 Aug 2022 15:34:34 +0200 Subject: [PATCH 017/246] bidWatch Analytics Adapter : add creative endpoint (#8710) * New Analytics Adapter bidwatch * test for bidwatch Analytics Adapter * change maintainer address * Update bidwatchAnalyticsAdapter.js * Update bidwatchAnalyticsAdapter.js * Update bidwatchAnalyticsAdapter.md * Update bidwatchAnalyticsAdapter.md * add features to bidwatchAnalyticsAdapter * update tests * add test and made improvements --- modules/bidwatchAnalyticsAdapter.js | 81 ++++++++++++++++--- .../modules/bidwatchAnalyticsAdapter_spec.js | 18 ++++- 2 files changed, 87 insertions(+), 12 deletions(-) diff --git a/modules/bidwatchAnalyticsAdapter.js b/modules/bidwatchAnalyticsAdapter.js index 26a8c370af3..138f0533238 100644 --- a/modules/bidwatchAnalyticsAdapter.js +++ b/modules/bidwatchAnalyticsAdapter.js @@ -10,10 +10,14 @@ const { EVENTS: { AUCTION_END, BID_WON, + BID_RESPONSE, + BID_REQUESTED, } } = CONSTANTS; +let saveEvents = {} let allEvents = {} +let auctionEnd = {} let initOptions = {} let endpoint = 'https://default' let objectToSearchForBidderCode = ['bidderRequests', 'bidsReceived', 'noBids'] @@ -22,38 +26,87 @@ function getAdapterNameForAlias(aliasName) { return adapterManager.aliasRegistry[aliasName] || aliasName; } -function setOriginalBidder(arg) { +function cleanArgObject(arg, removead) { + if (typeof arg['bidderCode'] == 'string') { arg['originalBidder'] = getAdapterNameForAlias(arg['bidderCode']); } + if (typeof arg['creativeId'] == 'number') { + arg['creativeId'] = arg['creativeId'].toString(); + } + if (removead && typeof arg['ad'] != 'undefined') { + arg['ad'] = 'emptied'; + } + if (typeof arg['gdprConsent'] != 'undefined' && typeof arg['gdprConsent']['vendorData'] != 'undefined') { + arg['gdprConsent']['vendorData'] = 'emptied'; + } + return arg; +} + +function cleanArgs(arg, removead) { Object.keys(arg).forEach(key => { - arg[key]['originalBidder'] = getAdapterNameForAlias(arg[key]['bidderCode']); - if (typeof arg[key]['creativeId'] == 'number') { arg[key]['creativeId'] = arg[key]['creativeId'].toString(); } + arg[key] = cleanArgObject(arg[key], removead); }); return arg } -function checkBidderCode(args) { +function checkBidderCode(args, removead) { if (typeof args == 'object') { for (let i = 0; i < objectToSearchForBidderCode.length; i++) { - if (typeof args[objectToSearchForBidderCode[i]] == 'object') { args[objectToSearchForBidderCode[i]] = setOriginalBidder(args[objectToSearchForBidderCode[i]]) } + if (typeof args[objectToSearchForBidderCode[i]] == 'object') { args[objectToSearchForBidderCode[i]] = cleanArgs(args[objectToSearchForBidderCode[i]], removead) } } } if (typeof args['bidderCode'] == 'string') { args['originalBidder'] = getAdapterNameForAlias(args['bidderCode']); } else if (typeof args['bidder'] == 'string') { args['originalBidder'] = getAdapterNameForAlias(args['bidder']); } if (typeof args['creativeId'] == 'number') { args['creativeId'] = args['creativeId'].toString(); } + return args } function addEvent(eventType, args) { - if (allEvents[eventType] == undefined) { allEvents[eventType] = [] } - if (eventType && args) { args = checkBidderCode(args); } - allEvents[eventType].push(args); + let argsCleaned; + if (eventType && args) { + if (allEvents[eventType] == undefined) { allEvents[eventType] = [] } + if (saveEvents[eventType] == undefined) { saveEvents[eventType] = [] } + argsCleaned = checkBidderCode(JSON.parse(JSON.stringify(args)), false); + allEvents[eventType].push(argsCleaned); + saveEvents[eventType].push(argsCleaned); + argsCleaned = checkBidderCode(JSON.parse(JSON.stringify(args)), true); + if (['auctionend', 'bidtimeout'].includes(eventType.toLowerCase())) { + if (auctionEnd[eventType] == undefined) { auctionEnd[eventType] = [] } + auctionEnd[eventType].push(argsCleaned); + } + } } function handleBidWon(args) { - if (typeof allEvents.bidRequested == 'object' && allEvents.bidRequested.length > 0 && allEvents.bidRequested[0].gdprConsent) { args.gdpr = allEvents.bidRequested[0].gdprConsent; } + args = cleanArgObject(JSON.parse(JSON.stringify(args)), true); + let increment = args['cpm']; + if (typeof saveEvents['auctionEnd'] == 'object') { + for (let i = 0; i < saveEvents['auctionEnd'].length; i++) { + let tmpAuction = saveEvents['auctionEnd'][i]; + if (tmpAuction['auctionId'] == args['auctionId'] && typeof tmpAuction['bidsReceived'] == 'object') { + for (let j = 0; j < tmpAuction['bidsReceived'].length; j++) { + let tmpBid = tmpAuction['bidsReceived'][j]; + if (tmpBid['transactionId'] == args['transactionId'] && tmpBid['adId'] != args['adId']) { + if (args['cpm'] < tmpBid['cpm']) { + increment = 0; + } else if (increment > args['cpm'] - tmpBid['cpm']) { + increment = args['cpm'] - tmpBid['cpm']; + } + } + } + } + } + } + args['cpmIncrement'] = increment; + if (typeof saveEvents.bidRequested == 'object' && saveEvents.bidRequested.length > 0 && saveEvents.bidRequested[0].gdprConsent) { args.gdpr = saveEvents.bidRequested[0].gdprConsent; } ajax(endpoint + '.bidwatch.io/analytics/bid_won', null, JSON.stringify(args), {method: 'POST', withCredentials: true}); } function handleAuctionEnd() { - ajax(endpoint + '.bidwatch.io/analytics/auctions', null, JSON.stringify(allEvents), {method: 'POST', withCredentials: true}); + ajax(endpoint + '.bidwatch.io/analytics/auctions', null, JSON.stringify(auctionEnd), {method: 'POST', withCredentials: true}); + auctionEnd = {} + if (typeof allEvents['bidResponse'] != 'undefined') { + for (let i = 0; i < allEvents['bidResponse'].length; i++) { ajax(endpoint + '.bidwatch.io/analytics/creatives', null, JSON.stringify(allEvents['bidResponse'][i]), {method: 'POST', withCredentials: true}); } + } + allEvents = {} } let bidwatchAnalytics = Object.assign(adapter({url, analyticsType}), { @@ -61,14 +114,20 @@ let bidwatchAnalytics = Object.assign(adapter({url, analyticsType}), { eventType, args }) { - addEvent(eventType, args); switch (eventType) { case AUCTION_END: + addEvent(eventType, args); handleAuctionEnd(); break; case BID_WON: handleBidWon(args); break; + case BID_RESPONSE: + addEvent(eventType, args); + break; + case BID_REQUESTED: + addEvent(eventType, args); + break; } }}); diff --git a/test/spec/modules/bidwatchAnalyticsAdapter_spec.js b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js index 1a322d131a9..f827f068bb3 100644 --- a/test/spec/modules/bidwatchAnalyticsAdapter_spec.js +++ b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js @@ -106,7 +106,8 @@ describe('BidWatch Analytics', function () { 'gdprConsent': { 'consentString': 'CONSENT', 'gdprApplies': true, - 'apiVersion': 2 + 'apiVersion': 2, + 'vendorData': 'a lot of borring stuff', }, 'start': 1647424261189 }, @@ -281,7 +282,22 @@ describe('BidWatch Analytics', function () { }); events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeout); events.emit(constants.EVENTS.AUCTION_END, auctionEnd); + expect(server.requests.length).to.equal(1); + let message = JSON.parse(server.requests[0].requestBody); + expect(message).to.have.property('auctionEnd').exist; + expect(message.auctionEnd).to.have.lengthOf(1); + expect(message.auctionEnd[0]).to.have.property('bidsReceived').and.to.have.lengthOf(1); + expect(message.auctionEnd[0].bidsReceived[0]).to.have.property('ad'); + expect(message.auctionEnd[0].bidsReceived[0].ad).to.equal('emptied'); + expect(message.auctionEnd[0]).to.have.property('bidderRequests').and.to.have.lengthOf(1); + expect(message.auctionEnd[0].bidderRequests[0]).to.have.property('gdprConsent'); + expect(message.auctionEnd[0].bidderRequests[0].gdprConsent).to.have.property('vendorData'); + expect(message.auctionEnd[0].bidderRequests[0].gdprConsent.vendorData).to.equal('emptied'); events.emit(constants.EVENTS.BID_WON, bidWon); + expect(server.requests.length).to.equal(2); + message = JSON.parse(server.requests[1].requestBody); + expect(message).to.have.property('ad').and.to.equal('emptied'); + expect(message).to.have.property('cpmIncrement').and.to.equal(27.4276); sinon.assert.callCount(bidwatchAnalytics.track, 3); }); }); From b9fe9a2991e8383b6c121e19fe1d859fbd80365f Mon Sep 17 00:00:00 2001 From: AndBeyondMediaHB <107686862+AndBeyondMediaHB@users.noreply.github.com> Date: Wed, 3 Aug 2022 19:42:33 +0300 Subject: [PATCH 018/246] BeyondMedia Bid Adapter: initial bid adapter release (#8682) * add andBeyondMedia adapter * update bidderCode --- modules/andBeyondMediaBidAdapter.js | 202 +++++++++ modules/andBeyondMediaBidAdapter.md | 79 ++++ .../modules/andBeyondMediaBidAdapter_spec.js | 400 ++++++++++++++++++ 3 files changed, 681 insertions(+) create mode 100644 modules/andBeyondMediaBidAdapter.js create mode 100644 modules/andBeyondMediaBidAdapter.md create mode 100644 test/spec/modules/andBeyondMediaBidAdapter_spec.js diff --git a/modules/andBeyondMediaBidAdapter.js b/modules/andBeyondMediaBidAdapter.js new file mode 100644 index 00000000000..57c141dc2aa --- /dev/null +++ b/modules/andBeyondMediaBidAdapter.js @@ -0,0 +1,202 @@ +import { isFn, deepAccess, logMessage, logError } from '../src/utils.js'; + +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; + +const BIDDER_CODE = 'beyondmedia'; +const AD_URL = 'https://backend.andbeyond.media/pbjs'; +const SYNC_URL = 'https://cookies.andbeyond.media'; + +function isBidResponseValid(bid) { + if (!bid.requestId || !bid.cpm || !bid.creativeId || !bid.ttl || !bid.currency) { + return false; + } + + switch (bid.mediaType) { + case BANNER: + return Boolean(bid.width && bid.height && bid.ad); + case VIDEO: + return Boolean(bid.vastUrl || bid.vastXml); + case NATIVE: + return Boolean(bid.native && bid.native.impressionTrackers && bid.native.impressionTrackers.length); + default: + return false; + } +} + +function getPlacementReqData(bid) { + const { params, bidId, mediaTypes } = bid; + const schain = bid.schain || {}; + const { placementId } = params; + const bidfloor = getBidFloor(bid); + + const placement = { + bidId, + schain, + bidfloor + }; + + placement.placementId = placementId; + placement.type = 'publisher'; + + if (mediaTypes && mediaTypes[BANNER]) { + placement.adFormat = BANNER; + placement.sizes = mediaTypes[BANNER].sizes; + } else if (mediaTypes && mediaTypes[VIDEO]) { + placement.adFormat = VIDEO; + placement.playerSize = mediaTypes[VIDEO].playerSize; + placement.minduration = mediaTypes[VIDEO].minduration; + placement.maxduration = mediaTypes[VIDEO].maxduration; + placement.mimes = mediaTypes[VIDEO].mimes; + placement.protocols = mediaTypes[VIDEO].protocols; + placement.startdelay = mediaTypes[VIDEO].startdelay; + placement.placement = mediaTypes[VIDEO].placement; + placement.skip = mediaTypes[VIDEO].skip; + placement.skipafter = mediaTypes[VIDEO].skipafter; + placement.minbitrate = mediaTypes[VIDEO].minbitrate; + placement.maxbitrate = mediaTypes[VIDEO].maxbitrate; + placement.delivery = mediaTypes[VIDEO].delivery; + placement.playbackmethod = mediaTypes[VIDEO].playbackmethod; + placement.api = mediaTypes[VIDEO].api; + placement.linearity = mediaTypes[VIDEO].linearity; + } else if (mediaTypes && mediaTypes[NATIVE]) { + placement.native = mediaTypes[NATIVE]; + placement.adFormat = NATIVE; + } + + return placement; +} + +function getBidFloor(bid) { + if (!isFn(bid.getFloor)) { + return deepAccess(bid, 'params.bidfloor', 0); + } + + try { + const bidFloor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*', + }); + return bidFloor.floor; + } catch (err) { + logError(err); + return 0; + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid = {}) => { + const { params, bidId, mediaTypes } = bid; + let valid = Boolean(bidId && params && params.placementId); + + if (mediaTypes && mediaTypes[BANNER]) { + valid = valid && Boolean(mediaTypes[BANNER] && mediaTypes[BANNER].sizes); + } else if (mediaTypes && mediaTypes[VIDEO]) { + valid = valid && Boolean(mediaTypes[VIDEO] && mediaTypes[VIDEO].playerSize); + } else if (mediaTypes && mediaTypes[NATIVE]) { + valid = valid && Boolean(mediaTypes[NATIVE]); + } + + return valid; + }, + + buildRequests: (validBidRequests = [], bidderRequest = {}) => { + let deviceWidth = 0; + let deviceHeight = 0; + + let winLocation; + try { + const winTop = window.top; + deviceWidth = winTop.screen.width; + deviceHeight = winTop.screen.height; + winLocation = winTop.location; + } catch (e) { + logMessage(e); + winLocation = window.location; + } + + const refferUrl = bidderRequest.refererInfo && bidderRequest.refererInfo.page; + let refferLocation; + try { + refferLocation = refferUrl && new URL(refferUrl); + } catch (e) { + logMessage(e); + } + + let location = refferLocation || winLocation; + const language = (navigator && navigator.language) ? navigator.language.split('-')[0] : ''; + const host = location.host; + const page = location.pathname; + const secure = location.protocol === 'https:' ? 1 : 0; + const placements = []; + const request = { + deviceWidth, + deviceHeight, + language, + secure, + host, + page, + placements, + coppa: config.getConfig('coppa') === true ? 1 : 0, + ccpa: bidderRequest.uspConsent || undefined, + gdpr: bidderRequest.gdprConsent || undefined, + tmax: config.getConfig('bidderTimeout') + }; + + const len = validBidRequests.length; + for (let i = 0; i < len; i++) { + const bid = validBidRequests[i]; + placements.push(getPlacementReqData(bid)); + } + + return { + method: 'POST', + url: AD_URL, + data: request + }; + }, + + interpretResponse: (serverResponse) => { + let response = []; + for (let i = 0; i < serverResponse.body.length; i++) { + let resItem = serverResponse.body[i]; + if (isBidResponseValid(resItem)) { + const advertiserDomains = resItem.adomain && resItem.adomain.length ? resItem.adomain : []; + resItem.meta = { ...resItem.meta, advertiserDomains }; + + response.push(resItem); + } + } + return response; + }, + + getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { + let syncType = syncOptions.iframeEnabled ? 'iframe' : 'image'; + let syncUrl = SYNC_URL + `/${syncType}?pbjs=1`; + if (gdprConsent && gdprConsent.consentString) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + syncUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + syncUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; + } + } + if (uspConsent && uspConsent.consentString) { + syncUrl += `&ccpa_consent=${uspConsent.consentString}`; + } + + const coppa = config.getConfig('coppa') ? 1 : 0; + syncUrl += `&coppa=${coppa}`; + + return [{ + type: syncType, + url: syncUrl + }]; + } +}; + +registerBidder(spec); diff --git a/modules/andBeyondMediaBidAdapter.md b/modules/andBeyondMediaBidAdapter.md new file mode 100644 index 00000000000..ebb44a7779c --- /dev/null +++ b/modules/andBeyondMediaBidAdapter.md @@ -0,0 +1,79 @@ +# Overview + +``` +Module Name: AndBeyond.Media Bidder Adapter +Module Type: AndBeyond.Media Bidder Adapter +Maintainer: sysengg@andbeyond.media +``` + +# Description + +Connects to AndBeyond.Media exchange for bids. +AndBeyond.Media bid adapter supports Banner, Video (instream and outstream) and Native. + +# Test Parameters +``` + var adUnits = [ + // Will return static test banner + { + code: 'adunit1', + mediaTypes: { + banner: { + sizes: [ [300, 250], [320, 50] ], + } + }, + bids: [ + { + bidder: 'beyondmedia', + params: { + placementId: 'testBanner', + } + } + ] + }, + { + code: 'addunit2', + mediaTypes: { + video: { + playerSize: [ [640, 480] ], + context: 'instream', + minduration: 5, + maxduration: 60, + } + }, + bids: [ + { + bidder: 'beyondmedia', + params: { + placementId: 'testVideo', + } + } + ] + }, + { + code: 'addunit3', + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids: [ + { + bidder: 'beyondmedia', + params: { + placementId: 'testNative', + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/test/spec/modules/andBeyondMediaBidAdapter_spec.js b/test/spec/modules/andBeyondMediaBidAdapter_spec.js new file mode 100644 index 00000000000..b739bec288e --- /dev/null +++ b/test/spec/modules/andBeyondMediaBidAdapter_spec.js @@ -0,0 +1,400 @@ +import { expect } from 'chai'; +import { spec } from '../../../modules/andBeyondMediaBidAdapter.js'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; +import { getUniqueIdentifierStr } from '../../../src/utils.js'; + +const bidder = 'beyondmedia' + +describe('AndBeyondMediaBidAdapter', function () { + const bids = [ + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + placementId: 'testBanner', + } + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [VIDEO]: { + playerSize: [[300, 300]], + minduration: 5, + maxduration: 60 + } + }, + params: { + placementId: 'testVideo', + } + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [NATIVE]: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + } + }, + params: { + placementId: 'testNative', + } + } + ]; + + const invalidBid = { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + + } + } + + const bidderRequest = { + uspConsent: '1---', + gdprConsent: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', + refererInfo: { + referer: 'https://test.com' + } + }; + + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and key parameters present', function () { + expect(spec.isBidRequestValid(bids[0])).to.be.true; + expect(spec.isBidRequestValid(bids[1])).to.be.true; + expect(spec.isBidRequestValid(bids[2])).to.be.true; + }); + it('Should return false if at least one of parameters is not present', function () { + expect(spec.isBidRequestValid(invalidBid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests(bids, bidderRequest); + + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('https://backend.andbeyond.media/pbjs'); + }); + + it('Returns general data valid', function () { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', + 'deviceHeight', + 'language', + 'secure', + 'host', + 'page', + 'placements', + 'coppa', + 'ccpa', + 'gdpr', + 'tmax' + ); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.language).to.be.a('string'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + expect(data.coppa).to.be.a('number'); + expect(data.gdpr).to.be.a('string'); + expect(data.ccpa).to.be.a('string'); + expect(data.tmax).to.be.a('number'); + expect(data.placements).to.have.lengthOf(3); + }); + + it('Returns valid placements', function () { + const { placements } = serverRequest.data; + for (let i = 0, len = placements.length; i < len; i++) { + const placement = placements[i]; + expect(placement.placementId).to.be.oneOf(['testBanner', 'testVideo', 'testNative']); + expect(placement.adFormat).to.be.oneOf([BANNER, VIDEO, NATIVE]); + expect(placement.bidId).to.be.a('string'); + expect(placement.schain).to.be.an('object'); + expect(placement.bidfloor).to.exist.and.to.equal(0); + expect(placement.type).to.exist.and.to.equal('publisher'); + + if (placement.adFormat === BANNER) { + expect(placement.sizes).to.be.an('array'); + } + switch (placement.adFormat) { + case BANNER: + expect(placement.sizes).to.be.an('array'); + break; + case VIDEO: + expect(placement.playerSize).to.be.an('array'); + expect(placement.minduration).to.be.an('number'); + expect(placement.maxduration).to.be.an('number'); + break; + case NATIVE: + expect(placement.native).to.be.an('object'); + break; + } + } + }); + + it('Returns data with gdprConsent and without uspConsent', function () { + delete bidderRequest.uspConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + let data = serverRequest.data; + expect(data.gdpr).to.exist; + expect(data.gdpr).to.be.a('string'); + expect(data.gdpr).to.equal(bidderRequest.gdprConsent); + expect(data.ccpa).to.not.exist; + delete bidderRequest.gdprConsent; + }); + + it('Returns data with uspConsent and without gdprConsent', function () { + bidderRequest.uspConsent = '1---'; + delete bidderRequest.gdprConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + let data = serverRequest.data; + expect(data.ccpa).to.exist; + expect(data.ccpa).to.be.a('string'); + expect(data.ccpa).to.equal(bidderRequest.uspConsent); + expect(data.gdpr).to.not.exist; + }); + + it('Returns empty data if no valid requests are passed', function () { + serverRequest = spec.buildRequests([], bidderRequest); + let data = serverRequest.data; + expect(data.placements).to.be.an('array').that.is.empty; + }); + }); + + describe('interpretResponse', function () { + it('Should interpret banner response', function () { + const banner = { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let bannerResponses = spec.interpretResponse(banner); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + let dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal(banner.body[0].requestId); + expect(dataItem.cpm).to.equal(banner.body[0].cpm); + expect(dataItem.width).to.equal(banner.body[0].width); + expect(dataItem.height).to.equal(banner.body[0].height); + expect(dataItem.ad).to.equal(banner.body[0].ad); + expect(dataItem.ttl).to.equal(banner.body[0].ttl); + expect(dataItem.creativeId).to.equal(banner.body[0].creativeId); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal(banner.body[0].currency); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret video response', function () { + const video = { + body: [{ + vastUrl: 'test.com', + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let videoResponses = spec.interpretResponse(video); + expect(videoResponses).to.be.an('array').that.is.not.empty; + + let dataItem = videoResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.5); + expect(dataItem.vastUrl).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret native response', function () { + const native = { + body: [{ + mediaType: 'native', + native: { + clickUrl: 'test.com', + title: 'Test', + image: 'test.com', + impressionTrackers: ['test.com'], + }, + ttl: 120, + cpm: 0.4, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let nativeResponses = spec.interpretResponse(native); + expect(nativeResponses).to.be.an('array').that.is.not.empty; + + let dataItem = nativeResponses[0]; + expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native', 'meta'); + expect(dataItem.native).to.have.keys('clickUrl', 'impressionTrackers', 'title', 'image') + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.native.clickUrl).to.equal('test.com'); + expect(dataItem.native.title).to.equal('Test'); + expect(dataItem.native.image).to.equal('test.com'); + expect(dataItem.native.impressionTrackers).to.be.an('array').that.is.not.empty; + expect(dataItem.native.impressionTrackers[0]).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should return an empty array if invalid banner response is passed', function () { + const invBanner = { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + + let serverResponses = spec.interpretResponse(invBanner); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid video response is passed', function () { + const invVideo = { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invVideo); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid native response is passed', function () { + const invNative = { + body: [{ + mediaType: 'native', + clickUrl: 'test.com', + title: 'Test', + impressionTrackers: ['test.com'], + ttl: 120, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + let serverResponses = spec.interpretResponse(invNative); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid response is passed', function () { + const invalid = { + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invalid); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + + describe('getUserSyncs', function() { + it('Should return array of objects with proper sync config , include GDPR', function() { + const syncData = spec.getUserSyncs({}, {}, { + consentString: 'ALL', + gdprApplies: true, + }, {}); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://cookies.andbeyond.media/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') + }); + it('Should return array of objects with proper sync config , include CCPA', function() { + const syncData = spec.getUserSyncs({}, {}, {}, { + consentString: '1---' + }); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://cookies.andbeyond.media/image?pbjs=1&ccpa_consent=1---&coppa=0') + }); + }); +}); From 7792685ad4787357058eacbd103478964f3877e3 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Wed, 3 Aug 2022 18:16:19 +0000 Subject: [PATCH 019/246] Prebid 7.9.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index c29ddad3673..1033d517bad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.9.0-pre", + "version": "7.9.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 3c6622adf9b..772023d54fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.9.0-pre", + "version": "7.9.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 53c5f4ddc44e6a190cfb6d2cef85c9886e5c7331 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Wed, 3 Aug 2022 18:16:20 +0000 Subject: [PATCH 020/246] Increment version to 7.10.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1033d517bad..a8d1b0f9097 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.9.0", + "version": "7.10.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 772023d54fb..91a41436d32 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.9.0", + "version": "7.10.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From db2a333f2c521824193771fc302ec64f0b1e1118 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Wed, 3 Aug 2022 12:39:42 -0700 Subject: [PATCH 021/246] Prebid core: improve library support; make AnalyticsAdapter a library (#8599) * Automatically resolve libraries and their dependencies * Move AnalyticsAdapter under libraries --- .gitignore | 2 +- allowedModules.js | 3 + gulpHelpers.js | 77 ++++++------------ gulpfile.js | 9 ++- libraries/LIBRARIES.md | 5 ++ .../analyticsAdapter}/AnalyticsAdapter.js | 11 ++- .../analyticsAdapter/examples}/example.js | 2 +- .../analyticsAdapter/examples}/example2.js | 2 +- .../examples}/libraries/example.js | 0 .../examples}/libraries/example2.js | 0 modules/.submodules.json | 12 --- modules/adWMGAnalyticsAdapter.js | 2 +- modules/adagioAnalyticsAdapter.js | 2 +- modules/adkernelAdnAnalyticsAdapter.js | 2 +- modules/adlooxAnalyticsAdapter.js | 2 +- modules/adomikAnalyticsAdapter.js | 2 +- modules/adxcgAnalyticsAdapter.js | 2 +- modules/adxpremiumAnalyticsAdapter.js | 2 +- modules/appierAnalyticsAdapter.js | 2 +- modules/atsAnalyticsAdapter.js | 2 +- modules/bidwatchAnalyticsAdapter.js | 2 +- modules/byDataAnalyticsAdapter.js | 2 +- modules/concertAnalyticsAdapter.js | 2 +- modules/datablocksAnalyticsAdapter.js | 2 +- modules/eplanningAnalyticsAdapter.js | 2 +- modules/fintezaAnalyticsAdapter.js | 2 +- modules/hadronAnalyticsAdapter.js | 2 +- modules/id5AnalyticsAdapter.js | 2 +- modules/invisiblyAnalyticsAdapter.js | 2 +- modules/kargoAnalyticsAdapter.js | 2 +- modules/konduitAnalyticsAdapter.js | 2 +- modules/livewrappedAnalyticsAdapter.js | 2 +- modules/liveyieldAnalyticsAdapter.js | 2 +- modules/malltvAnalyticsAdapter.js | 2 +- modules/marsmediaAnalyticsAdapter.js | 2 +- modules/medianetAnalyticsAdapter.js | 2 +- modules/ooloAnalyticsAdapter.js | 2 +- modules/openxAnalyticsAdapter.js | 2 +- modules/optimonAnalyticsAdapter.js | 2 +- modules/pianoDmpAnalyticsAdapter.js | 2 +- modules/prebidmanagerAnalyticsAdapter.js | 2 +- modules/pubmaticAnalyticsAdapter.js | 2 +- modules/pubperfAnalyticsAdapter.js | 2 +- modules/pubstackAnalyticsAdapter.js | 2 +- modules/pubwiseAnalyticsAdapter.js | 2 +- modules/pubxaiAnalyticsAdapter.js | 2 +- modules/pulsepointAnalyticsAdapter.js | 2 +- modules/realvuAnalyticsAdapter.js | 2 +- modules/relevantAnalyticsAdapter.js | 2 +- modules/rivrAnalyticsAdapter.js | 2 +- modules/roxotAnalyticsAdapter.js | 2 +- modules/rubiconAnalyticsAdapter.js | 2 +- modules/scaleableAnalyticsAdapter.js | 2 +- modules/sharethroughAnalyticsAdapter.js | 2 +- modules/sigmoidAnalyticsAdapter.js | 2 +- modules/sonobiAnalyticsAdapter.js | 2 +- modules/sovrnAnalyticsAdapter.js | 2 +- modules/staqAnalyticsAdapter.js | 2 +- modules/terceptAnalyticsAdapter.js | 2 +- modules/ucfunnelAnalyticsAdapter.js | 2 +- modules/yieldoneAnalyticsAdapter.js | 2 +- modules/yuktamediaAnalyticsAdapter.js | 2 +- modules/zeta_global_sspAnalyticsAdapter.js | 2 +- package-lock.json | 79 +++++++++++++++++++ package.json | 1 + test/mocks/analyticsStub.js | 2 +- test/spec/AnalyticsAdapter_spec.js | 2 +- webpack.conf.js | 58 +++++++++++--- 68 files changed, 227 insertions(+), 142 deletions(-) create mode 100644 libraries/LIBRARIES.md rename {src => libraries/analyticsAdapter}/AnalyticsAdapter.js (95%) rename {src/adapters/analytics => libraries/analyticsAdapter/examples}/example.js (85%) rename {src/adapters/analytics => libraries/analyticsAdapter/examples}/example2.js (92%) rename {src/adapters/analytics => libraries/analyticsAdapter/examples}/libraries/example.js (100%) rename {src/adapters/analytics => libraries/analyticsAdapter/examples}/libraries/example2.js (100%) diff --git a/.gitignore b/.gitignore index c0452b7b3d0..e5f000dd4d5 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,7 @@ selenium*.log integrationExamples/gpt/gpt.html integrationExamples/gpt/*-test.html integrationExamples/implementations/ -src/adapters/analytics/libraries +libraries/analyticsAdapter/examples/libraries # Coverage reports build/coverage/ diff --git a/allowedModules.js b/allowedModules.js index be9a2dc2abf..3e6e3039fa2 100644 --- a/allowedModules.js +++ b/allowedModules.js @@ -15,5 +15,8 @@ module.exports = { 'just-clone', 'dlv', 'dset' + ], + 'libraries': [ + ...sharedWhiteList // empty for now, but keep it to enable linting ] }; diff --git a/gulpHelpers.js b/gulpHelpers.js index 60e2fab0df2..1eec08b7a3e 100644 --- a/gulpHelpers.js +++ b/gulpHelpers.js @@ -6,12 +6,9 @@ const MANIFEST = 'package.json'; const through = require('through2'); const _ = require('lodash'); const gutil = require('gulp-util'); -const dependencyMap = require('./modules/.submodules.json'); -const submodules = dependencyMap.parentModules; -const libraries = dependencyMap.libraries; +const submodules = require('./modules/.submodules.json').parentModules; const MODULE_PATH = './modules'; -const LIBRARY_PATH = './libraries'; const BUILD_PATH = './build/dist'; const DEV_PATH = './build/dev'; const ANALYTICS_PATH = '../analytics'; @@ -71,68 +68,34 @@ module.exports = { } }); - Object.keys(libraries).forEach(library => { - if (!modules.includes(library) && modules.some(module => libraries[library].dependants.includes(module))) { - modules.unshift(library); - } - }); - return modules; }, - getParentLibraries(moduleName) { - const libraryNames = []; - Object.keys(libraries).forEach(libraryName => { - const library = libraries[libraryName]; - if (library.dependants.includes(moduleName)) { - libraryNames.push(libraryName); - } - }); - return libraryNames; - }, - getLibraryFiles(name) { - const library = libraries[name]; - const files = library.files.map((file) => path.resolve('./libraries/', name, file)) - return files; - }, - isLibrary(name) { - return !!libraries[name]; - }, getModules: _.memoize(function(externalModules) { externalModules = externalModules || []; var internalModules; try { - var getInternalModules = function(absolutePath) { - return fs.readdirSync(absolutePath) - .filter(file => (/^[^\.]+(\.js)?$/).test(file)) - .reduce((memo, file) => { - var moduleName = file.split(new RegExp('[.\\' + path.sep + ']'))[0]; - var modulePath = path.join(absolutePath, file); - if (fs.lstatSync(modulePath).isDirectory()) { - modulePath = path.join(modulePath, 'index.js') - } - if (fs.existsSync(modulePath)) { - memo[modulePath] = moduleName; - } - return memo; - }, {}); - }; - var absoluteModulePath = path.join(__dirname, MODULE_PATH); - var absoluteLibraryPath = path.join(__dirname, LIBRARY_PATH); - - internalModules = getInternalModules(absoluteModulePath); - var internalLibraries = getInternalModules(absoluteLibraryPath); - Object.assign(internalModules, internalLibraries); + internalModules = fs.readdirSync(absoluteModulePath) + .filter(file => (/^[^\.]+(\.js)?$/).test(file)) + .reduce((memo, file) => { + var moduleName = file.split(new RegExp('[.\\' + path.sep + ']'))[0]; + var modulePath = path.join(absoluteModulePath, file); + if (fs.lstatSync(modulePath).isDirectory()) { + modulePath = path.join(modulePath, 'index.js') + } + if (fs.existsSync(modulePath)) { + memo[modulePath] = moduleName; + } + return memo; + }, {}); } catch (err) { internalModules = {}; } return Object.assign(externalModules.reduce((memo, module) => { try { // prefer internal project modules before looking at project dependencies - var modulePath = require.resolve(module, {paths: [MODULE_PATH, LIBRARY_PATH]}); - if (modulePath === '') { - modulePath = require.resolve(module); - } + var modulePath = require.resolve(module, {paths: ['./modules']}); + if (modulePath === '') modulePath = require.resolve(module); memo[modulePath] = module; } catch (err) { @@ -142,16 +105,20 @@ module.exports = { }, internalModules)); }), + getBuiltPath(dev, assetPath) { + return path.join(__dirname, dev ? DEV_PATH : BUILD_PATH, assetPath) + }, + getBuiltModules: function(dev, externalModules) { var modules = this.getModuleNames(externalModules); if (Array.isArray(externalModules)) { modules = _.intersection(modules, externalModules); } - return modules.map(name => path.join(__dirname, dev ? DEV_PATH : BUILD_PATH, name + '.js')); + return modules.map(name => this.getBuiltPath(dev, name + '.js')); }, getBuiltPrebidCoreFile: function(dev) { - return path.join(__dirname, dev ? DEV_PATH : BUILD_PATH, 'prebid-core' + '.js'); + return this.getBuiltPath(dev, 'prebid-core.js') }, getModulePaths: function(externalModules) { diff --git a/gulpfile.js b/gulpfile.js index 39181c92316..3425850286d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -240,8 +240,15 @@ function bundle(dev, moduleArr) { }); } } + const coreFile = helpers.getBuiltPrebidCoreFile(dev); + const moduleFiles = helpers.getBuiltModules(dev, modules); + const depGraph = require(helpers.getBuiltPath(dev, 'dependencies.json')); + const dependencies = new Set(); + [coreFile].concat(moduleFiles).map(name => path.basename(name)).forEach((file) => { + (depGraph[file] || []).forEach((dep) => dependencies.add(helpers.getBuiltPath(dev, dep))); + }) - var entries = [helpers.getBuiltPrebidCoreFile(dev)].concat(helpers.getBuiltModules(dev, modules)); + const entries = [coreFile].concat(Array.from(dependencies), moduleFiles); var outputFileName = argv.bundleName ? argv.bundleName : 'prebid.js'; diff --git a/libraries/LIBRARIES.md b/libraries/LIBRARIES.md new file mode 100644 index 00000000000..e4d8fcc4f98 --- /dev/null +++ b/libraries/LIBRARIES.md @@ -0,0 +1,5 @@ +## Cross-module libraries + +Each directory under this one is packaged into a "library" during the build. + +Modules may share code by simply importing from a common library file; if the module is included in the build, any libraries they import from will also be included. diff --git a/src/AnalyticsAdapter.js b/libraries/analyticsAdapter/AnalyticsAdapter.js similarity index 95% rename from src/AnalyticsAdapter.js rename to libraries/analyticsAdapter/AnalyticsAdapter.js index 47f56dc054f..277a1455a75 100644 --- a/src/AnalyticsAdapter.js +++ b/libraries/analyticsAdapter/AnalyticsAdapter.js @@ -1,7 +1,7 @@ -import CONSTANTS from './constants.json'; -import { ajax } from './ajax.js'; -import { logMessage, _each } from './utils.js'; -import * as events from './events.js' +import CONSTANTS from '../../src/constants.json'; +import { ajax } from '../../src/ajax.js'; +import { logMessage, _each } from '../../src/utils.js'; +import * as events from '../../src/events.js' export const _internal = { ajax @@ -31,14 +31,13 @@ const { const ENDPOINT = 'endpoint'; const BUNDLE = 'bundle'; -var _sampled = true; - export default function AnalyticsAdapter({ url, analyticsType, global, handler }) { const _queue = []; let _eventCount = 0; let _enableCheck = true; let _handlers; let _enabled = false; + let _sampled = true; if (analyticsType === ENDPOINT || BUNDLE) { _emptyQueue(); diff --git a/src/adapters/analytics/example.js b/libraries/analyticsAdapter/examples/example.js similarity index 85% rename from src/adapters/analytics/example.js rename to libraries/analyticsAdapter/examples/example.js index 1321612b688..c6907907b23 100644 --- a/src/adapters/analytics/example.js +++ b/libraries/analyticsAdapter/examples/example.js @@ -2,7 +2,7 @@ * example.js - analytics adapter for Example Analytics Library example */ -import adapter from '../../AnalyticsAdapter.js'; +import adapter from '../AnalyticsAdapter.js'; export default adapter( { diff --git a/src/adapters/analytics/example2.js b/libraries/analyticsAdapter/examples/example2.js similarity index 92% rename from src/adapters/analytics/example2.js rename to libraries/analyticsAdapter/examples/example2.js index eadf994ce36..d95a3f54283 100644 --- a/src/adapters/analytics/example2.js +++ b/libraries/analyticsAdapter/examples/example2.js @@ -5,7 +5,7 @@ import { ajax } from '../../../src/ajax.js'; * example2.js - analytics adapter for Example2 Analytics Endpoint example */ -import adapter from '../../AnalyticsAdapter.js'; +import adapter from '../AnalyticsAdapter.js'; const url = 'https://httpbin.org/post'; const analyticsType = 'endpoint'; diff --git a/src/adapters/analytics/libraries/example.js b/libraries/analyticsAdapter/examples/libraries/example.js similarity index 100% rename from src/adapters/analytics/libraries/example.js rename to libraries/analyticsAdapter/examples/libraries/example.js diff --git a/src/adapters/analytics/libraries/example2.js b/libraries/analyticsAdapter/examples/libraries/example2.js similarity index 100% rename from src/adapters/analytics/libraries/example2.js rename to libraries/analyticsAdapter/examples/libraries/example2.js diff --git a/modules/.submodules.json b/modules/.submodules.json index b031a6bac02..72eadf15d31 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -73,17 +73,5 @@ "validationFpdModule", "topicsFpdModule" ] - }, - "libraries": { - "getOrigin": { - "files": [ - "./index.js" - ], - "dependants": [ - "ooloAnalyticsAdapter", - "resetdigitalBidAdapter", - "rtbhouseBidAdapter.js" - ] - } } } \ No newline at end of file diff --git a/modules/adWMGAnalyticsAdapter.js b/modules/adWMGAnalyticsAdapter.js index 8183187eb73..dd0340071d1 100644 --- a/modules/adWMGAnalyticsAdapter.js +++ b/modules/adWMGAnalyticsAdapter.js @@ -1,4 +1,4 @@ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import { ajax } from '../src/ajax.js'; diff --git a/modules/adagioAnalyticsAdapter.js b/modules/adagioAnalyticsAdapter.js index f929f7e660b..96f3f089cbd 100644 --- a/modules/adagioAnalyticsAdapter.js +++ b/modules/adagioAnalyticsAdapter.js @@ -2,7 +2,7 @@ * Analytics Adapter for Adagio */ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import { getWindowTop } from '../src/utils.js'; diff --git a/modules/adkernelAdnAnalyticsAdapter.js b/modules/adkernelAdnAnalyticsAdapter.js index d4aa3b035e0..901c0d2fd98 100644 --- a/modules/adkernelAdnAnalyticsAdapter.js +++ b/modules/adkernelAdnAnalyticsAdapter.js @@ -1,4 +1,4 @@ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import { logError, parseUrl, _each } from '../src/utils.js'; diff --git a/modules/adlooxAnalyticsAdapter.js b/modules/adlooxAnalyticsAdapter.js index a26a5e507e6..5db75c656bb 100644 --- a/modules/adlooxAnalyticsAdapter.js +++ b/modules/adlooxAnalyticsAdapter.js @@ -5,7 +5,7 @@ */ import adapterManager from '../src/adapterManager.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import {loadExternalScript} from '../src/adloader.js'; import {auctionManager} from '../src/auctionManager.js'; import {AUCTION_COMPLETED} from '../src/auction.js'; diff --git a/modules/adomikAnalyticsAdapter.js b/modules/adomikAnalyticsAdapter.js index 40809c59093..27a6821d9f5 100644 --- a/modules/adomikAnalyticsAdapter.js +++ b/modules/adomikAnalyticsAdapter.js @@ -1,4 +1,4 @@ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import {logInfo} from '../src/utils.js'; diff --git a/modules/adxcgAnalyticsAdapter.js b/modules/adxcgAnalyticsAdapter.js index 5cd04ce13cd..f3bb1270334 100644 --- a/modules/adxcgAnalyticsAdapter.js +++ b/modules/adxcgAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { parseSizesInput, uniques, buildUrl, logError } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; diff --git a/modules/adxpremiumAnalyticsAdapter.js b/modules/adxpremiumAnalyticsAdapter.js index 4db01024208..456180a964b 100644 --- a/modules/adxpremiumAnalyticsAdapter.js +++ b/modules/adxpremiumAnalyticsAdapter.js @@ -1,6 +1,6 @@ import {deepClone, logError, logInfo} from '../src/utils.js'; import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import {includes} from '../src/polyfill.js'; diff --git a/modules/appierAnalyticsAdapter.js b/modules/appierAnalyticsAdapter.js index afcf63ef2c1..b4081feaf92 100644 --- a/modules/appierAnalyticsAdapter.js +++ b/modules/appierAnalyticsAdapter.js @@ -1,5 +1,5 @@ import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import {getGlobal} from '../src/prebidGlobal.js'; diff --git a/modules/atsAnalyticsAdapter.js b/modules/atsAnalyticsAdapter.js index 390e06366d1..0c0227ea34a 100644 --- a/modules/atsAnalyticsAdapter.js +++ b/modules/atsAnalyticsAdapter.js @@ -1,5 +1,5 @@ import { logError, logInfo } from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adaptermanager from '../src/adapterManager.js'; import {ajax} from '../src/ajax.js'; diff --git a/modules/bidwatchAnalyticsAdapter.js b/modules/bidwatchAnalyticsAdapter.js index 138f0533238..ef63fee53d5 100644 --- a/modules/bidwatchAnalyticsAdapter.js +++ b/modules/bidwatchAnalyticsAdapter.js @@ -1,4 +1,4 @@ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import { ajax } from '../src/ajax.js'; diff --git a/modules/byDataAnalyticsAdapter.js b/modules/byDataAnalyticsAdapter.js index 1bf47cd467f..ccf57942d7a 100644 --- a/modules/byDataAnalyticsAdapter.js +++ b/modules/byDataAnalyticsAdapter.js @@ -2,7 +2,7 @@ import { deepClone, logInfo, logError } from '../src/utils.js'; import Base64 from 'crypto-js/enc-base64'; import hmacSHA512 from 'crypto-js/hmac-sha512'; import enc from 'crypto-js/enc-utf8'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import { getStorageManager } from '../src/storageManager.js'; diff --git a/modules/concertAnalyticsAdapter.js b/modules/concertAnalyticsAdapter.js index cd52a2ffabf..210d1898338 100644 --- a/modules/concertAnalyticsAdapter.js +++ b/modules/concertAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { logMessage } from '../src/utils.js'; import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; diff --git a/modules/datablocksAnalyticsAdapter.js b/modules/datablocksAnalyticsAdapter.js index 5e977155284..61933cc45e9 100644 --- a/modules/datablocksAnalyticsAdapter.js +++ b/modules/datablocksAnalyticsAdapter.js @@ -2,7 +2,7 @@ * Analytics Adapter for Datablocks */ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; var datablocksAdapter = adapter({ diff --git a/modules/eplanningAnalyticsAdapter.js b/modules/eplanningAnalyticsAdapter.js index 365f91382f7..9eb701b8ecc 100644 --- a/modules/eplanningAnalyticsAdapter.js +++ b/modules/eplanningAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { logError } from '../src/utils.js'; import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; diff --git a/modules/fintezaAnalyticsAdapter.js b/modules/fintezaAnalyticsAdapter.js index 10b59de1838..7b1b2f69364 100644 --- a/modules/fintezaAnalyticsAdapter.js +++ b/modules/fintezaAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { parseUrl, logError } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { getStorageManager } from '../src/storageManager.js'; import CONSTANTS from '../src/constants.json'; diff --git a/modules/hadronAnalyticsAdapter.js b/modules/hadronAnalyticsAdapter.js index c0e39925b4a..073dd15ea49 100644 --- a/modules/hadronAnalyticsAdapter.js +++ b/modules/hadronAnalyticsAdapter.js @@ -1,5 +1,5 @@ import { ajax } from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import * as utils from '../src/utils.js'; import CONSTANTS from '../src/constants.json'; diff --git a/modules/id5AnalyticsAdapter.js b/modules/id5AnalyticsAdapter.js index 69e303b520a..5ae04eb57ce 100644 --- a/modules/id5AnalyticsAdapter.js +++ b/modules/id5AnalyticsAdapter.js @@ -1,4 +1,4 @@ -import buildAdapter from '../src/AnalyticsAdapter.js'; +import buildAdapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import { ajax } from '../src/ajax.js'; diff --git a/modules/invisiblyAnalyticsAdapter.js b/modules/invisiblyAnalyticsAdapter.js index 1f0bbfd46c3..a4f4eba271c 100644 --- a/modules/invisiblyAnalyticsAdapter.js +++ b/modules/invisiblyAnalyticsAdapter.js @@ -2,7 +2,7 @@ * invisiblyAdapterAdapter.js - analytics adapter for Invisibly */ import { ajaxBuilder } from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { generateUUID, logInfo } from '../src/utils.js'; diff --git a/modules/kargoAnalyticsAdapter.js b/modules/kargoAnalyticsAdapter.js index 8ddf14aaa83..652e105167d 100644 --- a/modules/kargoAnalyticsAdapter.js +++ b/modules/kargoAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { logError } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; diff --git a/modules/konduitAnalyticsAdapter.js b/modules/konduitAnalyticsAdapter.js index f8a44d7cc94..a1a586b25db 100644 --- a/modules/konduitAnalyticsAdapter.js +++ b/modules/konduitAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { parseSizesInput, logError, uniques } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { targeting } from '../src/targeting.js'; import { config } from '../src/config.js'; diff --git a/modules/livewrappedAnalyticsAdapter.js b/modules/livewrappedAnalyticsAdapter.js index 1116fd99ba0..2721031948d 100644 --- a/modules/livewrappedAnalyticsAdapter.js +++ b/modules/livewrappedAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { timestamp, logInfo, getWindowTop } from '../src/utils.js'; import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import { getGlobal } from '../src/prebidGlobal.js'; diff --git a/modules/liveyieldAnalyticsAdapter.js b/modules/liveyieldAnalyticsAdapter.js index 411b76a5149..997c0eaace0 100644 --- a/modules/liveyieldAnalyticsAdapter.js +++ b/modules/liveyieldAnalyticsAdapter.js @@ -1,5 +1,5 @@ import { logError } from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; diff --git a/modules/malltvAnalyticsAdapter.js b/modules/malltvAnalyticsAdapter.js index a0e2a208bc9..af903795e49 100644 --- a/modules/malltvAnalyticsAdapter.js +++ b/modules/malltvAnalyticsAdapter.js @@ -1,5 +1,5 @@ import {ajax} from '../src/ajax.js' -import adapter from '../src/AnalyticsAdapter.js' +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js' import CONSTANTS from '../src/constants.json' import adapterManager from '../src/adapterManager.js' import {getGlobal} from '../src/prebidGlobal.js' diff --git a/modules/marsmediaAnalyticsAdapter.js b/modules/marsmediaAnalyticsAdapter.js index 12c333631a2..c86cc4dfbc2 100644 --- a/modules/marsmediaAnalyticsAdapter.js +++ b/modules/marsmediaAnalyticsAdapter.js @@ -1,5 +1,5 @@ import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; /**** diff --git a/modules/medianetAnalyticsAdapter.js b/modules/medianetAnalyticsAdapter.js index 05c18a47f94..b7abe5f56cf 100644 --- a/modules/medianetAnalyticsAdapter.js +++ b/modules/medianetAnalyticsAdapter.js @@ -11,7 +11,7 @@ import { uniques, getHighestCpm } from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import {ajax} from '../src/ajax.js'; diff --git a/modules/ooloAnalyticsAdapter.js b/modules/ooloAnalyticsAdapter.js index 6f9d5620d53..9bc140f0536 100644 --- a/modules/ooloAnalyticsAdapter.js +++ b/modules/ooloAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { _each, deepClone, pick, deepSetValue, logError, logInfo } from '../src/utils.js'; import { getOrigin } from '../libraries/getOrigin/index.js'; -import adapter from '../src/AnalyticsAdapter.js' +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js' import adapterManager from '../src/adapterManager.js' import CONSTANTS from '../src/constants.json' import { ajax } from '../src/ajax.js' diff --git a/modules/openxAnalyticsAdapter.js b/modules/openxAnalyticsAdapter.js index 89140c0aacd..7cc71fe898e 100644 --- a/modules/openxAnalyticsAdapter.js +++ b/modules/openxAnalyticsAdapter.js @@ -13,7 +13,7 @@ import { parseSizesInput, uniques } from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import {ajax} from '../src/ajax.js'; diff --git a/modules/optimonAnalyticsAdapter.js b/modules/optimonAnalyticsAdapter.js index 34b2778afc9..82bc18f605d 100644 --- a/modules/optimonAnalyticsAdapter.js +++ b/modules/optimonAnalyticsAdapter.js @@ -8,7 +8,7 @@ * */ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; const optimonAnalyticsAdapter = adapter({ diff --git a/modules/pianoDmpAnalyticsAdapter.js b/modules/pianoDmpAnalyticsAdapter.js index 08ef5406d9f..47159475b5d 100644 --- a/modules/pianoDmpAnalyticsAdapter.js +++ b/modules/pianoDmpAnalyticsAdapter.js @@ -1,4 +1,4 @@ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; const pianoDmpAnalytics = adapter({ analyticsType: 'bundle', handler: 'on' }); diff --git a/modules/prebidmanagerAnalyticsAdapter.js b/modules/prebidmanagerAnalyticsAdapter.js index 6235b10fa13..708d2b79243 100644 --- a/modules/prebidmanagerAnalyticsAdapter.js +++ b/modules/prebidmanagerAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { generateUUID, getParameterByName, logError, parseUrl, logInfo } from '../src/utils.js'; import {ajaxBuilder} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { getStorageManager } from '../src/storageManager.js'; import CONSTANTS from '../src/constants.json'; diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index 6a73838ff1d..48b570a683b 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -1,5 +1,5 @@ import { _each, pick, logWarn, isStr, isArray, logError } from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import { ajax } from '../src/ajax.js'; diff --git a/modules/pubperfAnalyticsAdapter.js b/modules/pubperfAnalyticsAdapter.js index 9282d5814c0..9ef95adb77a 100644 --- a/modules/pubperfAnalyticsAdapter.js +++ b/modules/pubperfAnalyticsAdapter.js @@ -2,7 +2,7 @@ * Analytics Adapter for Pubperf */ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { logError } from '../src/utils.js'; diff --git a/modules/pubstackAnalyticsAdapter.js b/modules/pubstackAnalyticsAdapter.js index b1da40c5b89..ef33b2d75ae 100644 --- a/modules/pubstackAnalyticsAdapter.js +++ b/modules/pubstackAnalyticsAdapter.js @@ -1,4 +1,4 @@ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; const pubstackAnalytics = adapter({ diff --git a/modules/pubwiseAnalyticsAdapter.js b/modules/pubwiseAnalyticsAdapter.js index 9b40d45542c..7976bcef58d 100644 --- a/modules/pubwiseAnalyticsAdapter.js +++ b/modules/pubwiseAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { getParameterByName, logInfo, generateUUID, debugTurnedOn } from '../src/utils.js'; import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import { getStorageManager } from '../src/storageManager.js'; diff --git a/modules/pubxaiAnalyticsAdapter.js b/modules/pubxaiAnalyticsAdapter.js index 3f35cd8ff79..bd92162b8cb 100644 --- a/modules/pubxaiAnalyticsAdapter.js +++ b/modules/pubxaiAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { deepAccess, getGptSlotInfoForAdUnitCode, parseSizesInput, getWindowLocation, buildUrl } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; diff --git a/modules/pulsepointAnalyticsAdapter.js b/modules/pulsepointAnalyticsAdapter.js index 375a817f257..999ae3dd3de 100644 --- a/modules/pulsepointAnalyticsAdapter.js +++ b/modules/pulsepointAnalyticsAdapter.js @@ -2,7 +2,7 @@ * pulsepoint.js - Analytics Adapter for PulsePoint */ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; var pulsepointAdapter = adapter({ diff --git a/modules/realvuAnalyticsAdapter.js b/modules/realvuAnalyticsAdapter.js index 832e907893c..0811c70a2f7 100644 --- a/modules/realvuAnalyticsAdapter.js +++ b/modules/realvuAnalyticsAdapter.js @@ -1,5 +1,5 @@ // RealVu Analytics Adapter -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import { getStorageManager } from '../src/storageManager.js'; diff --git a/modules/relevantAnalyticsAdapter.js b/modules/relevantAnalyticsAdapter.js index 5917262c810..7774370f7ad 100644 --- a/modules/relevantAnalyticsAdapter.js +++ b/modules/relevantAnalyticsAdapter.js @@ -1,4 +1,4 @@ -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; const relevantAnalytics = adapter({ analyticsType: 'bundle', handler: 'on' }); diff --git a/modules/rivrAnalyticsAdapter.js b/modules/rivrAnalyticsAdapter.js index 279b1b13051..cf14ff44571 100644 --- a/modules/rivrAnalyticsAdapter.js +++ b/modules/rivrAnalyticsAdapter.js @@ -1,5 +1,5 @@ import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import * as utils from '../src/utils.js'; diff --git a/modules/roxotAnalyticsAdapter.js b/modules/roxotAnalyticsAdapter.js index b11898b9ea8..1ded17f3a5b 100644 --- a/modules/roxotAnalyticsAdapter.js +++ b/modules/roxotAnalyticsAdapter.js @@ -1,5 +1,5 @@ import {deepClone, getParameterByName, logError, logInfo} from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import {includes} from '../src/polyfill.js'; diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index ca0d0c1fe65..3ad1bb26b47 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -1,5 +1,5 @@ import { generateUUID, mergeDeep, deepAccess, parseUrl, logError, pick, isEmpty, logWarn, debugTurnedOn, parseQS, getWindowLocation, isAdUnitCodeMatchingSlot, isNumber, isGptPubadsDefined, _each, deepSetValue, deepClone, logInfo } from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import { ajax } from '../src/ajax.js'; diff --git a/modules/scaleableAnalyticsAdapter.js b/modules/scaleableAnalyticsAdapter.js index d7379462e0d..46f9d45d84d 100644 --- a/modules/scaleableAnalyticsAdapter.js +++ b/modules/scaleableAnalyticsAdapter.js @@ -2,7 +2,7 @@ import { ajax } from '../src/ajax.js'; import CONSTANTS from '../src/constants.json'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { logMessage } from '../src/utils.js'; diff --git a/modules/sharethroughAnalyticsAdapter.js b/modules/sharethroughAnalyticsAdapter.js index 4f065cbca23..6502c7e3a53 100644 --- a/modules/sharethroughAnalyticsAdapter.js +++ b/modules/sharethroughAnalyticsAdapter.js @@ -1,5 +1,5 @@ import { tryAppendQueryString } from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; const emptyUrl = ''; diff --git a/modules/sigmoidAnalyticsAdapter.js b/modules/sigmoidAnalyticsAdapter.js index a0521bd5297..dc386813978 100644 --- a/modules/sigmoidAnalyticsAdapter.js +++ b/modules/sigmoidAnalyticsAdapter.js @@ -1,7 +1,7 @@ /* Sigmoid Analytics Adapter for prebid.js v1.1.0-pre Updated : 2018-03-28 */ import {includes} from '../src/polyfill.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import {getStorageManager} from '../src/storageManager.js'; diff --git a/modules/sonobiAnalyticsAdapter.js b/modules/sonobiAnalyticsAdapter.js index 0de6647149a..75d481aba9e 100644 --- a/modules/sonobiAnalyticsAdapter.js +++ b/modules/sonobiAnalyticsAdapter.js @@ -1,5 +1,5 @@ import { deepClone, logInfo, logError } from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import {ajaxBuilder} from '../src/ajax.js'; diff --git a/modules/sovrnAnalyticsAdapter.js b/modules/sovrnAnalyticsAdapter.js index 8a5b816ef43..a72c4b1a5a5 100644 --- a/modules/sovrnAnalyticsAdapter.js +++ b/modules/sovrnAnalyticsAdapter.js @@ -1,5 +1,5 @@ import {logError, timestamp} from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adaptermanager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import {ajaxBuilder} from '../src/ajax.js'; diff --git a/modules/staqAnalyticsAdapter.js b/modules/staqAnalyticsAdapter.js index b9bfe5212c6..69d23a3a604 100644 --- a/modules/staqAnalyticsAdapter.js +++ b/modules/staqAnalyticsAdapter.js @@ -1,5 +1,5 @@ import { logInfo, logError, parseUrl, _each } from '../src/utils.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import { getRefererInfo } from '../src/refererDetection.js'; diff --git a/modules/terceptAnalyticsAdapter.js b/modules/terceptAnalyticsAdapter.js index 748e512bd42..9d215f0ceda 100644 --- a/modules/terceptAnalyticsAdapter.js +++ b/modules/terceptAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { parseSizesInput, getWindowLocation, buildUrl } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; diff --git a/modules/ucfunnelAnalyticsAdapter.js b/modules/ucfunnelAnalyticsAdapter.js index 7a471a1d3b4..77fffddbaae 100644 --- a/modules/ucfunnelAnalyticsAdapter.js +++ b/modules/ucfunnelAnalyticsAdapter.js @@ -1,5 +1,5 @@ import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import {getGlobal} from '../src/prebidGlobal.js'; diff --git a/modules/yieldoneAnalyticsAdapter.js b/modules/yieldoneAnalyticsAdapter.js index 126e3504f98..0663ecb3f76 100644 --- a/modules/yieldoneAnalyticsAdapter.js +++ b/modules/yieldoneAnalyticsAdapter.js @@ -1,6 +1,6 @@ import { isArray, deepClone } from '../src/utils.js'; import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import { targeting } from '../src/targeting.js'; diff --git a/modules/yuktamediaAnalyticsAdapter.js b/modules/yuktamediaAnalyticsAdapter.js index 8ff22faa7f4..31c6daae7f6 100644 --- a/modules/yuktamediaAnalyticsAdapter.js +++ b/modules/yuktamediaAnalyticsAdapter.js @@ -1,6 +1,6 @@ import {buildUrl, generateUUID, getWindowLocation, logError, logInfo, parseSizesInput, parseUrl} from '../src/utils.js'; import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import {getStorageManager} from '../src/storageManager.js'; diff --git a/modules/zeta_global_sspAnalyticsAdapter.js b/modules/zeta_global_sspAnalyticsAdapter.js index 906e6e19cc2..ee3bb9cd5d6 100644 --- a/modules/zeta_global_sspAnalyticsAdapter.js +++ b/modules/zeta_global_sspAnalyticsAdapter.js @@ -3,7 +3,7 @@ import { ajax } from '../src/ajax.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; -import adapter from '../src/AnalyticsAdapter.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; const ZETA_GVL_ID = 833; const ADAPTER_CODE = 'zeta_global_ssp'; diff --git a/package-lock.json b/package-lock.json index a8d1b0f9097..0e221a988fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -99,6 +99,7 @@ "webdriverio": "^7.6.1", "webpack": "^5.70.0", "webpack-bundle-analyzer": "^4.5.0", + "webpack-manifest-plugin": "^5.0.0", "webpack-stream": "^7.0.0", "yargs": "^1.3.1" }, @@ -20391,6 +20392,12 @@ "node": ">=10.0.0" } }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -22907,6 +22914,44 @@ } } }, + "node_modules/webpack-manifest-plugin": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-5.0.0.tgz", + "integrity": "sha512-8RQfMAdc5Uw3QbCQ/CBV/AXqOR8mt03B6GJmRbhWopE8GzRfEpn+k0ZuWywxW+5QZsffhmFDY1J6ohqJo+eMuw==", + "dev": true, + "dependencies": { + "tapable": "^2.0.0", + "webpack-sources": "^2.2.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "peerDependencies": { + "webpack": "^5.47.0" + } + }, + "node_modules/webpack-manifest-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-manifest-plugin/node_modules/webpack-sources": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", + "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "dev": true, + "dependencies": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/webpack-merge": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", @@ -39479,6 +39524,12 @@ "debug": "~4.3.1" } }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -41524,6 +41575,34 @@ } } }, + "webpack-manifest-plugin": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-5.0.0.tgz", + "integrity": "sha512-8RQfMAdc5Uw3QbCQ/CBV/AXqOR8mt03B6GJmRbhWopE8GzRfEpn+k0ZuWywxW+5QZsffhmFDY1J6ohqJo+eMuw==", + "dev": true, + "requires": { + "tapable": "^2.0.0", + "webpack-sources": "^2.2.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "webpack-sources": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", + "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "dev": true, + "requires": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + } + } + } + }, "webpack-merge": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", diff --git a/package.json b/package.json index 91a41436d32..f3bbc12d2d5 100644 --- a/package.json +++ b/package.json @@ -108,6 +108,7 @@ "webdriverio": "^7.6.1", "webpack": "^5.70.0", "webpack-bundle-analyzer": "^4.5.0", + "webpack-manifest-plugin": "^5.0.0", "webpack-stream": "^7.0.0", "yargs": "^1.3.1" }, diff --git a/test/mocks/analyticsStub.js b/test/mocks/analyticsStub.js index 1023db882e8..8507c5a6275 100644 --- a/test/mocks/analyticsStub.js +++ b/test/mocks/analyticsStub.js @@ -1,4 +1,4 @@ -import {_internal} from '../../src/AnalyticsAdapter.js'; +import {_internal} from '../../libraries/analyticsAdapter/AnalyticsAdapter.js'; before(() => { // stub out analytics networking to avoid random events polluting the global xhr mock diff --git a/test/spec/AnalyticsAdapter_spec.js b/test/spec/AnalyticsAdapter_spec.js index cdd17ea89ad..06ab8838985 100644 --- a/test/spec/AnalyticsAdapter_spec.js +++ b/test/spec/AnalyticsAdapter_spec.js @@ -14,7 +14,7 @@ const AD_RENDER_SUCCEEDED = CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED; const AUCTION_DEBUG = CONSTANTS.EVENTS.AUCTION_DEBUG; const ADD_AD_UNITS = CONSTANTS.EVENTS.ADD_AD_UNITS; -const AnalyticsAdapter = require('src/AnalyticsAdapter').default; +const AnalyticsAdapter = require('libraries/analyticsAdapter/AnalyticsAdapter.js').default; const config = { url: 'https://localhost:9999/endpoint', analyticsType: 'endpoint' diff --git a/webpack.conf.js b/webpack.conf.js index deb8ad94e51..0ead550e446 100644 --- a/webpack.conf.js +++ b/webpack.conf.js @@ -5,10 +5,30 @@ var webpack = require('webpack'); var helpers = require('./gulpHelpers.js'); var { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); var argv = require('yargs').argv; +const fs = require('fs'); const babelConfig = require('./babelConfig.js')({disableFeatures: helpers.getDisabledFeatures(), prebidDistUrlBase: argv.distUrlBase}); +const {WebpackManifestPlugin} = require('webpack-manifest-plugin') var plugins = [ new webpack.EnvironmentPlugin({'LiveConnectMode': null}), + new WebpackManifestPlugin({ + fileName: 'dependencies.json', + generate: (seed, files) => { + const entries = new Set(); + const addEntry = entries.add.bind(entries); + + files.forEach(file => file.chunk && file.chunk._groups && file.chunk._groups.forEach(addEntry)); + + return Array.from(entries).reduce((acc, entry) => { + const name = (entry.options || {}).name || (entry.runtimeChunk || {}).name + const files = (entry.chunks || []) + .filter(chunk => chunk.name !== name) + .flatMap(chunk => [...chunk.files]) + .filter(Boolean); + return name && files.length ? {...acc, [`${name}.js`]: files} : acc + }, seed) + } + }) ]; if (argv.analyze) { @@ -44,16 +64,6 @@ module.exports = { dependOn: 'prebid-core' }; - if (helpers.isLibrary(mod)) { - const libraryFiles = helpers.getLibraryFiles(mod); - moduleEntry.import = libraryFiles || moduleEntry.import; - } - - const libraries = helpers.getParentLibraries(mod); - if (libraries.length) { - moduleEntry.dependOn = ['prebid-core'].concat(libraries); - } - entry[mod] = moduleEntry; } }); @@ -97,7 +107,33 @@ module.exports = { module: true, // do not prepend every module with 'use strict'; allow mangling of top-level locals } }) - ] + ], + splitChunks: { + chunks: 'initial', + minChunks: 1, + minSize: 0, + cacheGroups: (() => { + const libRoot = path.resolve(__dirname, 'libraries'); + const libraries = Object.fromEntries( + fs.readdirSync(libRoot) + .filter((f) => fs.lstatSync(path.resolve(libRoot, f)).isDirectory()) + .map(lib => { + const dir = path.resolve(libRoot, lib) + const def = { + name: lib, + test: (module) => { + return module.resource && module.resource.startsWith(dir) + } + } + return [lib, def]; + }) + ); + return Object.assign(libraries, { + default: false, + defaultVendors: false + }) + })() + } }, plugins }; From 301b07260123283af5c7378385550a77081f505a Mon Sep 17 00:00:00 2001 From: eknis Date: Thu, 4 Aug 2022 13:18:00 +0900 Subject: [PATCH 022/246] Intimate Merger Universal Identifier System: add imppid (#8767) * add imuidModule "imppid" * update response * fix test * update spec * add GAM ppid test for imppid --- modules/imuIdSystem.js | 66 ++++++++++++++++----------- modules/imuIdSystem.md | 1 + modules/userId/eids.js | 5 ++ test/spec/modules/eids_spec.js | 32 +++++++++++++ test/spec/modules/imuIdSystem_spec.js | 24 ++++++++-- test/spec/modules/userId_spec.js | 25 +++++++++- 6 files changed, 121 insertions(+), 32 deletions(-) diff --git a/modules/imuIdSystem.js b/modules/imuIdSystem.js index 72e81d243a3..24b11dc1667 100644 --- a/modules/imuIdSystem.js +++ b/modules/imuIdSystem.js @@ -13,21 +13,12 @@ import { getStorageManager } from '../src/storageManager.js'; export const storage = getStorageManager(); export const storageKey = '__im_uid'; +export const storagePpKey = '__im_ppid'; export const cookieKey = '_im_vid'; -export const apiUrl = 'https://audiencedata.im-apps.net/imuid/get'; +export const apiDomain = 'sync6.im-apps.net'; const storageMaxAge = 1800000; // 30 minites (30 * 60 * 1000) const cookiesMaxAge = 97200000000; // 37 months ((365 * 3 + 30) * 24 * 60 * 60 * 1000) -export function setImDataInLocalStorage(value) { - storage.setDataInLocalStorage(storageKey, value); - storage.setDataInLocalStorage(`${storageKey}_mt`, new Date(timestamp()).toUTCString()); -} - -export function removeImDataFromLocalStorage() { - storage.removeDataFromLocalStorage(storageKey); - storage.removeDataFromLocalStorage(`${storageKey}_mt`); -} - function setImDataInCookie(value) { storage.setCookie( cookieKey, @@ -37,6 +28,12 @@ function setImDataInCookie(value) { ); } +export function removeImDataFromLocalStorage() { + storage.removeDataFromLocalStorage(storageKey); + storage.removeDataFromLocalStorage(`${storageKey}_mt`); + storage.removeDataFromLocalStorage(storagePpKey); +} + export function getLocalData() { const mt = storage.getDataFromLocalStorage(`${storageKey}_mt`); let expired = true; @@ -45,17 +42,27 @@ export function getLocalData() { } return { id: storage.getDataFromLocalStorage(storageKey), + ppid: storage.getDataFromLocalStorage(storagePpKey), vid: storage.getCookie(cookieKey), expired: expired }; } +export function getApiUrl(cid, url) { + if (url) { + return `${url}?cid=${cid}`; + } + return `https://${apiDomain}/${cid}/pid`; +} + export function apiSuccessProcess(jsonResponse) { if (!jsonResponse) { return; } - if (jsonResponse.uid) { - setImDataInLocalStorage(jsonResponse.uid); + if (jsonResponse.uid && jsonResponse.ppid) { + storage.setDataInLocalStorage(storageKey, jsonResponse.uid); + storage.setDataInLocalStorage(`${storageKey}_mt`, new Date(timestamp()).toUTCString()); + storage.setDataInLocalStorage(storagePpKey, jsonResponse.ppid); if (jsonResponse.vid) { setImDataInCookie(jsonResponse.vid); } @@ -77,7 +84,11 @@ export function getApiCallback(callback) { } } if (callback && responseObj.uid) { - callback(responseObj.uid); + const callbackObj = { + imuid: responseObj.uid, + imppid: responseObj.ppid + }; + callback(callbackObj); } }, error: error => { @@ -95,13 +106,6 @@ export function callImuidApi(apiUrl) { }; } -export function getApiUrl(cid, url) { - if (url) { - return `${url}?cid=${cid}`; - } - return `${apiUrl}?cid=${cid}`; -} - /** @type {Submodule} */ export const imuIdSubmodule = { /** @@ -112,18 +116,21 @@ export const imuIdSubmodule = { /** * decode the stored id value for passing to bid requests * @function - * @returns {{imuid: string} | undefined} + * @returns {{imuid: string, imppid: string} | undefined} */ - decode(id) { - if (id && typeof id === 'string') { - return {imuid: id}; + decode(ids) { + if (ids && typeof ids === 'object') { + return { + imuid: ids.imuid, + imppid: ids.imppid + }; } return undefined; }, /** * @function * @param {SubmoduleConfig} [config] - * @returns {{id: string} | undefined | {callback:function}}} + * @returns {{id:{imuid: string, imppid: string}} | undefined | {callback:function}}} */ getId(config) { const configParams = (config && config.params) || {}; @@ -144,7 +151,12 @@ export const imuIdSubmodule = { if (localData.expired) { callImuidApi(apiUrl)(); } - return {id: localData.id}; + return { + id: { + imuid: localData.id, + imppid: localData.ppid + } + }; } }; diff --git a/modules/imuIdSystem.md b/modules/imuIdSystem.md index 81aa87ba1d4..cda7fa6528d 100644 --- a/modules/imuIdSystem.md +++ b/modules/imuIdSystem.md @@ -16,6 +16,7 @@ The following configuration parameters are available: ```javascript pbjs.setConfig({ userSync: { + ppid: 'ppid.intimatemerger.com', // GAM Publisher Provided id support userIds: [{ name: 'imuid', params: { diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 4c2e3a00107..e0f100ee660 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -277,6 +277,11 @@ export const USER_IDS_CONFIG = { atype: 3 }, + 'imppid': { + source: 'ppid.intimatemerger.com', + atype: 1 + }, + 'imuid': { source: 'intimatemerger.com', atype: 1 diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index e71058824a4..6872145710c 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -467,6 +467,38 @@ describe('eids array generation for known sub-modules', function() { }]); }); }); + + describe('imuid', function() { + it('should return the correct EID schema with imuid', function() { + const userId = { + imuid: 'testimuid' + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'intimatemerger.com', + uids: [{ + id: 'testimuid', + atype: 1 + }] + }); + }); + + it('should return the correct EID schema with imppid', function() { + const userId = { + imppid: 'imppid-value-imppid-value-imppid-value' + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'ppid.intimatemerger.com', + uids: [{ + id: 'imppid-value-imppid-value-imppid-value', + atype: 1 + }] + }); + }); + }); }); describe('Negative case', function () { diff --git a/test/spec/modules/imuIdSystem_spec.js b/test/spec/modules/imuIdSystem_spec.js index 2934a7c213b..3650302a2ed 100644 --- a/test/spec/modules/imuIdSystem_spec.js +++ b/test/spec/modules/imuIdSystem_spec.js @@ -7,6 +7,7 @@ import { callImuidApi, getApiCallback, storageKey, + storagePpKey, cookieKey, apiUrl } from 'modules/imuIdSystem.js'; @@ -48,12 +49,17 @@ describe('imuId module', function () { describe('getId()', function () { it('should return the uid when it exists in local storages', function () { getLocalStorageStub.withArgs(storageKey).returns('testUid'); + getLocalStorageStub.withArgs(storagePpKey).returns('testPpid'); const id = imuIdSubmodule.getId(configParamTestCase); - expect(id).to.be.deep.equal({id: 'testUid'}); + expect(id).to.be.deep.equal({id: { + imuid: 'testUid', + imppid: 'testPpid' + }}); }); storageTestCasesForEmpty.forEach(testCase => it('should return the callback when it not exists in local storages', function () { getLocalStorageStub.withArgs(storageKey).returns(testCase); + getLocalStorageStub.withArgs(storagePpKey).returns(testCase); const id = imuIdSubmodule.getId(configParamTestCase); expect(id).have.all.keys('callback'); })); @@ -73,7 +79,7 @@ describe('imuId module', function () { describe('getApiUrl()', function () { it('should return default url when cid only', function () { const url = getApiUrl(5126); - expect(url).to.be.equal(`${apiUrl}?cid=5126`); + expect(url).to.be.equal(`https://sync6.im-apps.net/5126/pid`); }); it('should return param url when set url', function () { @@ -84,8 +90,14 @@ describe('imuId module', function () { describe('decode()', function () { it('should return the uid when it exists in local storages', function () { - const id = imuIdSubmodule.decode('testDecode'); - expect(id).to.be.deep.equal({imuid: 'testDecode'}); + const id = imuIdSubmodule.decode({ + imppid: 'imppid-value-imppid-value-imppid-value', + imuid: 'testDecodeImPpid' + }); + expect(id).to.be.deep.equal({ + imppid: 'imppid-value-imppid-value-imppid-value', + imuid: 'testDecodeImPpid' + }); }); it('should return the undefined when decode id is not "string"', function () { @@ -97,11 +109,13 @@ describe('imuId module', function () { describe('getLocalData()', function () { it('always have the same key', function () { getLocalStorageStub.withArgs(storageKey).returns('testid'); + getLocalStorageStub.withArgs(storagePpKey).returns('imppid-value-imppid-value-imppid-value'); getCookieStub.withArgs(cookieKey).returns('testvid'); getLocalStorageStub.withArgs(`${storageKey}_mt`).returns(new Date(utils.timestamp()).toUTCString()); const localData = getLocalData(); expect(localData).to.be.deep.equal({ id: 'testid', + ppid: 'imppid-value-imppid-value-imppid-value', vid: 'testvid', expired: false }); @@ -112,6 +126,7 @@ describe('imuId module', function () { const localData = getLocalData(); expect(localData).to.be.deep.equal({ id: undefined, + ppid: undefined, vid: undefined, expired: true }); @@ -122,6 +137,7 @@ describe('imuId module', function () { it('should return the undefined when success response', function () { const res = apiSuccessProcess({ uid: 'test', + ppid: 'imppid-value-imppid-value-imppid-value', vid: 'test' }); expect(res).to.equal(undefined); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 77ba1396afb..046b3ffd321 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -46,6 +46,7 @@ import {deepintentDpesSubmodule} from 'modules/deepintentDpesIdSystem.js'; import {amxIdSubmodule} from '../../../modules/amxIdSystem.js'; import {kinessoIdSubmodule} from 'modules/kinessoIdSystem.js'; import {adqueryIdSubmodule} from 'modules/adqueryIdSystem.js'; +import {imuIdSubmodule} from 'modules/imuIdSystem.js'; import * as mockGpt from '../integration/faker/googletag.js'; import 'src/prebid.js'; import {hook} from '../../../src/hook.js'; @@ -402,7 +403,7 @@ describe('User ID', function () { it('should set googletag ppid correctly', function () { let adUnits = [getAdUnitMock()]; init(config); - setSubmoduleRegistry([amxIdSubmodule, sharedIdSystemSubmodule, identityLinkSubmodule]); + setSubmoduleRegistry([amxIdSubmodule, sharedIdSystemSubmodule, identityLinkSubmodule, imuIdSubmodule]); config.setConfig({ userSync: { @@ -411,6 +412,7 @@ describe('User ID', function () { { name: 'amxId', value: {'amxId': 'amx-id-value-amx-id-value-amx-id-value'} }, { name: 'pubCommonId', value: {'pubcid': 'pubCommon-id-value-pubCommon-id-value'} }, { name: 'identityLink', value: {'idl_env': 'identityLink-id-value-identityLink-id-value'} }, + { name: 'imuid', value: {'imppid': 'imppid-value-imppid-value-imppid-value'} }, ] } }); @@ -422,6 +424,27 @@ describe('User ID', function () { }); }); + it('should set googletag ppid correctly for imuIdSubmodule', function () { + let adUnits = [getAdUnitMock()]; + init(config); + setSubmoduleRegistry([imuIdSubmodule]); + + config.setConfig({ + userSync: { + ppid: 'ppid.intimatemerger.com', + userIds: [ + { name: 'imuid', value: {'imppid': 'imppid-value-imppid-value-imppid-value'} }, + ] + } + }); + // before ppid should not be set + expect(window.googletag._ppid).to.equal(undefined); + return expectImmediateBidHook(() => {}, {adUnits}).then(() => { + // ppid should have been set without dashes and stuff + expect(window.googletag._ppid).to.equal('imppidvalueimppidvalueimppidvalue'); + }); + }); + it('should log a warning if PPID too big or small', function () { let adUnits = [getAdUnitMock()]; From 8743babfdbe9f30740d8ee524f7fb8cdf000c3de Mon Sep 17 00:00:00 2001 From: Pgb-Criteo <92986445+Pgb-Criteo@users.noreply.github.com> Date: Thu, 4 Aug 2022 14:27:25 +0200 Subject: [PATCH 023/246] Criteo Bid Adapter: Add Coppa support (#8781) * Criteo Bid Adapter: Add support of the COPPA flag * Criteo Bid Adapter: Add support of the COPPA flag --- modules/criteoBidAdapter.js | 6 +++++- test/spec/modules/criteoBidAdapter_spec.js | 25 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 82fe74c458e..be97a709421 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -175,7 +175,8 @@ export const spec = { Object.assign(bidderRequest, { publisherExt: fpd.site?.ext, userExt: fpd.user?.ext, - ceh: config.getConfig('criteo.ceh') + ceh: config.getConfig('criteo.ceh'), + coppa: config.getConfig('coppa') }); // If publisher tag not already loaded try to get it from fast bid @@ -446,6 +447,9 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { url: context.url, ext: bidderRequest.publisherExt, }, + regs: { + coppa: bidderRequest.coppa === true ? 1 : (bidderRequest.coppa === false ? 0 : undefined) + }, slots: bidRequests.map(bidRequest => { networkId = bidRequest.params.networkId || networkId; schain = bidRequest.schain || schain; diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 58f8e1ad871..ea29d43e4c0 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -1213,6 +1213,31 @@ describe('The Criteo bidding adapter', function () { } }); }); + + it('should properly build a request when coppa flag is true', function () { + const bidRequests = []; + const bidderRequest = {}; + config.setConfig({ coppa: true }); + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.regs.coppa).to.not.be.undefined; + expect(request.data.regs.coppa).to.equal(1); + }); + + it('should properly build a request when coppa flag is false', function () { + const bidRequests = []; + const bidderRequest = {}; + config.setConfig({ coppa: false }); + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.regs.coppa).to.not.be.undefined; + expect(request.data.regs.coppa).to.equal(0); + }); + + it('should properly build a request when coppa flag is not defined', function () { + const bidRequests = []; + const bidderRequest = {}; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.regs.coppa).to.be.undefined; + }); }); describe('interpretResponse', function () { From c2036dbd9075e2eddd1ac0aeae64233e61bcd7fa Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Thu, 4 Aug 2022 05:28:45 -0700 Subject: [PATCH 024/246] ix Bid Adapter: fix LGTM trailing semi-colon (#8782) --- modules/ixBidAdapter.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 62ffba3d8dc..ce4fdcc2431 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -47,7 +47,7 @@ const PRICE_TO_DOLLAR_FACTOR = { }; const IFRAME_USER_SYNC_URL = 'https://js-sec.indexww.com/um/ixmatch.html'; const FLOOR_SOURCE = { PBJS: 'p', IX: 'x' }; -const IMG_USER_SYNC_URL = 'https://dsum.casalemedia.com/pbusermatch?origin=prebid' +const IMG_USER_SYNC_URL = 'https://dsum.casalemedia.com/pbusermatch?origin=prebid'; export const ERROR_CODES = { BID_SIZE_INVALID_FORMAT: 1, BID_SIZE_NOT_INCLUDED: 2, @@ -309,7 +309,7 @@ function bidToNativeImp(bid) { const imp = bidToImp(bid); const nativeAdUnitRef = deepAccess(bid, 'mediaTypes.native'); - const assets = [] + const assets = []; // Convert all native assets to imp object for (const [adUnitProperty, adUnitValues] of Object.entries(nativeAdUnitRef)) { @@ -350,7 +350,7 @@ function bidToNativeImp(bid) { methods: [1, 2] }], privacy: 1 - } + }; imp.native = { request: JSON.stringify(request), @@ -536,9 +536,9 @@ function parseBid(rawBid, currency, bidRequest) { bid.creativeId = rawBid.hasOwnProperty('crid') ? rawBid.crid : '-'; if (rawBid.mtype == MEDIA_TYPES.Video) { - bid.vastXml = rawBid.adm + bid.vastXml = rawBid.adm; } else if (rawBid.ext && rawBid.ext.vasturl) { - bid.vastUrl = rawBid.ext.vasturl + bid.vastUrl = rawBid.ext.vasturl; } let parsedAdm = null; @@ -559,7 +559,7 @@ function parseBid(rawBid, currency, bidRequest) { bid.mediaTypes = bidRequest.mediaTypes; bid.ttl = isValidExpiry ? rawBid.exp : VIDEO_TIME_TO_LIVE; } else if (parsedAdm && parsedAdm.native) { - bid.native = interpretNativeAdm(parsedAdm.native) + bid.native = interpretNativeAdm(parsedAdm.native); bid.width = rawBid.w ? rawBid.w : 1; bid.height = rawBid.h ? rawBid.h : 1; bid.mediaType = NATIVE; @@ -889,7 +889,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { } if (tmax) { - r.ext.ixdiag.tmax = tmax + r.ext.ixdiag.tmax = tmax; } if (config.getConfig('userSync')) { @@ -943,7 +943,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { if (gdprConsent.hasOwnProperty('addtlConsent') && gdprConsent.addtlConsent) { r.user.ext.consented_providers_settings = { consented_providers: gdprConsent.addtlConsent - } + }; } } } @@ -1061,7 +1061,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { const divId = impressions[transactionIds[adUnitIndex]].divId; if (!gpid && dfpAdUnitCode && divId) { - gpid = `${dfpAdUnitCode}#${divId}` + gpid = `${dfpAdUnitCode}#${divId}`; } if (impressionObjects.length && BANNER in impressionObjects[0]) { const { id, banner: { topframe } } = impressionObjects[0]; @@ -1071,7 +1071,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { topframe, format: impressionObjects.map(({ banner: { w, h }, ext }) => ({ w, h, ext })) }, - } + }; if (dfpAdUnitCode || gpid || tid) { _bannerImpression.ext = {}; @@ -1139,9 +1139,9 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { } // add identifiers info to ixDiag - const pbaAdSlot = impressions[transactionIds[adUnitIndex]].pbadslot - const tagId = impressions[transactionIds[adUnitIndex]].tagId - const adUnitCode = impressions[transactionIds[adUnitIndex]].adUnitCode + const pbaAdSlot = impressions[transactionIds[adUnitIndex]].pbadslot; + const tagId = impressions[transactionIds[adUnitIndex]].tagId; + const adUnitCode = impressions[transactionIds[adUnitIndex]].adUnitCode; if (pbaAdSlot || tagId || adUnitCode || divId) { const clonedRObject = deepClone(r); const requestSize = `${baseUrl}${parseQueryStringParameters({ ...payload, r: JSON.stringify(clonedRObject) })}`.length; @@ -1203,7 +1203,7 @@ function _getUserIds(bidRequest) { function buildIXDiag(validBidRequests) { var adUnitMap = validBidRequests .map(bidRequest => bidRequest.transactionId) - .filter((value, index, arr) => arr.indexOf(value) === index) + .filter((value, index, arr) => arr.indexOf(value) === index); var ixdiag = { mfu: 0, @@ -1355,7 +1355,7 @@ function createBannerImps(validBidRequest, missingBannerSizes, bannerImps) { // Create IX imps from params.size if (bannerSizeDefined) { if (!bannerImps[validBidRequest.transactionId].hasOwnProperty('ixImps')) { - bannerImps[validBidRequest.transactionId].ixImps = [] + bannerImps[validBidRequest.transactionId].ixImps = []; } bannerImps[validBidRequest.transactionId].ixImps.push(imp); } From 67cb83ca7121f5a03d41024822799389c5d878ee Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 4 Aug 2022 10:53:02 -0400 Subject: [PATCH 025/246] Various files: clean up unneeded code (#8778) * Update example2.js * Update example.js * Update config.js * Update freewheel-sspBidAdapter.js * Update browsiRtdProvider.js * Update consentManagementUsp.js * Update synacormediaBidAdapter.js * Update trustpidSystem.js * Update tappxBidAdapter.js * Update nobidBidAdapter.js * Update visxBidAdapter.js * Update remote.html * Update prebidServer_example.html * Update openxBidAdapter.js * Update fintezaAnalyticsAdapter.js * Update postbid-config.js * Update postbid-config.js * Update fintezaAnalyticsAdapter.js * Update 1plusXRtdProviderExample.html * Update prebidServer_example.html * Update blueconicRtdProvider_example.html * Update hadronRtdProvider_example.html * Update airgridRtdProvider_example.html * Update userId_example.html * Update inskin_example.html * Update serverbidServer_example.html * Update postbid-config.js --- integrationExamples/gpt/1plusXRtdProviderExample.html | 6 ++---- .../gpt/airgridRtdProvider_example.html | 5 +---- integrationExamples/gpt/amp/remote.html | 2 +- .../gpt/blueconicRtdProvider_example.html | 6 ++---- integrationExamples/gpt/hadronRtdProvider_example.html | 4 +--- integrationExamples/gpt/inskin_example.html | 4 +--- integrationExamples/gpt/prebidServer_example.html | 4 +--- integrationExamples/gpt/serverbidServer_example.html | 4 +--- integrationExamples/gpt/userId_example.html | 4 +--- integrationExamples/postbid/oas/postbid-config.js | 10 +++++----- .../analyticsAdapter/examples/libraries/example.js | 4 ++-- .../analyticsAdapter/examples/libraries/example2.js | 4 ++-- modules/browsiRtdProvider.js | 4 ++-- modules/consentManagementUsp.js | 2 +- modules/fintezaAnalyticsAdapter.js | 2 +- modules/freewheel-sspBidAdapter.js | 2 +- modules/nobidBidAdapter.js | 2 +- modules/openxBidAdapter.js | 2 -- modules/synacormediaBidAdapter.js | 2 +- modules/tappxBidAdapter.js | 4 ++-- modules/trustpidSystem.js | 2 +- modules/visxBidAdapter.js | 2 +- src/config.js | 2 +- 23 files changed, 32 insertions(+), 51 deletions(-) diff --git a/integrationExamples/gpt/1plusXRtdProviderExample.html b/integrationExamples/gpt/1plusXRtdProviderExample.html index 2eb75063df1..b2c7a0037ff 100644 --- a/integrationExamples/gpt/1plusXRtdProviderExample.html +++ b/integrationExamples/gpt/1plusXRtdProviderExample.html @@ -82,9 +82,7 @@ var gads = document.createElement('script'); gads.async = true; gads.type = 'text/javascript'; - var useSSL = 'https:' == document.location.protocol; - gads.src = (useSSL ? 'https:' : 'http:') + - '//securepubads.g.doubleclick.net/tag/js/gpt.js'; + gads.src = 'https://securepubads.g.doubleclick.net/tag/js/gpt.js'; var node = document.getElementsByTagName('script')[0]; node.parentNode.insertBefore(gads, node); })(); @@ -109,4 +107,4 @@

1plusX RTD Module for Prebid

- \ No newline at end of file + diff --git a/integrationExamples/gpt/airgridRtdProvider_example.html b/integrationExamples/gpt/airgridRtdProvider_example.html index a8fd989f682..657eae8f481 100644 --- a/integrationExamples/gpt/airgridRtdProvider_example.html +++ b/integrationExamples/gpt/airgridRtdProvider_example.html @@ -108,10 +108,7 @@ var gads = document.createElement("script"); gads.async = true; gads.type = "text/javascript"; - var useSSL = "https:" == document.location.protocol; - gads.src = - (useSSL ? "https:" : "http:") + - "//www.googletagservices.com/tag/js/gpt.js"; + gads.src = 'https://securepubads.g.doubleclick.net/tag/js/gpt.js'; var node = document.getElementsByTagName("script")[0]; node.parentNode.insertBefore(gads, node); })(); diff --git a/integrationExamples/gpt/amp/remote.html b/integrationExamples/gpt/amp/remote.html index 785d854766f..4ee2cdcb2f6 100644 --- a/integrationExamples/gpt/amp/remote.html +++ b/integrationExamples/gpt/amp/remote.html @@ -33,7 +33,7 @@ // load Prebid.js (function () { - var d = document, pbs = d.createElement("script"), pro = d.location.protocal; + var d = document, pbs = d.createElement("script"); pbs.type = "text/javascript"; pbs.src = prebidSrc; var target = document.getElementsByTagName("head")[0]; diff --git a/integrationExamples/gpt/blueconicRtdProvider_example.html b/integrationExamples/gpt/blueconicRtdProvider_example.html index e51a54b3be6..598a7569d8e 100644 --- a/integrationExamples/gpt/blueconicRtdProvider_example.html +++ b/integrationExamples/gpt/blueconicRtdProvider_example.html @@ -30,7 +30,7 @@ var pbjs = pbjs || {}; pbjs.que = pbjs.que || []; - + - \ No newline at end of file + diff --git a/libraries/analyticsAdapter/examples/libraries/example.js b/libraries/analyticsAdapter/examples/libraries/example.js index 0d758fd5513..f2bfd612193 100644 --- a/libraries/analyticsAdapter/examples/libraries/example.js +++ b/libraries/analyticsAdapter/examples/libraries/example.js @@ -31,8 +31,8 @@ events.init(); // overwrite example object and handle 'on' callbacks window[window.ExampleAnalyticsGlobalObject] = example = utils.errorless(function() { if (arguments[0] && arguments[0] === 'on') { - var eventName = arguments[1] && arguments[1]; - var args = arguments[2] && arguments[2]; + var eventName = arguments[1]; + var args = arguments[2]; if (eventName && args) { if (eventName === 'bidAdjustment') { pbjsHandlers.onBidAdjustment.apply(this, [args]); diff --git a/libraries/analyticsAdapter/examples/libraries/example2.js b/libraries/analyticsAdapter/examples/libraries/example2.js index 68e814b1417..9a7106a48e0 100644 --- a/libraries/analyticsAdapter/examples/libraries/example2.js +++ b/libraries/analyticsAdapter/examples/libraries/example2.js @@ -31,8 +31,8 @@ events.init(); // overwrite example object and handle 'on' callbacks window[window.ExampleAnalyticsGlobalObject2] = example = utils.errorless(function() { if (arguments[0] && arguments[0] === 'on') { - var eventName = arguments[1] && arguments[1]; - var args = arguments[2] && arguments[2]; + var eventName = arguments[1]; + var args = arguments[2]; if (eventName && args) { if (eventName === 'bidAdjustment') { pbjsHandlers.onBidAdjustment.apply(this, [args]); diff --git a/modules/browsiRtdProvider.js b/modules/browsiRtdProvider.js index 15f2d58010d..ffc946cbb33 100644 --- a/modules/browsiRtdProvider.js +++ b/modules/browsiRtdProvider.js @@ -93,7 +93,7 @@ export function collectData() { function waitForData(callback) { if (_browsiData) { _dataReadyCallback = null; - callback(_browsiData); + callback(); } else { _dataReadyCallback = callback; } @@ -102,7 +102,7 @@ function waitForData(callback) { export function setData(data) { _browsiData = data; if (isFn(_dataReadyCallback)) { - _dataReadyCallback(_browsiData); + _dataReadyCallback(); _dataReadyCallback = null; } } diff --git a/modules/consentManagementUsp.js b/modules/consentManagementUsp.js index 0224d6ef2c0..cde32a2f1c0 100644 --- a/modules/consentManagementUsp.js +++ b/modules/consentManagementUsp.js @@ -44,7 +44,7 @@ function lookupUspConsent({onSuccess, onError}) { let uspapiFrame; let uspapiFunction; - while (!uspapiFrame) { + while (true) { try { if (typeof f.__uspapi === 'function') { uspapiFunction = f.__uspapi; diff --git a/modules/fintezaAnalyticsAdapter.js b/modules/fintezaAnalyticsAdapter.js index 7b1b2f69364..88c5f85f15d 100644 --- a/modules/fintezaAnalyticsAdapter.js +++ b/modules/fintezaAnalyticsAdapter.js @@ -181,7 +181,7 @@ function initSession() { !checkSessionByExpires() || !checkSessionByReferer() || !checkSessionByDay()) { - sessionId = '' + timestamp + getRandAsStr(SESSION_RAND_PART); + sessionId = '' + timestamp + getRandAsStr(SESSION_RAND_PART); // lgtm [js/insecure-randomness] begin = timestamp; isNew = true; diff --git a/modules/freewheel-sspBidAdapter.js b/modules/freewheel-sspBidAdapter.js index 91453fdcf5d..38973f915b6 100644 --- a/modules/freewheel-sspBidAdapter.js +++ b/modules/freewheel-sspBidAdapter.js @@ -189,7 +189,7 @@ function formatAdHTML(bid, size) { var libUrl = ''; if (integrationType && integrationType !== 'inbanner') { libUrl = PRIMETIME_URL + getComponentId(bid.params.format) + '.min.js'; - script = getOutstreamScript(bid, size); + script = getOutstreamScript(bid); } else { libUrl = MUSTANG_URL; script = getInBannerScript(bid, size); diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index cfe18301b32..877afb08293 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -30,7 +30,7 @@ function nobidBuildRequests(bids, bidderRequest) { var serializeState = function(divIds, siteId, adunits) { var filterAdUnitsByIds = function(divIds, adUnits) { var filtered = []; - if (!divIds || !divIds.length) { + if (!divIds.length) { filtered = adUnits; } else if (adUnits) { var a = []; diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index 7bea38a2de3..251e6fb50e3 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -213,13 +213,11 @@ function getViewportDimensions(isIfr) { } catch (e) { return; } - docEl = tDoc.documentElement; body = tDoc.body; width = tWin.innerWidth || docEl.clientWidth || body.clientWidth; height = tWin.innerHeight || docEl.clientHeight || body.clientHeight; } else { - docEl = tDoc.documentElement; width = tWin.innerWidth || docEl.clientWidth; height = tWin.innerHeight || docEl.clientHeight; } diff --git a/modules/synacormediaBidAdapter.js b/modules/synacormediaBidAdapter.js index a48c1aaf55b..845d6b4b5fb 100644 --- a/modules/synacormediaBidAdapter.js +++ b/modules/synacormediaBidAdapter.js @@ -101,7 +101,7 @@ export const spec = { }); // CCPA - if (bidderRequest && bidderRequest.uspConsent) { + if (bidderRequest.uspConsent) { deepSetValue(openRtbBidRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); } diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index fd038f3104e..b8f7a5559f8 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -406,9 +406,9 @@ function buildOneRequest(validBidRequests, bidderRequest) { (typeof uuid !== 'undefined' && uuid !== null) && (typeof uuid.source == 'string' && uuid.source !== null) && (typeof uuid.uids[0].id == 'string' && uuid.uids[0].id !== null) - ) + ); - if (typeof user !== 'undefined') { user.ext.eids = eidsArr } + user.ext.eids = eidsArr; }; let regs = {}; diff --git a/modules/trustpidSystem.js b/modules/trustpidSystem.js index 27ca4bf6340..fc2bab19913 100644 --- a/modules/trustpidSystem.js +++ b/modules/trustpidSystem.js @@ -84,7 +84,7 @@ function getTrustpidFromStorage() { logInfo(`${LOG_PREFIX}: Domain acronym found: ${acronym}`); // Domain is correct in both local storage and idGraph, but no acronym is existing for the domain - if (domain && !acronym) { + if (!acronym) { return { trustpid: null, acr: null diff --git a/modules/visxBidAdapter.js b/modules/visxBidAdapter.js index cec48a83c35..ee4d9708f15 100644 --- a/modules/visxBidAdapter.js +++ b/modules/visxBidAdapter.js @@ -375,7 +375,7 @@ function _isAdSlotExists(adUnitCode) { } const gptAdSlot = getGptSlotInfoForAdUnitCode(adUnitCode); - if (gptAdSlot && gptAdSlot.divId && document.getElementById(gptAdSlot.divId)) { + if (gptAdSlot.divId && document.getElementById(gptAdSlot.divId)) { return true; } diff --git a/src/config.js b/src/config.js index a50edc09221..30ab8f35e2c 100644 --- a/src/config.js +++ b/src/config.js @@ -526,7 +526,7 @@ export function newConfig() { } const mergedConfig = Object.keys(obj).reduce((accum, key) => { - const prevConf = _getConfig(key)[key] || {}; + const prevConf = _getConfig()[key] || {}; accum[key] = mergeDeep(prevConf, obj[key]); return accum; }, {}); From 35b49996f057a962f2705205e432d2e238827f69 Mon Sep 17 00:00:00 2001 From: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Date: Thu, 4 Aug 2022 20:33:03 +0530 Subject: [PATCH 026/246] Prebid Core: Handled edge cases for allowedAlternateBidderCodes functionality (#8760) * Changed net revenue to True * handled edge cases * Empty alternateBidderCodes will reject the bids Co-authored-by: Azhar --- src/adapters/bidderFactory.js | 1 + test/spec/unit/core/bidderFactory_spec.js | 29 +++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/adapters/bidderFactory.js b/src/adapters/bidderFactory.js index e798c63d753..03103dbdcdd 100644 --- a/src/adapters/bidderFactory.js +++ b/src/adapters/bidderFactory.js @@ -260,6 +260,7 @@ export function newBidder(spec) { let allowAlternateBidderCodes = bidderSettings.get(requestBidder, 'allowAlternateBidderCodes') || false; let alternateBiddersList = bidderSettings.get(requestBidder, 'allowedAlternateBidderCodes'); if (!!responseBidder && !!requestBidder && requestBidder !== responseBidder) { + alternateBiddersList = isArray(alternateBiddersList) ? alternateBiddersList.map(val => val.trim().toLowerCase()).filter(val => !!val).filter(uniques) : alternateBiddersList; if (!allowAlternateBidderCodes || (isArray(alternateBiddersList) && (alternateBiddersList[0] !== '*' && !alternateBiddersList.includes(responseBidder)))) { return true; } diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index c040bba4eec..93cb7bd4449 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -1050,8 +1050,8 @@ describe('validate bid response: ', function () { bids1 = Object.assign({}, bids[0], { - bidderCode: 'validAlternateBidder', - adapterCode: 'knownAdapter1' + bidderCode: 'validalternatebidder', + adapterCode: 'knownadapter1' } ); logWarnSpy = sinon.spy(utils, 'logWarn'); @@ -1126,6 +1126,31 @@ describe('validate bid response: ', function () { expect(logErrorSpy.callCount).to.equal(0); }); + it('should accept the bid, when allowedAlternateBidderCodes is marked as * (with space) and allowAlternateBidderCodes flag is true', function () { + bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(true); + bidderSettingStub.withArgs(CODE, 'allowedAlternateBidderCodes').returns([' * ']); + + const bidder = newBidder(spec); + spec.interpretResponse.returns(bids1); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + + expect(addBidResponseStub.calledOnce).to.equal(true); + expect(logWarnSpy.callCount).to.equal(0); + expect(logErrorSpy.callCount).to.equal(0); + }); + + it('should not accept the bid, when allowedAlternateBidderCodes is marked as empty array and allowAlternateBidderCodes flag is true', function () { + bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(true); + bidderSettingStub.withArgs(CODE, 'allowedAlternateBidderCodes').returns([]); + + const bidder = newBidder(spec); + spec.interpretResponse.returns(bids1); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + + expect(addBidResponseStub.calledOnce).to.equal(false); + expect(logWarnSpy.callCount).to.equal(1); + }); + it('should accept the bid, when allowedAlternateBidderCodes contains bidder name and allowAlternateBidderCodes flag is true', function () { bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(true); bidderSettingStub.withArgs(CODE, 'allowedAlternateBidderCodes').returns(['validAlternateBidder']); From cdb290c501d626d9020e2c0d5ce03b1edc4785bd Mon Sep 17 00:00:00 2001 From: dzhang-criteo <87757739+dzhang-criteo@users.noreply.github.com> Date: Thu, 4 Aug 2022 19:47:23 +0200 Subject: [PATCH 027/246] Criteo Adapter: remove bid.adId (#8783) --- modules/criteoBidAdapter.js | 3 +- test/spec/modules/criteoBidAdapter_spec.js | 37 ---------------------- 2 files changed, 1 insertion(+), 39 deletions(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index be97a709421..590062c76bd 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -1,4 +1,4 @@ -import { deepAccess, getUniqueIdentifierStr, isArray, logError, logInfo, logWarn, parseUrl } from '../src/utils.js'; +import { deepAccess, isArray, logError, logInfo, logWarn, parseUrl } from '../src/utils.js'; import { loadExternalScript } from '../src/adloader.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; @@ -235,7 +235,6 @@ export const spec = { const bidId = bidRequest.bidId; const bid = { requestId: bidId, - adId: slot.bidId || getUniqueIdentifierStr(), cpm: slot.cpm, currency: slot.currency, netRevenue: true, diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index ea29d43e4c0..19e97ea85bb 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -1310,7 +1310,6 @@ describe('The Criteo bidding adapter', function () { const bids = spec.interpretResponse(response, request); expect(bids).to.have.lengthOf(1); expect(bids[0].requestId).to.equal('test-bidId'); - expect(bids[0].adId).to.equal('abc123'); expect(bids[0].cpm).to.equal(1.23); expect(bids[0].ad).to.equal('test-ad'); expect(bids[0].width).to.equal(728); @@ -1344,7 +1343,6 @@ describe('The Criteo bidding adapter', function () { const bids = spec.interpretResponse(response, request); expect(bids).to.have.lengthOf(1); expect(bids[0].requestId).to.equal('test-bidId'); - expect(bids[0].adId).to.equal('abc123'); expect(bids[0].cpm).to.equal(1.23); expect(bids[0].vastUrl).to.equal('http://test-ad'); expect(bids[0].mediaType).to.equal(VIDEO); @@ -1401,7 +1399,6 @@ describe('The Criteo bidding adapter', function () { const bids = spec.interpretResponse(response, request); expect(bids).to.have.lengthOf(1); expect(bids[0].requestId).to.equal('test-bidId'); - expect(bids[0].adId).to.equal('abc123'); expect(bids[0].cpm).to.equal(1.23); expect(bids[0].mediaType).to.equal(NATIVE); }); @@ -1520,40 +1517,6 @@ describe('The Criteo bidding adapter', function () { expect(bids[0].height).to.equal(90); }); - it('should generate unique adIds if none are returned by the endpoint', function () { - const response = { - body: { - slots: [{ - impid: 'test-requestId', - cpm: 1.23, - creative: 'test-ad', - width: 300, - height: 250, - }, { - impid: 'test-requestId', - cpm: 4.56, - creative: 'test-ad', - width: 728, - height: 90, - }], - }, - }; - const request = { - bidRequests: [{ - adUnitCode: 'test-requestId', - bidId: 'test-bidId', - sizes: [[300, 250], [728, 90]], - params: { - networkId: 456, - } - }] - }; - const bids = spec.interpretResponse(response, request); - expect(bids).to.have.lengthOf(2); - const prebidBids = bids.map(bid => Object.assign(createBid(CONSTANTS.STATUS.GOOD, request.bidRequests[0]), bid)); - expect(prebidBids[0].adId).to.not.equal(prebidBids[1].adId); - }); - [{ hasBidResponseLevelPafData: true, hasBidResponseBidLevelPafData: true, From a93a7c98adf1a3037c759069e817d8916ae60497 Mon Sep 17 00:00:00 2001 From: kapil-tuptewar <91458408+kapil-tuptewar@users.noreply.github.com> Date: Fri, 5 Aug 2022 16:32:23 +0530 Subject: [PATCH 028/246] Sending device.language in iso standard 2 characters (#8789) Co-authored-by: Kapil Tuptewar --- modules/pubmaticBidAdapter.js | 3 +++ test/spec/modules/pubmaticBidAdapter_spec.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 9ab5984860b..42c05f765bc 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -1112,6 +1112,9 @@ export const spec = { payload.device = Object.assign(payload.device, config.getConfig('device')); } + // update device.language to ISO-639-1-alpha-2 (2 character language) + payload.device.language = payload.device.language && payload.device.language.split('-')[0]; + // passing transactionId in source.tid deepSetValue(payload, 'source.tid', conf.transactionId); diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index e8bedc851c7..6e65e725024 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -1141,7 +1141,7 @@ describe('PubMatic adapter', function () { expect(data.device.dnt).to.equal((navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0); expect(data.device.h).to.equal(screen.height); expect(data.device.w).to.equal(screen.width); - expect(data.device.language).to.equal(navigator.language); + expect(data.device.language).to.equal(navigator.language.split('-')[0]); expect(data.device.newkey).to.equal('new-device-data');// additional data from config sandbox.restore(); }); From 81a9e50cc2c666630b372363d795f39b241dd9b5 Mon Sep 17 00:00:00 2001 From: Snigel <108489367+snigelweb@users.noreply.github.com> Date: Fri, 5 Aug 2022 16:04:41 +0200 Subject: [PATCH 029/246] Snigel Bid Adapter: initial adapter release (#8723) * add snigel prebid adapter, docs and tests * fix small oversight * improve user sync behavior * implement PR feedback, add support for schain, floors and user IDs --- modules/snigelBidAdapter.js | 150 +++++++++++ modules/snigelBidAdapter.md | 46 ++++ test/spec/modules/snigelBidAdapter_spec.js | 296 +++++++++++++++++++++ 3 files changed, 492 insertions(+) create mode 100644 modules/snigelBidAdapter.js create mode 100644 modules/snigelBidAdapter.md create mode 100644 test/spec/modules/snigelBidAdapter_spec.js diff --git a/modules/snigelBidAdapter.js b/modules/snigelBidAdapter.js new file mode 100644 index 00000000000..e0ec10c6ed6 --- /dev/null +++ b/modules/snigelBidAdapter.js @@ -0,0 +1,150 @@ +import {config} from '../src/config.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER} from '../src/mediaTypes.js'; +import {deepAccess, isArray, isFn, isPlainObject} from '../src/utils.js'; +import {hasPurpose1Consent} from '../src/utils/gpdr.js'; + +const BIDDER_CODE = 'snigel'; +const GVLID = 1076; +const DEFAULT_URL = 'https://adserv.snigelweb.com/bp/v1/prebid'; +const DEFAULT_TTL = 60; +const DEFAULT_CURRENCIES = ['USD']; +const FLOOR_MATCH_ALL_SIZES = '*'; + +const getConfig = config.getConfig; + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: [BANNER], + + isBidRequestValid: function (bidRequest) { + return !!bidRequest.params.placement; + }, + + buildRequests: function (bidRequests, bidderRequest) { + const gdprApplies = deepAccess(bidderRequest, 'gdprConsent.gdprApplies'); + return { + method: 'POST', + url: getEndpoint(), + data: JSON.stringify({ + id: bidderRequest.bidderRequestId, + cur: getCurrencies(), + test: getTestFlag(), + devw: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth, + devh: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight, + version: $$PREBID_GLOBAL$$.version, + gdprApplies: gdprApplies, + gdprConsentString: gdprApplies === true ? deepAccess(bidderRequest, 'gdprConsent.consentString') : undefined, + gdprConsentProv: gdprApplies === true ? deepAccess(bidderRequest, 'gdprConsent.addtlConsent') : undefined, + uspConsent: deepAccess(bidderRequest, 'uspConsent'), + coppa: getConfig('coppa'), + eids: deepAccess(bidRequests, '0.userIdAsEids'), + schain: deepAccess(bidRequests, '0.schain'), + page: getPage(bidderRequest), + placements: bidRequests.map((r) => { + return { + uuid: r.bidId, + name: r.params.placement, + sizes: r.sizes, + floor: getPriceFloor(r, BANNER, FLOOR_MATCH_ALL_SIZES), + }; + }), + }), + bidderRequest, + }; + }, + + interpretResponse: function (serverResponse) { + if (!serverResponse.body || !serverResponse.body.bids) { + return []; + } + + return serverResponse.body.bids.map((bid) => { + return { + requestId: bid.uuid, + cpm: bid.price, + creativeId: bid.crid, + currency: serverResponse.body.cur, + width: bid.width, + height: bid.height, + ad: bid.ad, + netRevenue: true, + ttl: bid.ttl || DEFAULT_TTL, + meta: bid.meta, + }; + }); + }, + + getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { + const syncUrl = getSyncUrl(responses || []); + if (syncUrl && syncOptions.iframeEnabled && hasSyncConsent(gdprConsent, uspConsent)) { + return [{type: 'iframe', url: getSyncEndpoint(syncUrl, gdprConsent)}]; + } + }, +}; + +registerBidder(spec); + +function getPage(bidderRequest) { + return ( + getConfig(`${BIDDER_CODE}.page`) || deepAccess(bidderRequest, 'refererInfo.canonicalUrl') || window.location.href + ); +} + +function getEndpoint() { + return getConfig(`${BIDDER_CODE}.url`) || DEFAULT_URL; +} + +function getTestFlag() { + return getConfig(`${BIDDER_CODE}.test`) === true; +} + +function getCurrencies() { + const currencyOverrides = getConfig(`${BIDDER_CODE}.cur`); + if (currencyOverrides !== undefined && (!isArray(currencyOverrides) || currencyOverrides.length === 0)) { + throw Error('Currency override must be an array with at least one currency'); + } + return currencyOverrides || DEFAULT_CURRENCIES; +} + +function getFloorCurrency() { + return getConfig(`${BIDDER_CODE}.floorCur`) || getCurrencies()[0]; +} + +function getPriceFloor(bidRequest, mediaType, size) { + if (isFn(bidRequest.getFloor)) { + const cur = getFloorCurrency(); + const floorInfo = bidRequest.getFloor({ + currency: cur, + mediaType: mediaType, + size: size, + }); + if (isPlainObject(floorInfo) && !isNaN(floorInfo.floor)) { + return { + cur: floorInfo.currency || cur, + value: floorInfo.floor, + }; + } + } +} + +function hasSyncConsent(gdprConsent, uspConsent) { + if (gdprConsent?.gdprApplies && !hasPurpose1Consent(gdprConsent)) { + return false; + } else if (uspConsent && uspConsent[1] === 'Y' && uspConsent[2] === 'Y') { + return false; + } else { + return true; + } +} + +function getSyncUrl(responses) { + return getConfig(`${BIDDER_CODE}.syncUrl`) || deepAccess(responses[0], 'body.syncUrl'); +} + +function getSyncEndpoint(url, gdprConsent) { + return `${url}?gdpr=${gdprConsent?.gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent( + gdprConsent?.consentString || '' + )}`; +} diff --git a/modules/snigelBidAdapter.md b/modules/snigelBidAdapter.md new file mode 100644 index 00000000000..a83e133144f --- /dev/null +++ b/modules/snigelBidAdapter.md @@ -0,0 +1,46 @@ +# Overview + +- Module name: Snigel Bid Adapter +- Module type: Bidder Adapter +- Maintainer: devops@snigel.com +- Bidder code: snigel +- Supported media types: Banner + +# Description + +Connects to Snigel demand sources for bids. + +**Note:** This bid adapter requires our ad operation experts to create an optimized setup for the desired placements on your property. +Please reach out to us [through our contact form](https://snigel.com/get-in-touch). We will reply as soon as possible. + +# Parameters + +| Name | Required | Description | Example | +| :--- | :-------- | :---------- | :------ | +| placement | Yes | Placement identifier | top_leaderboard | + +# Test + +```js +var adUnits = [ + { + code: "example", + mediaTypes: { + banner: { + sizes: [ + [970, 90], + [728, 90], + ], + }, + }, + bids: [ + { + bidder: "snigel", + params: { + placement: "prebid_test_placement", + }, + }, + ], + }, +]; +``` diff --git a/test/spec/modules/snigelBidAdapter_spec.js b/test/spec/modules/snigelBidAdapter_spec.js new file mode 100644 index 00000000000..3fc09493f03 --- /dev/null +++ b/test/spec/modules/snigelBidAdapter_spec.js @@ -0,0 +1,296 @@ +import {expect} from 'chai'; +import {spec} from 'modules/snigelBidAdapter.js'; +import {config} from 'src/config.js'; +import {isValid} from 'src/adapters/bidderFactory.js'; + +const BASE_BID_REQUEST = { + adUnitCode: 'top_leaderboard', + bidId: 'bid_test', + sizes: [ + [970, 90], + [728, 90], + ], + bidder: 'snigel', + params: {}, + requestId: 'req_test', + transactionId: 'trans_test', +}; +const makeBidRequest = function (overrides) { + return {...BASE_BID_REQUEST, ...overrides}; +}; + +const BASE_BIDDER_REQUEST = { + bidderRequestId: 'test', + refererInfo: { + canonicalUrl: 'https://localhost', + }, +}; +const makeBidderRequest = function (overrides) { + return {...BASE_BIDDER_REQUEST, ...overrides}; +}; + +const DUMMY_USP_CONSENT = '1YYN'; +const DUMMY_GDPR_CONSENT_STRING = + 'BOSSotLOSSotLAPABAENBc-AAAAgR7_______9______9uz_Gv_v_f__33e8__9v_l_7_-___u_-33d4-_1vX99yfm1-7ftr3tp_86ues2_XqK_9oIiA'; + +describe('snigelBidAdapter', function () { + describe('isBidRequestValid', function () { + it('should return false if no placement provided', function () { + expect(spec.isBidRequestValid(BASE_BID_REQUEST)).to.equal(false); + }); + + it('should return true if placement provided', function () { + const bidRequest = makeBidRequest({params: {placement: 'top_leaderboard'}}); + expect(spec.isBidRequestValid(bidRequest)).to.equal(true); + }); + }); + + describe('buildRequests', function () { + afterEach(function () { + config.resetConfig(); + }); + + it('should build a single request for every impression and its placement', function () { + const bidderRequest = Object.assign({}, BASE_BIDDER_REQUEST); + const bidRequests = [ + makeBidRequest({bidId: 'a', params: {placement: 'top_leaderboard'}}), + makeBidRequest({bidId: 'b', params: {placement: 'bottom_leaderboard'}}), + ]; + + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request).to.be.an('object'); + expect(request).to.have.property('url').and.to.equal('https://adserv.snigelweb.com/bp/v1/prebid'); + expect(request).to.have.property('method').and.to.equal('POST'); + + expect(request).to.have.property('data'); + const data = JSON.parse(request.data); + expect(data).to.have.property('id').and.to.equal('test'); + expect(data).to.have.property('cur').and.to.deep.equal(['USD']); + expect(data).to.have.property('test').and.to.equal(false); + expect(data).to.have.property('page').and.to.equal('https://localhost'); + expect(data).to.have.property('placements'); + expect(data.placements.length).to.equal(2); + expect(data.placements[0].uuid).to.equal('a'); + expect(data.placements[0].name).to.equal('top_leaderboard'); + expect(data.placements[1].uuid).to.equal('b'); + expect(data.placements[1].name).to.equal('bottom_leaderboard'); + }); + + it('should forward GDPR flag and GDPR consent string if enabled', function () { + const bidderRequest = makeBidderRequest({ + gdprConsent: { + gdprApplies: true, + consentString: DUMMY_GDPR_CONSENT_STRING, + }, + }); + + const request = spec.buildRequests([], bidderRequest); + expect(request).to.have.property('data'); + const data = JSON.parse(request.data); + expect(data).to.have.property('gdprApplies').and.to.equal(true); + expect(data).to.have.property('gdprConsentString').and.to.equal(DUMMY_GDPR_CONSENT_STRING); + }); + + it('should forward GDPR flag and no GDPR consent string if disabled', function () { + const bidderRequest = makeBidderRequest({ + gdprConsent: { + gdprApplies: false, + consentString: DUMMY_GDPR_CONSENT_STRING, + }, + }); + + const request = spec.buildRequests([], bidderRequest); + expect(request).to.have.property('data'); + const data = JSON.parse(request.data); + expect(data).to.have.property('gdprApplies').and.to.equal(false); + expect(data).to.not.have.property('gdprConsentString'); + }); + + it('should forward USP consent if set', function () { + const bidderRequest = makeBidderRequest({ + uspConsent: DUMMY_USP_CONSENT, + }); + + const request = spec.buildRequests([], bidderRequest); + expect(request).to.have.property('data'); + const data = JSON.parse(request.data); + expect(data).to.have.property('uspConsent').and.to.equal(DUMMY_USP_CONSENT); + }); + + it('should forward whether or not COPPA applies', function () { + config.setConfig({ + 'coppa': true, + }); + + const request = spec.buildRequests([], BASE_BIDDER_REQUEST); + expect(request).to.have.property('data'); + const data = JSON.parse(request.data); + expect(data).to.have.property('coppa').and.to.equal(true); + }); + }); + + describe('interpretResponse', function () { + it('should not return any bids if the request failed', function () { + expect(spec.interpretResponse({}, {})).to.be.empty; + expect(spec.interpretResponse({body: 'Some error message'}, {})).to.be.empty; + }); + + it('should not return any bids if the request did not return any bids either', function () { + expect(spec.interpretResponse({body: {bids: []}})).to.be.empty; + }); + + it('should return valid bids with additional meta information', function () { + const serverResponse = { + body: { + id: BASE_BIDDER_REQUEST.bidderRequestId, + cur: 'USD', + bids: [ + { + uuid: BASE_BID_REQUEST.bidId, + price: 0.0575, + ad: '

Test Ad

', + width: 728, + height: 90, + crid: 'test', + meta: { + advertiserDomains: ['addomain.com'], + }, + }, + ], + }, + }; + + const bids = spec.interpretResponse(serverResponse, {}); + expect(bids.length).to.equal(1); + const bid = bids[0]; + expect(isValid(BASE_BID_REQUEST.adUnitCode, bid)).to.be.true; + expect(bid).to.have.property('meta'); + expect(bid.meta).to.have.property('advertiserDomains'); + expect(bid.meta.advertiserDomains).to.be.an('array'); + expect(bid.meta.advertiserDomains.length).to.equal(1); + expect(bid.meta.advertiserDomains[0]).to.equal('addomain.com'); + }); + }); + + describe('getUserSyncs', function () { + it('should not return any user syncs if sync url does not exist in response', function () { + const response = { + body: { + id: BASE_BIDDER_REQUEST.bidderRequestId, + cur: 'USD', + bids: [], + }, + }; + const syncOptions = { + iframeEnabled: true, + }; + const gdprConsent = { + gdprApplies: false, + }; + + const syncs = spec.getUserSyncs(syncOptions, [response], gdprConsent); + expect(syncs).to.be.undefined; + }); + + it('should not return any user syncs if publisher disabled iframe-based sync', function () { + const response = { + body: { + id: BASE_BIDDER_REQUEST.bidderRequestId, + cur: 'USD', + syncUrl: 'https://somesyncurl', + bids: [], + }, + }; + const syncOptions = { + iframeEnabled: false, + }; + const gdprConsent = { + gdprApplies: false, + }; + + const syncs = spec.getUserSyncs(syncOptions, [response], gdprConsent); + expect(syncs).to.be.undefined; + }); + + it('should not return any user syncs if GDPR applies and the user did not consent to purpose one', function () { + const response = { + body: { + id: BASE_BIDDER_REQUEST.bidderRequestId, + cur: 'USD', + syncUrl: 'https://somesyncurl', + bids: [], + }, + }; + const syncOptions = { + iframeEnabled: true, + }; + const gdprConsent = { + gdprApplies: true, + vendorData: { + purpose: { + consents: {1: false}, + }, + }, + }; + + const syncs = spec.getUserSyncs(syncOptions, [response], gdprConsent); + expect(syncs).to.be.undefined; + }); + + it("should return an iframe specific to the publisher's property if all conditions are met", function () { + const response = { + body: { + id: BASE_BIDDER_REQUEST.bidderRequestId, + cur: 'USD', + syncUrl: 'https://somesyncurl', + bids: [], + }, + }; + const syncOptions = { + iframeEnabled: true, + }; + const gdprConsent = { + gdprApplies: false, + }; + + const syncs = spec.getUserSyncs(syncOptions, [response], gdprConsent); + expect(syncs).to.be.an('array').and.of.length(1); + const sync = syncs[0]; + expect(sync).to.have.property('type'); + expect(sync.type).to.equal('iframe'); + expect(sync).to.have.property('url'); + expect(sync.url).to.equal('https://somesyncurl?gdpr=0&gdpr_consent='); + }); + + it('should pass GDPR applicability and consent string as query parameters', function () { + const response = { + body: { + id: BASE_BIDDER_REQUEST.bidderRequestId, + cur: 'USD', + syncUrl: 'https://somesyncurl', + bids: [], + }, + }; + const syncOptions = { + iframeEnabled: true, + }; + const gdprConsent = { + gdprApplies: true, + consentString: DUMMY_GDPR_CONSENT_STRING, + vendorData: { + purpose: { + consents: {1: true}, + }, + }, + }; + + const syncs = spec.getUserSyncs(syncOptions, [response], gdprConsent); + expect(syncs).to.be.an('array').and.of.length(1); + const sync = syncs[0]; + expect(sync).to.have.property('type'); + expect(sync.type).to.equal('iframe'); + expect(sync).to.have.property('url'); + expect(sync.url).to.equal(`https://somesyncurl?gdpr=1&gdpr_consent=${DUMMY_GDPR_CONSENT_STRING}`); + }); + }); +}); From dd3267dcbef849ea9ddb509967f95711e9f585d0 Mon Sep 17 00:00:00 2001 From: Brian Schmidt Date: Sun, 7 Aug 2022 07:59:29 -0700 Subject: [PATCH 030/246] openxOrtb support 204 response, small bug fix (#8796) --- modules/openxOrtbBidAdapter.js | 6 +++- test/spec/modules/openxOrtbBidAdapter_spec.js | 33 ++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/modules/openxOrtbBidAdapter.js b/modules/openxOrtbBidAdapter.js index a7a8d53a624..bbe0e827b8e 100644 --- a/modules/openxOrtbBidAdapter.js +++ b/modules/openxOrtbBidAdapter.js @@ -252,6 +252,10 @@ function getFloor(bid, mediaType) { } function interpretResponse(resp, req) { + if (!resp.body) { + resp.body = {nbr: 0}; + } + // pass these from request to the responses for use in userSync if (req.data.ext) { if (req.data.ext.delDomain) { @@ -301,7 +305,7 @@ function interpretResponse(resp, req) { } if (respBody.ext && respBody.ext.paf) { - response.meta.paf = respBody.ext.paf; + response.meta.paf = Object.assign({}, respBody.ext.paf); response.meta.paf.content_id = utils.deepAccess(bid, 'ext.paf.content_id'); } diff --git a/test/spec/modules/openxOrtbBidAdapter_spec.js b/test/spec/modules/openxOrtbBidAdapter_spec.js index b3f63d9c23a..3fe9b627d04 100644 --- a/test/spec/modules/openxOrtbBidAdapter_spec.js +++ b/test/spec/modules/openxOrtbBidAdapter_spec.js @@ -952,7 +952,7 @@ describe('OpenxRtbAdapter', function () { let bidResponse; let bid; - context('when there is no response', function () { + context('when there is an nbr response', function () { let bids; beforeEach(function () { bidRequestConfigs = [{ @@ -983,6 +983,37 @@ describe('OpenxRtbAdapter', function () { }); }); + context('when there is no response', function () { + let bids; + beforeEach(function () { + bidRequestConfigs = [{ + bidder: 'openx', + params: { + unit: '12345678', + delDomain: 'test-del-domain' + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]], + }, + }, + bidId: 'test-bid-id', + bidderRequestId: 'test-bidder-request-id', + auctionId: 'test-auction-id' + }]; + + bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + + bidResponse = ''; // Unknown error + bids = spec.interpretResponse({body: bidResponse}, bidRequest); + }); + + it('should not return any bids', function () { + expect(bids.length).to.equal(0); + }); + }); + context('when there is a response, the common response properties', function () { beforeEach(function () { bidRequestConfigs = [{ From d7e7aa142598887a3b74a5e458f3dbaa290ef7a2 Mon Sep 17 00:00:00 2001 From: Alexey Stoletny Date: Mon, 8 Aug 2022 09:05:49 -0500 Subject: [PATCH 031/246] CleanIO RTD Module: support billable event (#8750) * clean.io RTD Module: send billable events * Added unit test for billable events * attempt to trigger circleci * Fixed some analytics test to expect Clean.io billableEvents too. Co-authored-by: mkikot-sigma Co-authored-by: Patrick McCann Co-authored-by: eugen-tikhonov <31205179+eugen-tikhonov@users.noreply.github.com> Co-authored-by: yevhen.tykhonov --- modules/cleanioRtdProvider.js | 29 +++++++++++++++++++ test/spec/modules/cleanioRtdProvider_spec.js | 23 +++++++++++++++ .../modules/concertAnalyticsAdapter_spec.js | 3 +- .../modules/eplanningAnalyticsAdapter_spec.js | 4 +-- .../modules/fintezaAnalyticsAdapter_spec.js | 3 +- .../modules/invisiblyAnalyticsAdapter_spec.js | 11 +++++-- .../modules/konduitAnalyticsAdapter_spec.js | 3 +- .../modules/optimonAnalyticsAdapter_spec.js | 3 +- .../modules/pianoDmpAnalyticsAdapter_spec.js | 4 +++ .../prebidmanagerAnalyticsAdapter_spec.js | 3 +- .../modules/pubperfAnalyticsAdapter_spec.js | 3 +- .../modules/pubstackAnalyticsAdapter_spec.js | 3 +- .../modules/pubwiseAnalyticsAdapter_spec.js | 6 ++-- .../modules/sigmoidAnalyticsAdapter_spec.js | 2 +- .../modules/sovrnAnalyticsAdapter_spec.js | 3 +- .../modules/yieldoneAnalyticsAdapter_spec.js | 6 +++- 16 files changed, 91 insertions(+), 18 deletions(-) diff --git a/modules/cleanioRtdProvider.js b/modules/cleanioRtdProvider.js index b9fdcef768e..a6ac3fc1317 100644 --- a/modules/cleanioRtdProvider.js +++ b/modules/cleanioRtdProvider.js @@ -8,6 +8,8 @@ import { submodule } from '../src/hook.js'; import { logError, generateUUID, insertElement } from '../src/utils.js'; +import * as events from '../src/events.js'; +import CONSTANTS from '../src/constants.json'; // ============================ MODULE STATE =============================== @@ -147,6 +149,26 @@ function readConfig(config) { } } +/** + * The function to be called upon module init + * Defined as a variable to be able to reset it naturally + */ +let startBillableEvents = function() { + // Upon clean.io submodule initialization, every winner bid is considered to be protected + // and therefore, subjected to billing + events.on(CONSTANTS.EVENTS.BID_WON, winnerBidResponse => { + events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { + vendor: 'clean.io', + billingId: generateUUID(), + type: 'impression', + // TODO: if absolutely crucial, winnerBidResponse may be used + // to track down auctionId and transactionId + // However, those seem to be of importance for Demand Managers, + // while these billable events are for publishers + }); + }); +} + // ============================ MODULE REGISTRATION =============================== /** @@ -160,6 +182,13 @@ function beforeInit() { try { readConfig(config); onModuleInit(); + + // Subscribing once to ensure no duplicate events + // in case module initialization code runs multiple times + // This should have been a part of submodule definition, but well... + // The assumption here is that in production init() will be called exactly once + startBillableEvents(); + startBillableEvents = () => {}; return true; } catch (err) { if (err instanceof ConfigError) { diff --git a/test/spec/modules/cleanioRtdProvider_spec.js b/test/spec/modules/cleanioRtdProvider_spec.js index 47c4b1b4961..8453b633906 100644 --- a/test/spec/modules/cleanioRtdProvider_spec.js +++ b/test/spec/modules/cleanioRtdProvider_spec.js @@ -1,5 +1,7 @@ import * as utils from '../../../src/utils.js'; import * as hook from '../../../src/hook.js' +import * as events from '../../../src/events.js'; +import CONSTANTS from '../../../src/constants.json'; import { __TEST__ } from '../../../modules/cleanioRtdProvider.js'; @@ -184,5 +186,26 @@ describe('clean.io RTD module', function () { onBidResponseEvent(fakeBidResponse3, {}, {}); ensurePrependToBidResponse(fakeBidResponse3); }); + + it('should send billable event per bid won event', function () { + const { init } = getModule(); + expect(init({ params: { cdnUrl: 'https://abc1234567890.cloudfront.net/script.js', protectionMode: 'full' } }, {})).to.equal(true); + + const eventCounter = { registerCleanioBillingEvent: function() {} }; + sinon.spy(eventCounter, 'registerCleanioBillingEvent'); + + events.on(CONSTANTS.EVENTS.BILLABLE_EVENT, (evt) => { + if (evt.vendor === 'clean.io') { + eventCounter.registerCleanioBillingEvent() + } + }); + + events.emit(CONSTANTS.EVENTS.BID_WON, {}); + events.emit(CONSTANTS.EVENTS.BID_WON, {}); + events.emit(CONSTANTS.EVENTS.BID_WON, {}); + events.emit(CONSTANTS.EVENTS.BID_WON, {}); + + sinon.assert.callCount(eventCounter.registerCleanioBillingEvent, 4); + }); }); }); diff --git a/test/spec/modules/concertAnalyticsAdapter_spec.js b/test/spec/modules/concertAnalyticsAdapter_spec.js index b0aad2f3156..d130aea6043 100644 --- a/test/spec/modules/concertAnalyticsAdapter_spec.js +++ b/test/spec/modules/concertAnalyticsAdapter_spec.js @@ -48,7 +48,8 @@ describe('ConcertAnalyticsAdapter', function() { sandbox.spy(concertAnalytics, 'track'); fireBidEvents(events); - sandbox.assert.callCount(concertAnalytics.track, 5); + // 5 Concert events + 1 Clean.io event + sandbox.assert.callCount(concertAnalytics.track, 6); }); it('should report data for BID_RESPONSE, BID_WON events', function() { diff --git a/test/spec/modules/eplanningAnalyticsAdapter_spec.js b/test/spec/modules/eplanningAnalyticsAdapter_spec.js index 872518f2f27..bb9e6c4fb86 100644 --- a/test/spec/modules/eplanningAnalyticsAdapter_spec.js +++ b/test/spec/modules/eplanningAnalyticsAdapter_spec.js @@ -153,8 +153,8 @@ describe('eplanning analytics adapter', function () { // Step 10 check that the host to send the ajax request is configurable via options expect(eplAnalyticsAdapter.context.host).to.equal(initOptions.host); - // Step 11 verify that we received 6 events - sinon.assert.callCount(eplAnalyticsAdapter.track, 6); + // Step 11 verify that we received 7 events (6 E-Planning events + 1 Clean.io event) + sinon.assert.callCount(eplAnalyticsAdapter.track, 7); }); }); }); diff --git a/test/spec/modules/fintezaAnalyticsAdapter_spec.js b/test/spec/modules/fintezaAnalyticsAdapter_spec.js index 76f77505105..cddffc63554 100644 --- a/test/spec/modules/fintezaAnalyticsAdapter_spec.js +++ b/test/spec/modules/fintezaAnalyticsAdapter_spec.js @@ -189,7 +189,8 @@ describe('finteza analytics adapter', function () { expect(url.search.value).to.equal(String(cpm)); expect(url.search.unit).to.equal('usd'); - sinon.assert.callCount(fntzAnalyticsAdapter.track, 1); + // 1 Finteza event + 1 Clean.io event + sinon.assert.callCount(fntzAnalyticsAdapter.track, 2); }); }); diff --git a/test/spec/modules/invisiblyAnalyticsAdapter_spec.js b/test/spec/modules/invisiblyAnalyticsAdapter_spec.js index e13b16661b0..d97b0925713 100644 --- a/test/spec/modules/invisiblyAnalyticsAdapter_spec.js +++ b/test/spec/modules/invisiblyAnalyticsAdapter_spec.js @@ -202,7 +202,9 @@ describe('Invisibly Analytics Adapter test suite', function () { events.emit(constants.EVENTS.BID_REQUESTED, MOCK.BID_REQUESTED); events.emit(constants.EVENTS.BID_RESPONSE, MOCK.BID_RESPONSE); events.emit(constants.EVENTS.BID_WON, MOCK.BID_WON); - sinon.assert.callCount(invisiblyAdapter.track, 5); + + // 5 Invisibly events + 1 Clean.io event + sinon.assert.callCount(invisiblyAdapter.track, 6); }); it('should not catch events triggered without invisibly account config', function () { @@ -380,7 +382,9 @@ describe('Invisibly Analytics Adapter test suite', function () { expect(invisiblyEvents.event_data.pageViewId).to.exist; expect(invisiblyEvents.event_data.ver).to.equal(1); expect(invisiblyEvents.event_type).to.equal('PREBID_bidWon'); - sinon.assert.callCount(invisiblyAdapter.track, 1); + + // 1 Invisibly event + 1 Clean.io event + sinon.assert.callCount(invisiblyAdapter.track, 2); }); // spec for bidder done event @@ -551,7 +555,8 @@ describe('Invisibly Analytics Adapter test suite', function () { events.emit(constants.EVENTS.ADD_AD_UNITS, MOCK.ADD_AD_UNITS); events.emit(constants.EVENTS.AD_RENDER_FAILED, MOCK.AD_RENDER_FAILED); - sinon.assert.callCount(invisiblyAdapter.track, 13); + // 13 Invisibly events + 1 Clean.io event + sinon.assert.callCount(invisiblyAdapter.track, 14); }); }); diff --git a/test/spec/modules/konduitAnalyticsAdapter_spec.js b/test/spec/modules/konduitAnalyticsAdapter_spec.js index ac557d27f90..1612138cde4 100644 --- a/test/spec/modules/konduitAnalyticsAdapter_spec.js +++ b/test/spec/modules/konduitAnalyticsAdapter_spec.js @@ -121,6 +121,7 @@ describe(`Konduit Analytics Adapter`, () => { expect(requestBody.konduitId).to.be.equal(konduitId); expect(requestBody.prebidVersion).to.be.equal('$prebid.version$'); expect(requestBody.environment).to.be.an('object'); - sinon.assert.callCount(konduitAnalyticsAdapter.track, 6); + // 6 Konduit events + 1 Clean.io event + sinon.assert.callCount(konduitAnalyticsAdapter.track, 7); }); }); diff --git a/test/spec/modules/optimonAnalyticsAdapter_spec.js b/test/spec/modules/optimonAnalyticsAdapter_spec.js index c50bfcb170f..406e9cb75c4 100644 --- a/test/spec/modules/optimonAnalyticsAdapter_spec.js +++ b/test/spec/modules/optimonAnalyticsAdapter_spec.js @@ -35,6 +35,7 @@ describe('Optimon Analytics Adapter', () => { events.emit(constants.EVENTS.BID_TIMEOUT, optmn_arguments) events.emit(constants.EVENTS.BID_WON, optmn_arguments) - expect(optmn_queue.length).to.eql(3); + // 3 Optimon events + 1 Clean.io event + expect(optmn_queue.length).to.eql(4); }); }); diff --git a/test/spec/modules/pianoDmpAnalyticsAdapter_spec.js b/test/spec/modules/pianoDmpAnalyticsAdapter_spec.js index 240e90c3c32..49b048f1fe6 100644 --- a/test/spec/modules/pianoDmpAnalyticsAdapter_spec.js +++ b/test/spec/modules/pianoDmpAnalyticsAdapter_spec.js @@ -52,6 +52,10 @@ describe('Piano DMP Analytics Adapter', () => { // Then const callQueue = (window.cX || {}).callQueue; + const billableEventIndex = callQueue.findIndex(([, params]) => params.eventType === constants.EVENTS.BILLABLE_EVENT); + if (billableEventIndex > -1) { + callQueue.splice(billableEventIndex, 1); + } expect(callQueue).to.be.an('array'); expect(callQueue.length).to.equal(testEvents.length); diff --git a/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js b/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js index e504eced868..2e1bf4df062 100644 --- a/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js +++ b/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js @@ -102,7 +102,8 @@ describe('Prebid Manager Analytics Adapter', function () { events.emit(constants.EVENTS.AUCTION_END, {}); events.emit(constants.EVENTS.BID_TIMEOUT, {}); - sinon.assert.callCount(prebidmanagerAnalytics.track, 6); + // 6 Prebid Manager events + 1 Clean.io event + sinon.assert.callCount(prebidmanagerAnalytics.track, 7); }); }); diff --git a/test/spec/modules/pubperfAnalyticsAdapter_spec.js b/test/spec/modules/pubperfAnalyticsAdapter_spec.js index b316b44617a..160529125d3 100644 --- a/test/spec/modules/pubperfAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubperfAnalyticsAdapter_spec.js @@ -49,7 +49,8 @@ describe('Pubperf Analytics Adapter', function() { events.emit(constants.EVENTS.AUCTION_END, {}); events.emit(constants.EVENTS.BID_TIMEOUT, {}); - sinon.assert.callCount(pubperfAnalytics.track, 6); + // 6 Pubperf events + 1 Clean.io event + sinon.assert.callCount(pubperfAnalytics.track, 7); }); }); }); diff --git a/test/spec/modules/pubstackAnalyticsAdapter_spec.js b/test/spec/modules/pubstackAnalyticsAdapter_spec.js index 4df25f1665b..3d01a0bb506 100644 --- a/test/spec/modules/pubstackAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubstackAnalyticsAdapter_spec.js @@ -33,6 +33,7 @@ describe('Pubstack Analytics Adapter', () => { events.emit(constants.EVENTS.NO_BID, args) // Then - expect(queue.length).to.eql(6); + // 6 Pubstack events + 1 Clean.io event + expect(queue.length).to.eql(7); }); }); diff --git a/test/spec/modules/pubwiseAnalyticsAdapter_spec.js b/test/spec/modules/pubwiseAnalyticsAdapter_spec.js index 3be4ea3d69c..dd5c223b767 100644 --- a/test/spec/modules/pubwiseAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubwiseAnalyticsAdapter_spec.js @@ -71,10 +71,10 @@ describe('PubWise Prebid Analytics', function () { events.emit(constants.EVENTS.AUCTION_END, {}); // eslint-disable-next-line - //console.log(requests); + //console.log(requests); /* testing for 6 calls, including the 2 we're not currently tracking */ - sandbox.assert.callCount(pubwiseAnalytics.track, 7); + sandbox.assert.callCount(pubwiseAnalytics.track, 8); }); it('should initialize the auction properly', function () { @@ -92,7 +92,7 @@ describe('PubWise Prebid Analytics', function () { let request = requests[0]; let data = JSON.parse(request.requestBody); // eslint-disable-next-line - // console.log(data.metaData); + // console.log(data.metaData); expect(data.metaData, 'metaData property').to.exist; expect(data.metaData.pbjs_version, 'pbjs version').to.equal('$prebid.version$') expect(data.metaData.session_id, 'session id').not.to.be.empty diff --git a/test/spec/modules/sigmoidAnalyticsAdapter_spec.js b/test/spec/modules/sigmoidAnalyticsAdapter_spec.js index 854c3a8e22d..6d772de02eb 100644 --- a/test/spec/modules/sigmoidAnalyticsAdapter_spec.js +++ b/test/spec/modules/sigmoidAnalyticsAdapter_spec.js @@ -38,7 +38,7 @@ describe('sigmoid Prebid Analytic', function () { events.emit(constants.EVENTS.BID_RESPONSE, {}); events.emit(constants.EVENTS.BID_WON, {}); - sinon.assert.callCount(sigmoidAnalytic.track, 7); + sinon.assert.callCount(sigmoidAnalytic.track, 8); }); }); describe('build utm tag data', function () { diff --git a/test/spec/modules/sovrnAnalyticsAdapter_spec.js b/test/spec/modules/sovrnAnalyticsAdapter_spec.js index d6795331417..8284bb54e9b 100644 --- a/test/spec/modules/sovrnAnalyticsAdapter_spec.js +++ b/test/spec/modules/sovrnAnalyticsAdapter_spec.js @@ -202,7 +202,8 @@ describe('Sovrn Analytics Adapter', function () { events.emit(constants.EVENTS.BID_RESPONSE, {}); events.emit(constants.EVENTS.BID_WON, {}); - sinon.assert.callCount(sovrnAnalyticsAdapter.track, 5); + // 5 SovrnAnalytics events + 1 Clean.io event + sinon.assert.callCount(sovrnAnalyticsAdapter.track, 6); }); it('should catch no events if no affiliate id', function () { diff --git a/test/spec/modules/yieldoneAnalyticsAdapter_spec.js b/test/spec/modules/yieldoneAnalyticsAdapter_spec.js index f77bc7bc7ba..f55577913a5 100644 --- a/test/spec/modules/yieldoneAnalyticsAdapter_spec.js +++ b/test/spec/modules/yieldoneAnalyticsAdapter_spec.js @@ -269,7 +269,11 @@ describe('Yieldone Prebid Analytic', function () { setTimeout(function() { events.emit(constants.EVENTS.BID_WON, winner); - sinon.assert.callCount(sendStatStub, 2); + sinon.assert.callCount(sendStatStub, 2) + const billableEventIndex = yieldoneAnalytics.eventsStorage[auctionId].events.findIndex(event => event.eventType === constants.EVENTS.BILLABLE_EVENT); + if (billableEventIndex > -1) { + yieldoneAnalytics.eventsStorage[auctionId].events.splice(billableEventIndex, 1); + } expect(yieldoneAnalytics.eventsStorage[auctionId]).to.deep.equal(wonExpectedResult); delete yieldoneAnalytics.eventsStorage[auctionId]; From 2afac1ed2a531a72fc206358bcad38a51d9788ad Mon Sep 17 00:00:00 2001 From: Abdullah Al Mamun Oronno Date: Mon, 8 Aug 2022 19:10:11 -0400 Subject: [PATCH 032/246] Fix broken download link (#8800) Download link https://prebid.org/download.html no longer valid. Updated with current link https://docs.prebid.org/download.html --- RELEASE_SCHEDULE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE_SCHEDULE.md b/RELEASE_SCHEDULE.md index b68495ed4ae..45f4e6c7dc5 100644 --- a/RELEASE_SCHEDULE.md +++ b/RELEASE_SCHEDULE.md @@ -12,7 +12,7 @@ We aim to push a new release of Prebid.js every week on Tuesday. While the releases will be available immediately for those using direct Git access, -it will be about a week before the Prebid Org [Download Page](http://prebid.org/download.html) will be updated. +it will be about a week before the Prebid Org [Download Page](https://docs.prebid.org/download.html) will be updated. You can determine what is in a given build using the [releases page](https://github.com/prebid/Prebid.js/releases) From 9d8112a0e395bd37d3baa3482dad7da7bffec221 Mon Sep 17 00:00:00 2001 From: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Date: Tue, 9 Aug 2022 04:43:08 +0530 Subject: [PATCH 033/246] PubMatic Bid Adapter: Sending allowedAlternateBidderCodes data to AdServer (#8790) * Changed net revenue to True * Added bidderCode and adapterCode to bidObject * setting seat as default adapterCode * removed bidderCode * Added unit test cases * cloned the test object * send alternateBidderCodes to adserver * handled blank and duplicate entries * added test cases * Handled case for empty biddersArray Co-authored-by: Azhar --- modules/pubmaticBidAdapter.js | 21 ++++++-- test/spec/modules/pubmaticBidAdapter_spec.js | 57 ++++++++++++++++++++ 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 42c05f765bc..bdc8010618c 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -1,4 +1,4 @@ -import { logWarn, _each, isBoolean, isStr, isArray, inIframe, mergeDeep, deepAccess, isNumber, deepSetValue, logInfo, logError, deepClone, convertTypes } from '../src/utils.js'; +import { logWarn, _each, isBoolean, isStr, isArray, inIframe, mergeDeep, deepAccess, isNumber, deepSetValue, logInfo, logError, deepClone, convertTypes, uniques } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js'; import {config} from '../src/config.js'; @@ -177,6 +177,8 @@ let publisherId = 0; let isInvalidNativeRequest = false; let NATIVE_ASSET_ID_TO_KEY_MAP = {}; let NATIVE_ASSET_KEY_TO_ASSET_MAP = {}; +let biddersList = ['pubmatic']; +const allBiddersList = ['all']; // loading NATIVE_ASSET_ID_TO_KEY_MAP _each(NATIVE_ASSETS, anAsset => { NATIVE_ASSET_ID_TO_KEY_MAP[anAsset.ID] = anAsset.KEY }); @@ -1089,10 +1091,21 @@ export const spec = { payload.ext.wrapper.wv = $$REPO_AND_VERSION$$; payload.ext.wrapper.transactionId = conf.transactionId; payload.ext.wrapper.wp = 'pbjs'; - if (bidderRequest && bidderRequest.bidderCode) { - payload.ext.allowAlternateBidderCodes = bidderSettings.get(bidderRequest.bidderCode, 'allowAlternateBidderCodes'); - payload.ext.allowedAlternateBidderCodes = bidderSettings.get(bidderRequest.bidderCode, 'allowedAlternateBidderCodes'); + const allowAlternateBidder = bidderRequest ? bidderSettings.get(bidderRequest.bidderCode, 'allowAlternateBidderCodes') : undefined; + if (allowAlternateBidder !== undefined) { + payload.ext.marketplace = {}; + if (bidderRequest && allowAlternateBidder == true) { + let allowedBiddersList = bidderSettings.get(bidderRequest.bidderCode, 'allowedAlternateBidderCodes'); + if (isArray(allowedBiddersList)) { + allowedBiddersList = allowedBiddersList.map(val => val.trim().toLowerCase()).filter(val => !!val).filter(uniques) + biddersList = allowedBiddersList.includes('*') ? allBiddersList : [...biddersList, ...allowedBiddersList]; + } else { + biddersList = allBiddersList; + } + } + payload.ext.marketplace.allowedbidders = biddersList.filter(uniques); } + payload.user.gender = (conf.gender ? conf.gender.trim() : UNDEFINED); payload.user.geo = {}; payload.user.geo.lat = _parseSlotParam('lat', conf.lat); diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 6e65e725024..f3a49a6a925 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -3,6 +3,7 @@ import {spec, checkVideoPlacement} from 'modules/pubmaticBidAdapter.js'; import * as utils from 'src/utils.js'; import {config} from 'src/config.js'; import { createEidsArray } from 'modules/userId/eids.js'; +import { bidderSettings } from 'src/bidderSettings.js'; const constants = require('src/constants.json'); describe('PubMatic adapter', function () { @@ -1104,6 +1105,62 @@ describe('PubMatic adapter', function () { expect(data.source.ext.schain).to.deep.equal(bidRequests[0].schain); }); + describe('Marketplace parameters', function() { + let bidderSettingStub; + beforeEach(function() { + bidderSettingStub = sinon.stub(bidderSettings, 'get'); + }); + + afterEach(function() { + bidderSettingStub.restore(); + }); + + it('should not be present when allowAlternateBidderCodes is undefined', function () { + bidderSettingStub.returns(undefined); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + expect(data.ext.marketplace).to.equal(undefined); + }); + + it('should be pubmatic and groupm when allowedAlternateBidderCodes is \'groupm\'', function () { + bidderSettingStub.withArgs('pubmatic', 'allowAlternateBidderCodes').returns(true); + bidderSettingStub.withArgs('pubmatic', 'allowedAlternateBidderCodes').returns(['groupm']); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id', + bidderCode: 'pubmatic' + }); + let data = JSON.parse(request.data); + expect(data.ext.marketplace.allowedbidders).to.be.an('array'); + expect(data.ext.marketplace.allowedbidders.length).to.equal(2); + expect(data.ext.marketplace.allowedbidders[0]).to.equal('pubmatic'); + expect(data.ext.marketplace.allowedbidders[1]).to.equal('groupm'); + }); + + it('should be ALL by default', function () { + bidderSettingStub.returns(true); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id' + }); + let data = JSON.parse(request.data); + expect(data.ext.marketplace.allowedbidders).to.be.an('array'); + expect(data.ext.marketplace.allowedbidders[0]).to.equal('all'); + }); + + it('should be ALL when allowedAlternateBidderCodes is \'*\'', function () { + bidderSettingStub.withArgs('pubmatic', 'allowAlternateBidderCodes').returns(true); + bidderSettingStub.withArgs('pubmatic', 'allowedAlternateBidderCodes').returns(['*']); + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id', + bidderCode: 'pubmatic' + }); + let data = JSON.parse(request.data); + expect(data.ext.marketplace.allowedbidders).to.be.an('array'); + expect(data.ext.marketplace.allowedbidders[0]).to.equal('all'); + }); + }) + it('Set content from config, set site.content', function() { let sandbox = sinon.sandbox.create(); const content = { From 84499f743115d6c4bf8c33de62eb23ce5a3843ff Mon Sep 17 00:00:00 2001 From: julian-burger-ttd <105891200+julian-burger-ttd@users.noreply.github.com> Date: Mon, 8 Aug 2022 20:12:08 -0700 Subject: [PATCH 034/246] Ttd Bid adapter: support for transaction id, bcat, and consolidate params (#8679) * remove siteId parameter make placementId parameter optional if GPID is passed pass transactionId through to source.tid read bcat from ortb2 * enforce check for gpid or placementId * add error message * Support badv/battr, get tid from ext object * Address review feedback Co-authored-by: Minh Daole --- modules/ttdBidAdapter.js | 63 ++++++---- modules/ttdBidAdapter.md | 14 +-- test/spec/modules/ttdBidAdapter_spec.js | 147 ++++++++++++++++++------ 3 files changed, 156 insertions(+), 68 deletions(-) diff --git a/modules/ttdBidAdapter.js b/modules/ttdBidAdapter.js index 55d7490a4ad..b95b379d74d 100644 --- a/modules/ttdBidAdapter.js +++ b/modules/ttdBidAdapter.js @@ -4,7 +4,7 @@ import { createEidsArray } from './userId/eids.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; -const BIDADAPTERVERSION = 'TTD-PREBID-2022.02.18'; +const BIDADAPTERVERSION = 'TTD-PREBID-2022.06.28'; const BIDDER_CODE = 'ttd'; const BIDDER_CODE_LONG = 'thetradedesk'; const BIDDER_ENDPOINT = 'https://direct.adsrvr.org/bid/bidder/'; @@ -58,7 +58,9 @@ function getBidFloor(bid) { } function getSource(validBidRequests) { - let source = {}; + let source = { + tid: validBidRequests[0].transactionId + }; if (validBidRequests[0].schain) { utils.deepSetValue(source, 'ext.schain', validBidRequests[0].schain); } @@ -124,7 +126,6 @@ function getUser(bidderRequest) { function getSite(bidderRequest, firstPartyData) { var site = { - id: utils.deepAccess(bidderRequest, 'bids.0.params.siteId'), page: utils.deepAccess(bidderRequest, 'refererInfo.page'), publisher: { id: utils.deepAccess(bidderRequest, 'bids.0.params.publisherId'), @@ -141,15 +142,20 @@ function getSite(bidderRequest, firstPartyData) { function getImpression(bidRequest) { let impression = { - id: bidRequest.bidId, - tagid: bidRequest.params.placementId + id: bidRequest.bidId }; - let gpid = utils.deepAccess(bidRequest, 'ortb2Imp.ext.gpid'); - if (gpid) { - impression.ext = { - gpid: gpid - } + const gpid = utils.deepAccess(bidRequest, 'ortb2Imp.ext.gpid'); + const tid = utils.deepAccess(bidRequest, 'ortb2Imp.ext.tid'); + if (gpid || tid) { + impression.ext = {} + if (gpid) { impression.ext.gpid = gpid } + if (tid) { impression.ext.tid = tid } + } + + const tagid = gpid || bidRequest.params.placementId; + if (tagid) { + impression.tagid = tagid } const mediaTypesVideo = utils.deepAccess(bidRequest, 'mediaTypes.video'); @@ -212,6 +218,12 @@ function banner(bid) { format: sizes, }, optionalParams); + + const battr = utils.deepAccess(bid, 'ortb2Imp.battr'); + if (battr) { + banner.battr = battr + } + return banner; } @@ -279,6 +291,11 @@ function video(bid) { video.maxbitrate = maxbitrate; } + const battr = utils.deepAccess(bid, 'ortb2Imp.battr'); + if (battr) { + video.battr = battr + } + return video; } @@ -318,20 +335,10 @@ export const spec = { utils.logWarn(BIDDER_CODE + ': params.publisherId must be 32 characters or less'); return false; } - if (!bid.params.siteId) { - utils.logWarn(BIDDER_CODE + ': Missing required parameter params.siteId'); - return false; - } - if (bid.params.siteId.length > 50) { - utils.logWarn(BIDDER_CODE + ': params.siteId must be 50 characters or less'); - return false; - } - if (!bid.params.placementId) { - utils.logWarn(BIDDER_CODE + ': Missing required parameter params.placementId'); - return false; - } - if (bid.params.placementId.length > 128) { - utils.logWarn(BIDDER_CODE + ': params.placementId must be 128 characters or less'); + + const gpid = utils.deepAccess(bid, 'ortb2Imp.ext.gpid'); + if (!bid.params.placementId && !gpid) { + utils.logWarn(BIDDER_CODE + ': one of params.placementId or gpid (via the GPT module https://docs.prebid.org/dev-docs/modules/gpt-pre-auction.html) must be passed'); return false; } @@ -387,6 +394,14 @@ export const spec = { ext: getExt(firstPartyData) } + if (firstPartyData && firstPartyData.bcat) { + topLevel.bcat = firstPartyData.bcat; + } + + if (firstPartyData && firstPartyData.badv) { + topLevel.badv = firstPartyData.badv; + } + let url = BIDDER_ENDPOINT + bidderRequest.bids[0].params.supplySourceId; let serverRequest = { diff --git a/modules/ttdBidAdapter.md b/modules/ttdBidAdapter.md index 9c67f3267cb..efdef751149 100644 --- a/modules/ttdBidAdapter.md +++ b/modules/ttdBidAdapter.md @@ -29,9 +29,7 @@ The Trade Desk bid adapter supports Banner and Video. bidder: 'ttd', params: { supplySourceId: 'supplier', - publisherId: '1427ab10f2e448057ed3b422', - siteId: 'site-123', - placementId: 'footer1' + publisherId: '1427ab10f2e448057ed3b422' } } ] @@ -51,8 +49,7 @@ The Trade Desk bid adapter supports Banner and Video. params: { supplySourceId: 'supplier', publisherId: '1427ab10f2e448057ed3b422', - siteId: 'site-123', - placementId: 'footer1', + placementId: '/1111/home#header', banner: { expdir: [1, 3] }, @@ -77,9 +74,7 @@ The Trade Desk bid adapter supports Banner and Video. bidder: 'ttd', params: { supplySourceId: 'supplier', - publisherId: '1427ab10f2e448057ed3b422', - siteId: 'site-123', - placementId: 'footer1' + publisherId: '1427ab10f2e448057ed3b422' } } ] @@ -112,8 +107,7 @@ The Trade Desk bid adapter supports Banner and Video. params: { supplySourceId: 'supplier', publisherId: '1427ab10f2e448057ed3b422', - siteId: 'site-123', - placementId: 'footer1' + placementId: '/1111/home#header' } } ] diff --git a/test/spec/modules/ttdBidAdapter_spec.js b/test/spec/modules/ttdBidAdapter_spec.js index b933a5dd0c9..aa2e84b619e 100644 --- a/test/spec/modules/ttdBidAdapter_spec.js +++ b/test/spec/modules/ttdBidAdapter_spec.js @@ -17,8 +17,7 @@ describe('ttdBidAdapter', function () { 'params': { 'supplySourceId': 'supplier', 'publisherId': '22222222', - 'placementId': 'some-PlacementId_1', - 'siteId': 'testSiteId' + 'placementId': 'some-PlacementId_1' }, 'mediaTypes': { 'banner': { @@ -57,27 +56,20 @@ describe('ttdBidAdapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when siteId not passed', function () { - let bid = makeBid(); - delete bid.params.siteId; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when siteId is longer than 50 characters', function () { - let bid = makeBid(); - bid.params.siteId = '1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111' - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when placementId not passed', function () { + it('should return true if placementId is not passed and gpid is passed', function () { let bid = makeBid(); delete bid.params.placementId; - expect(spec.isBidRequestValid(bid)).to.equal(false); + bid.ortb2Imp = { + ext: { + gpid: '/1111/home#header' + } + } + expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return false when the placementId is longer than 128 characters', function () { + it('should return false if neither placementId nor gpid is passed', function () { let bid = makeBid(); - bid.params.placementId = '1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'; // 130 characters + delete bid.params.placementId; expect(spec.isBidRequestValid(bid)).to.equal(false); }); @@ -103,7 +95,6 @@ describe('ttdBidAdapter', function () { 'params': { 'supplySourceId': 'supplier', 'publisherId': '22222222', - 'siteId': 'testSiteId123', 'placementId': 'somePlacementId' }, 'mediaTypes': { @@ -187,17 +178,21 @@ describe('ttdBidAdapter', function () { 'params': { 'supplySourceId': 'supplier', 'publisherId': '13144370', - 'placementId': '1gaa015', - 'siteId': 'testSiteId123' + 'placementId': '1gaa015' }, 'mediaTypes': { 'banner': { 'sizes': [[300, 250], [300, 600]] } }, + 'ortb2Imp': { + 'ext': { + 'tid': '8651474f-58b1-4368-b812-84f8c937a099', + } + }, 'sizes': [[300, 250], [300, 600]], + 'transactionId': '1111474f-58b1-4368-b812-84f8c937a099', 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '8651474f-58b1-4368-b812-84f8c937a099', 'bidId': '243310435309b5', 'bidderRequestId': '18084284054531', 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', @@ -240,16 +235,49 @@ describe('ttdBidAdapter', function () { expect(url).to.equal('https://direct.adsrvr.org/bid/bidder/supplier'); }); - it('sends publisher id, site id, and placement id', function () { + it('sends publisher id', function () { const requestBody = testBuildRequests(baseBannerBidRequests, baseBidderRequest).data; expect(requestBody.site).to.be.not.null; expect(requestBody.site.publisher).to.be.not.null; - expect(requestBody.imp[0].tagid).to.be.not.null; expect(requestBody.site.publisher.id).to.equal(baseBannerBidRequests[0].params.publisherId); - expect(requestBody.site.id).to.equal(baseBannerBidRequests[0].params.siteId); + }); + + it('sends placement id in tagid', function () { + const requestBody = testBuildRequests(baseBannerBidRequests, baseBidderRequest).data; expect(requestBody.imp[0].tagid).to.equal(baseBannerBidRequests[0].params.placementId); }); + it('sends gpid in tagid if present', function () { + let clonedBannerRequests = deepClone(baseBannerBidRequests); + const gpid = '/1111/home#header'; + clonedBannerRequests[0].ortb2Imp = { + ext: { + gpid: gpid + } + }; + const requestBody = testBuildRequests(clonedBannerRequests, baseBidderRequest).data; + expect(requestBody.imp[0].tagid).to.equal(gpid); + }); + + it('sends gpid in ext.gpid if present', function () { + let clonedBannerRequests = deepClone(baseBannerBidRequests); + const gpid = '/1111/home#header'; + clonedBannerRequests[0].ortb2Imp = { + ext: { + gpid: gpid + } + }; + const requestBody = testBuildRequests(clonedBannerRequests, baseBidderRequest).data; + expect(requestBody.imp[0].ext).to.be.not.null; + expect(requestBody.imp[0].ext.gpid).to.equal(gpid); + }); + + it('sends transaction id in source.tid', function () { + const requestBody = testBuildRequests(baseBannerBidRequests, baseBidderRequest).data; + expect(requestBody.source).to.be.not.null; + expect(requestBody.source.tid).to.equal('1111474f-58b1-4368-b812-84f8c937a099'); + }); + it('includes the ad size in the bid request', function () { const requestBody = testBuildRequests(baseBannerBidRequests, baseBidderRequest).data; expect(requestBody.imp[0].banner.format[0].w).to.equal(300); @@ -283,18 +311,44 @@ describe('ttdBidAdapter', function () { }); it('sets keywords properly if sent', function () { - let clonedBannerRequests = deepClone(baseBannerBidRequests); - const ortb2 = { site: { keywords: 'highViewability, clothing, holiday shopping' } }; - const requestBody = testBuildRequests(clonedBannerRequests, {...baseBidderRequest, ortb2}).data; + const requestBody = testBuildRequests(baseBannerBidRequests, {...baseBidderRequest, ortb2}).data; config.resetConfig(); expect(requestBody.ext.ttdprebid.keywords).to.deep.equal(['highViewability', 'clothing', 'holiday shopping']); }); + it('sets bcat properly if sent', function () { + const ortb2 = { + bcat: ['IAB1-1', 'IAB2-9'] + }; + const requestBody = testBuildRequests(baseBannerBidRequests, {...baseBidderRequest, ortb2}).data; + config.resetConfig(); + expect(requestBody.bcat).to.deep.equal(['IAB1-1', 'IAB2-9']); + }); + + it('sets badv properly if sent', function () { + const ortb2 = { + badv: ['adv1.com', 'adv2.com'] + }; + const requestBody = testBuildRequests(baseBannerBidRequests, {...baseBidderRequest, ortb2}).data; + config.resetConfig(); + expect(requestBody.badv).to.deep.equal(['adv1.com', 'adv2.com']); + }); + + it('sets battr properly if present', function () { + let clonedBannerRequests = deepClone(baseBannerBidRequests); + const battr = [1, 2, 3]; + clonedBannerRequests[0].ortb2Imp = { + battr: battr + }; + const requestBody = testBuildRequests(clonedBannerRequests, baseBidderRequest).data; + expect(requestBody.imp[0].banner.battr).to.equal(battr); + }); + it('sets ext properly', function () { let clonedBannerRequests = deepClone(baseBannerBidRequests); @@ -442,9 +496,14 @@ describe('ttdBidAdapter', function () { 'sizes': [[300, 250], [300, 600]] } }, + 'ortb2Imp': { + 'ext': { + 'tid': '8651474f-58b1-4368-b812-84f8c937a099', + } + }, 'sizes': [[300, 250], [300, 600]], + 'transactionId': '1111474f-58b1-4368-b812-84f8c937a099', 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '8651474f-58b1-4368-b812-84f8c937a099', 'bidId': 'small', 'bidderRequestId': '18084284054531', 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', @@ -454,17 +513,21 @@ describe('ttdBidAdapter', function () { 'bidder': 'ttd', 'params': { 'publisherId': '13144370', - 'placementId': 'top', - 'siteId': 'testSite123' + 'placementId': 'top' }, 'mediaTypes': { 'banner': { 'sizes': [[728, 90]] } }, + 'ortb2Imp': { + 'ext': { + 'tid': '12345678-58b1-4368-b812-84f8c937a099', + } + }, 'sizes': [[728, 90]], - 'adUnitCode': 'div-gpt-ad-91515710-0', 'transactionId': '825c1228-ca8c-4657-b40f-2df500621527', + 'adUnitCode': 'div-gpt-ad-91515710-0', 'bidId': 'large', 'bidderRequestId': '18084284054531', 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', @@ -493,6 +556,12 @@ describe('ttdBidAdapter', function () { it('sends multiple impressions', function () { const requestBody = testBuildRequests(baseBannerMultipleBidRequests, baseBidderRequest).data; expect(requestBody.imp.length).to.equal(2); + expect(requestBody.source).to.be.not.null; + expect(requestBody.source.tid).to.equal('1111474f-58b1-4368-b812-84f8c937a099'); + expect(requestBody.imp[0].ext).to.be.not.null; + expect(requestBody.imp[0].ext.tid).to.equal('8651474f-58b1-4368-b812-84f8c937a099'); + expect(requestBody.imp[1].ext).to.be.not.null; + expect(requestBody.imp[1].ext.tid).to.equal('12345678-58b1-4368-b812-84f8c937a099'); }); it('sends the right tag ids for each ad unit', function () { @@ -547,8 +616,13 @@ describe('ttdBidAdapter', function () { 'sizes': [[300, 250], [300, 600]] } }, + 'ortb2Imp': { + 'ext': { + 'tid': '8651474f-58b1-4368-b812-84f8c937a099', + } + }, + 'transactionId': '1111474f-58b1-4368-b812-84f8c937a099', 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '8651474f-58b1-4368-b812-84f8c937a099', 'bidId': '243310435309b5', 'bidderRequestId': '18084284054531', 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', @@ -607,8 +681,13 @@ describe('ttdBidAdapter', function () { 'maxduration': 30 } }, + 'ortb2Imp': { + 'ext': { + 'tid': '8651474f-58b1-4368-b812-84f8c937a099', + } + }, + 'transactionId': '1111474f-58b1-4368-b812-84f8c937a099', 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '8651474f-58b1-4368-b812-84f8c937a099', 'bidId': '243310435309b5', 'bidderRequestId': '18084284054531', 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', From 555c4c0b9c01c05b8cb4a91fd0822a7e3f9ef6ce Mon Sep 17 00:00:00 2001 From: JacobKlein26 <42449375+JacobKlein26@users.noreply.github.com> Date: Mon, 8 Aug 2022 23:13:45 -0400 Subject: [PATCH 035/246] NextMillennium Bid Adapter: Add referrer and imp to bid request (#8718) * add more information and testing * fix typo * fix typo in testing * use getRefererInfo Co-authored-by: Mikhail Ivanchenko --- modules/nextMillenniumBidAdapter.js | 23 +++++++++++++++++-- .../modules/nextMillenniumBidAdapter_spec.js | 17 ++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index 375258176da..f873e5b5c29 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -1,4 +1,5 @@ import { isStr, _each, parseUrl, getWindowTop, getBidIdParameter } from '../src/utils.js'; +import { getRefererInfo } from '../src/refererDetection.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; @@ -24,12 +25,18 @@ export const spec = { _each(validBidRequests, function(bid) { window.nmmRefreshCounts[bid.adUnitCode] = window.nmmRefreshCounts[bid.adUnitCode] || 0; + const id = getPlacementId(bid) + + if (bid.sizes && !Array.isArray(bid.sizes[0])) bid.sizes = [bid.sizes] + if (!bid.ortb2) bid.ortb2 = {} + if (!bid.ortb2.device) bid.ortb2.device = {} + bid.ortb2.device.referrer = (getRefererInfo && getRefererInfo().ref) || '' const postBody = { 'id': bid.auctionId, 'ext': { 'prebid': { 'storedrequest': { - 'id': getPlacementId(bid) + 'id': id } }, @@ -39,7 +46,19 @@ export const spec = { 'scrollTop': window.pageYOffset || document.documentElement.scrollTop } }, - ...bid.ortb2 + ...bid.ortb2, + 'imp': [{ + 'banner': { + 'format': (bid.sizes || []).map(s => { return {w: s[0], h: s[1]} }) + }, + 'ext': { + 'prebid': { + 'storedrequest': { + 'id': id + } + } + } + }] } const gdprConsent = bidderRequest && bidderRequest.gdprConsent; diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index 9362388b539..40005356fd8 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -114,6 +114,23 @@ describe('nextMillenniumBidAdapterTests', function() { expect(JSON.parse(request[0].data).ext.nextMillennium.elOffsets).to.be.an('object') }) + it('Check if refferer was added', function() { + const request = spec.buildRequests(bidRequestData) + expect(JSON.parse(request[0].data).device.referrer).to.exist + }) + + it('Check if imp object was added', function() { + const request = spec.buildRequests(bidRequestData) + expect(JSON.parse(request[0].data).imp).to.be.an('array') + }) + + it('Check if imp prebid stored id is correct', function() { + const request = spec.buildRequests(bidRequestData) + const requestData = JSON.parse(request[0].data); + const storedReqId = requestData.ext.prebid.storedrequest.id; + expect(requestData.imp[0].ext.prebid.storedrequest.id).to.equal(storedReqId) + }) + it('Test getUserSyncs function', function () { const syncOptions = { 'iframeEnabled': true From 638e7c5866fa62093f9fea074554d90c3cb5cf23 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Mon, 8 Aug 2022 20:14:19 -0700 Subject: [PATCH 036/246] UserID: continue the auction if userId init fails (#8788) --- modules/userId/index.js | 16 ++++++++++------ test/spec/modules/userId_spec.js | 15 ++++++++++++++- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/modules/userId/index.js b/modules/userId/index.js index f42c4c7827f..f925e637178 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -614,9 +614,9 @@ function getPPID() { * @param {Object} reqBidsConfigObj required; This is the same param that's used in pbjs.requestBids. * @param {function} fn required; The next function in the chain, used by hook.js */ -export function requestBidsHook(fn, reqBidsConfigObj, {delay = GreedyPromise.timeout} = {}) { +export function requestBidsHook(fn, reqBidsConfigObj, {delay = GreedyPromise.timeout, getIds = getUserIdsAsync} = {}) { GreedyPromise.race([ - getUserIdsAsync(), + getIds().catch(() => null), delay(auctionDelay) ]).then(() => { // pass available user id data to bid adapters @@ -762,13 +762,17 @@ function refreshUserIds({submoduleNames} = {}, callback) { function getUserIdsAsync() { return initIdSystem().then( () => getUserIds(), - (e) => - e === INIT_CANCELED + (e) => { + if (e === INIT_CANCELED) { // there's a pending refresh - because GreedyPromise runs this synchronously, we are now in the middle // of canceling the previous init, before the refresh logic has had a chance to run. // Use a "normal" Promise to clear the stack and let it complete (or this will just recurse infinitely) - ? Promise.resolve().then(getUserIdsAsync) - : GreedyPromise.reject(e) + return Promise.resolve().then(getUserIdsAsync) + } else { + logError('Error initializing userId', e) + return GreedyPromise.reject(e) + } + } ); } diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 046b3ffd321..e6058673d41 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -539,9 +539,22 @@ describe('User ID', function () { clearStack().then(() => { // simulate init complete mockIdCallback.callArg(0, {id: {MOCKID: '1111'}}); - }) + }); }); + it('should continue the auction when init fails', (done) => { + startInit(); + requestBidsHook(() => { + done(); + }, + {adUnits: [getAdUnitMock()]}, + { + delay: delay(), + getIds: () => Promise.reject(new Error()) + } + ); + }) + it('should not get stuck when init fails', () => { const err = new Error(); mockIdCallback.callsFake(() => { throw err; }); From a5b4cab0b185927d73b780bffdd27674aafc6432 Mon Sep 17 00:00:00 2001 From: wojciech-bialy-wpm <67895844+wojciech-bialy-wpm@users.noreply.github.com> Date: Tue, 9 Aug 2022 05:28:10 +0200 Subject: [PATCH 037/246] sspBC bid adapter - remove storage/cookie check, add screen size to request (#8762) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update tests for sspBC adapter Update tests for sspBC adapter: - change userSync test (due to tcf param appended in v4.6) - add tests for onBidWon and onTimeout * [sspbc-adapter] 5.3 updates: content-type for notifications * [sspbc-adapter] pass CTA to native bid * [sspbc-5.3] keep pbsize for detected adunits * [sspbc-5.3] increment adaptor ver * [sspbc-adapter] maintenance update to sspBCBidAdapter * remove yarn.lock * Delete package-lock.json * remove package-lock.jsonfrom pull request * [sspbc-adapter] send pageViewId in request * [sspbc-adapter] update pageViewId test * [sspbc-adapter] add viewabiility tracker to native ads * [sspbc-adapter] add support for bid.admNative property * [sspbc-adapter] ensure that placement id length is always 3 (improves matching response to request) * [sspbc-adapter] read publisher id and custom ad label, then send them to banner creative * [sspbc-adapter] adlabel and pubid are set as empty strings, if not present in bid response * [sspbc-adapter] jstracker data fix * [sspbc-adapter] jstracker data fix * [sspbc-adapter] send tagid in notifications * [sspbc-adapter] add gvlid to spec; prepare getUserSyncs for iframe + image sync * [sspbc-adapter] fix notification payload * [sspbc-adapter] fix notification payload, fix tests * [sspbc-adapter] add userIds to ortb request * [sspbc-adapter] update to 4.1, change request to be ortb 2.6 compliant * [sspbc-adapter] update tests * [ssbc-adapter] bid cache for video ads * [sspbc-adapter] add PageView.id to banner ad; update tests * [sspbc-adapter] fix window.gam not being added to banner html * [sspbc-adapter] send device / content language * [sspbc-adapter] send pageview and site ids to user sync frame * [sspbc-adapter] add ES6 version of common ad library (for banner creatives) * [sspbc-adapter] move content property * [sspbc-adapter] reorganize notification payload creator * [sspbc-adapter] store PLN price in meta; send in bidWon notification * [sspbc-adapter] add playbackmethod to supporten video params; allow overridinbg video settngs via bid.params.video * [sspbc-adapter] update md * [sspbc-adapter] fix error in mapVideo method (Object assign merror when mediaTypes do not contain video) * [sspbc-5.7] remove storage/cookie detection * [sspbc-5.7] add screen size to request Co-authored-by: Wojciech Biały --- modules/sspBCBidAdapter.js | 29 ++++++++++------------- test/spec/modules/sspBCBidAdapter_spec.js | 4 ++-- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/modules/sspBCBidAdapter.js b/modules/sspBCBidAdapter.js index 94dbe9a4468..0a19410c78f 100644 --- a/modules/sspBCBidAdapter.js +++ b/modules/sspBCBidAdapter.js @@ -1,10 +1,9 @@ -import {deepAccess, getWindowTop, isArray, logWarn} from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {includes as strIncludes} from '../src/polyfill.js'; -import { getStorageManager } from '../src/storageManager.js'; +import { deepAccess, getWindowTop, isArray, logWarn } from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { includes as strIncludes } from '../src/polyfill.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; const BIDDER_CODE = 'sspBC'; @@ -13,9 +12,8 @@ const SYNC_URL = 'https://ssp.wp.pl/bidder/usersync'; const NOTIFY_URL = 'https://ssp.wp.pl/bidder/notify'; const TRACKER_URL = 'https://bdr.wpcdn.pl/tag/jstracker.js'; const GVLID = 676; -const storage = getStorageManager({gvlid: GVLID, bidderCode: BIDDER_CODE}); const TMAX = 450; -const BIDDER_VERSION = '5.6'; +const BIDDER_VERSION = '5.7'; const DEFAULT_CURRENCY = 'PLN'; const W = window; const { navigator } = W; @@ -102,11 +100,6 @@ const getNotificationPayload = bidData => { } } -const cookieSupport = () => { - const isSafari = /^((?!chrome|android|crios|fxios).)*safari/i.test(navigator.userAgent); - return !isSafari && storage.cookiesAreEnabled(); -}; - const applyClientHints = ortbRequest => { const { location } = document; const { connection = {}, deviceMemory, userAgentData = {} } = navigator; @@ -569,7 +562,11 @@ const spec = { tmax, user: {}, regs: {}, - device: { language: getBrowserLanguage() }, + device: { + language: getBrowserLanguage(), + w: screen.width, + h: screen.height, + }, test: testMode, }; @@ -579,7 +576,7 @@ const spec = { return { method: 'POST', - url: `${BIDDER_URL}?cs=${cookieSupport()}&bdver=${BIDDER_VERSION}&pbver=${pbver}&inver=0`, + url: `${BIDDER_URL}?bdver=${BIDDER_VERSION}&pbver=${pbver}&inver=0`, data: JSON.stringify(payload), bidderRequest, }; diff --git a/test/spec/modules/sspBCBidAdapter_spec.js b/test/spec/modules/sspBCBidAdapter_spec.js index f286eb3a1ff..d182b9db24c 100644 --- a/test/spec/modules/sspBCBidAdapter_spec.js +++ b/test/spec/modules/sspBCBidAdapter_spec.js @@ -678,8 +678,8 @@ describe('SSPBC adapter', function () { }); it('should send no syncs, if frame sync is not allowed', function () { - expect(syncResultImage).to.have.length(0); ; - expect(syncResultNone).to.have.length(0); ; + expect(syncResultImage).to.have.length(0); + expect(syncResultNone).to.have.length(0); }); }); From ec2117323b23c3084149292b8610f7bed3749534 Mon Sep 17 00:00:00 2001 From: Rupesh Lakhani <35333377+AskRupert-DM@users.noreply.github.com> Date: Tue, 9 Aug 2022 14:18:56 +0100 Subject: [PATCH 038/246] Ozone Bid Adapter: Various improvements (#8755) * ozone adapter 2.8.0 added instream support fixed/cleaned up code * spec test for ozone 2.8.0 adapter * ozone adapter 2.8.0 * Update ozoneBidAdapter_spec.js * Update ozoneBidAdapter.md Co-authored-by: Patrick McCann --- modules/ozoneBidAdapter.js | 369 +++------ modules/ozoneBidAdapter.md | 37 + test/spec/modules/ozoneBidAdapter_spec.js | 878 +++++++++------------- 3 files changed, 504 insertions(+), 780 deletions(-) diff --git a/modules/ozoneBidAdapter.js b/modules/ozoneBidAdapter.js index bbcb559b53b..2f229720208 100644 --- a/modules/ozoneBidAdapter.js +++ b/modules/ozoneBidAdapter.js @@ -1,23 +1,30 @@ -import { logInfo, logError, deepAccess, logWarn, deepSetValue, isArray, contains, isStr, mergeDeep } from '../src/utils.js'; +import { + logInfo, + logError, + deepAccess, + logWarn, + deepSetValue, + isArray, + contains, + mergeDeep, + parseUrl +} from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import {config} from '../src/config.js'; import {getPriceBucketString} from '../src/cpmBucketManager.js'; import { Renderer } from '../src/Renderer.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; - +import {getRefererInfo} from '../src/refererDetection.js'; const BIDDER_CODE = 'ozone'; - const ORIGIN = 'https://elb.the-ozone-project.com' // applies only to auction & cookie const AUCTIONURI = '/openrtb2/auction'; const OZONECOOKIESYNC = '/static/load-cookie.html'; const OZONE_RENDERER_URL = 'https://prebid.the-ozone-project.com/ozone-renderer.js'; const ORIGIN_DEV = 'https://test.ozpr.net'; - -const OZONEVERSION = '2.7.0'; +const OZONEVERSION = '2.8.0'; export const spec = { gvlid: 524, - aliases: [{code: 'lmc', gvlid: 524}, {code: 'newspassid', gvlid: 524}], + aliases: [{code: 'lmc', gvlid: 524}], version: OZONEVERSION, code: BIDDER_CODE, supportedMediaTypes: [VIDEO, BANNER], @@ -31,10 +38,6 @@ export const spec = { 'cookieSyncUrl': ORIGIN + OZONECOOKIESYNC, 'rendererUrl': OZONE_RENDERER_URL }, - /** - * make sure that the whitelabel/default values are available in the propertyBag - * @param bid Object : the bid - */ loadWhitelabelData(bid) { if (this.propertyBag.whitelabel) { return; } this.propertyBag.whitelabel = JSON.parse(JSON.stringify(this.whitelabel_defaults)); @@ -53,7 +56,6 @@ export const spec = { this.propertyBag.whitelabel.auctionUrl = bidderConfig.endpointOverride.origin + AUCTIONURI; this.propertyBag.whitelabel.cookieSyncUrl = bidderConfig.endpointOverride.origin + OZONECOOKIESYNC; } - if (arr.hasOwnProperty('renderer')) { if (arr.renderer.match('%3A%2F%2F')) { this.propertyBag.whitelabel.rendererUrl = decodeURIComponent(arr['renderer']); @@ -92,11 +94,6 @@ export const spec = { getRendererUrl() { return this.propertyBag.whitelabel.rendererUrl; }, - /** - * Basic check to see whether required parameters are in the request. - * @param bid - * @returns {boolean} - */ isBidRequestValid(bid) { this.loadWhitelabelData(bid); logInfo('isBidRequestValid : ', config.getConfig(), bid); @@ -115,7 +112,7 @@ export const spec = { return false; } if (!(bid.params.publisherId).toString().match(/^[a-zA-Z0-9\-]{12}$/)) { - logError('VALIDATION FAILED : publisherId must be exactly 12 alphanumieric characters including hyphens', adUnitCode); + logError('VALIDATION FAILED : publisherId must be exactly 12 alphanumeric characters including hyphens', adUnitCode); return false; } if (!(bid.params.hasOwnProperty('siteId'))) { @@ -160,19 +157,10 @@ export const spec = { } return true; }, - - /** - * Split this out so that we can validate the placementId and also the override GET parameter ozstoredrequest - * @param placementId - */ isValidPlacementId(placementId) { return placementId.toString().match(/^[0-9]{10}$/); }, - buildRequests(validBidRequests, bidderRequest) { - // convert Native ORTB definition to old-style prebid native definition - validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); - this.loadWhitelabelData(validBidRequests[0]); this.propertyBag.buildRequestsStart = new Date().getTime(); let whitelabelBidder = this.propertyBag.whitelabel.bidder; // by default = ozone @@ -193,14 +181,13 @@ export const spec = { singleRequest = singleRequest !== false; // undefined & true will be true logInfo(`config ${whitelabelBidder}.singleRequest : `, singleRequest); let ozoneRequest = {}; // we only want to set specific properties on this, not validBidRequests[0].params - delete ozoneRequest.test; // don't allow test to be set in the config - ONLY use $_GET['pbjs_debug'] - - let fpd = bidderRequest.ortb2; + logInfo('going to get ortb2 from bidder request...'); + let fpd = deepAccess(bidderRequest, 'ortb2', null); + logInfo('got fpd: ', fpd); if (fpd && deepAccess(fpd, 'user')) { logInfo('added FPD user object'); ozoneRequest.user = fpd.user; } - const getParams = this.getGetParametersAsObject(); const wlOztestmodeKey = whitelabelPrefix + 'testmode'; const isTestMode = getParams[wlOztestmodeKey] || null; // this can be any string, it's used for testing ads @@ -212,7 +199,8 @@ export const spec = { let placementId = placementIdOverrideFromGetParam || this.getPlacementId(ozoneBidRequest); // prefer to use a valid override param, else the bidRequest placement Id obj.id = ozoneBidRequest.bidId; // this causes an error if we change it to something else, even if you update the bidRequest object: "WARNING: Bidder ozone made bid for unknown request ID: mb7953.859498327448. Ignoring." obj.tagid = placementId; - obj.secure = window.location.protocol === 'https:' ? 1 : 0; + let parsed = parseUrl(getRefererInfo().page); + obj.secure = parsed.protocol === 'https' ? 1 : 0; let arrBannerSizes = []; if (!ozoneBidRequest.hasOwnProperty('mediaTypes')) { if (ozoneBidRequest.hasOwnProperty('sizes')) { @@ -292,7 +280,7 @@ export const spec = { } } if (fpd && deepAccess(fpd, 'site')) { - logInfo('added fpd.site'); + logInfo('adding fpd.site'); if (deepAccess(obj, 'ext.' + whitelabelBidder + '.customData.0.targeting', false)) { obj.ext[whitelabelBidder].customData[0].targeting = Object.assign(obj.ext[whitelabelBidder].customData[0].targeting, fpd.site); } else { @@ -304,7 +292,6 @@ export const spec = { } return obj; }); - let extObj = {}; extObj[whitelabelBidder] = {}; extObj[whitelabelBidder][whitelabelPrefix + '_pb_v'] = OZONEVERSION; @@ -315,8 +302,7 @@ export const spec = { extObj[whitelabelBidder].pubcid = userIds.pubcid; } } - - extObj[whitelabelBidder].pv = this.getPageId(); // attach the page ID that will be common to all auciton calls for this page if refresh() is called + extObj[whitelabelBidder].pv = this.getPageId(); // attach the page ID that will be common to all auction calls for this page if refresh() is called let ozOmpFloorDollars = this.getWhitelabelConfigItem('ozone.oz_omp_floor'); // valid only if a dollar value (typeof == 'number') logInfo(`${whitelabelPrefix}_omp_floor dollar value = `, ozOmpFloorDollars); if (typeof ozOmpFloorDollars === 'number') { @@ -327,25 +313,22 @@ export const spec = { let ozWhitelistAdserverKeys = this.getWhitelabelConfigItem('ozone.oz_whitelist_adserver_keys'); let useOzWhitelistAdserverKeys = isArray(ozWhitelistAdserverKeys) && ozWhitelistAdserverKeys.length > 0; extObj[whitelabelBidder][whitelabelPrefix + '_kvp_rw'] = useOzWhitelistAdserverKeys ? 1 : 0; - if (whitelabelBidder != 'ozone') { + if (whitelabelBidder !== 'ozone') { logInfo('setting aliases object'); extObj.prebid = {aliases: {'ozone': whitelabelBidder}}; } - if (getParams.hasOwnProperty('ozf')) { extObj[whitelabelBidder]['ozf'] = getParams.ozf == 'true' || getParams.ozf == 1 ? 1 : 0; } - if (getParams.hasOwnProperty('ozpf')) { extObj[whitelabelBidder]['ozpf'] = getParams.ozpf == 'true' || getParams.ozpf == 1 ? 1 : 0; } + if (getParams.hasOwnProperty('ozf')) { extObj[whitelabelBidder]['ozf'] = getParams.ozf === 'true' || getParams.ozf === '1' ? 1 : 0; } + if (getParams.hasOwnProperty('ozpf')) { extObj[whitelabelBidder]['ozpf'] = getParams.ozpf === 'true' || getParams.ozpf === '1' ? 1 : 0; } if (getParams.hasOwnProperty('ozrp') && getParams.ozrp.match(/^[0-3]$/)) { extObj[whitelabelBidder]['ozrp'] = parseInt(getParams.ozrp); } if (getParams.hasOwnProperty('ozip') && getParams.ozip.match(/^\d+$/)) { extObj[whitelabelBidder]['ozip'] = parseInt(getParams.ozip); } if (this.propertyBag.endpointOverride != null) { extObj[whitelabelBidder]['origin'] = this.propertyBag.endpointOverride; } - - var userExtEids = this.generateEids(validBidRequests); // generate the UserIDs in the correct format for UserId module - + let userExtEids = deepAccess(validBidRequests, '0.userIdAsEids', []); // generate the UserIDs in the correct format for UserId module ozoneRequest.site = { 'publisher': {'id': htmlParams.publisherId}, - 'page': document.location.href, + 'page': getRefererInfo().page, 'id': htmlParams.siteId }; - ozoneRequest.test = (getParams.hasOwnProperty('pbjs_debug') && getParams['pbjs_debug'] === 'true') ? 1 : 0; - + ozoneRequest.test = config.getConfig('debug') ? 1 : 0; if (bidderRequest && bidderRequest.gdprConsent) { logInfo('ADDING GDPR info'); let apiVersion = deepAccess(bidderRequest, 'gdprConsent.apiVersion', 1); @@ -359,20 +342,18 @@ export const spec = { logInfo('WILL NOT ADD GDPR info; no bidderRequest.gdprConsent object'); } if (bidderRequest && bidderRequest.uspConsent) { - logInfo('ADDING CCPA info'); - deepSetValue(ozoneRequest, 'user.ext.uspConsent', bidderRequest.uspConsent); + logInfo('ADDING USP consent info'); + deepSetValue(ozoneRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); } else { - logInfo('WILL NOT ADD CCPA info; no bidderRequest.uspConsent.'); + logInfo('WILL NOT ADD USP consent info; no bidderRequest.uspConsent.'); } if (schain) { // we set this while iterating over the bids logInfo('schain found'); deepSetValue(ozoneRequest, 'source.ext.schain', schain); } - if (config.getConfig('coppa') === true) { deepSetValue(ozoneRequest, 'regs.coppa', 1); } - if (singleRequest) { logInfo('buildRequests starting to generate response for a single request'); ozoneRequest.id = bidderRequest.auctionId; // Unique ID of the bid request, provided by the exchange. @@ -414,19 +395,6 @@ export const spec = { logInfo(`buildRequests going to return for non-single at time ${this.propertyBag.buildRequestsEnd} (took ${this.propertyBag.buildRequestsEnd - this.propertyBag.buildRequestsStart}ms): `, arrRet); return arrRet; }, - /** - * parse a bidRequestRef that contains getFloor(), get all the data from it for the sizes & media requested for this bid & return an object containing floor data you can send to auciton endpoint - * @param bidRequestRef object = a valid bid request object reference - * @return object - * - * call: - * bidObj.getFloor({ - currency: 'USD', <- currency to return the value in - mediaType: ‘banner’, - size: ‘*’ <- or [300,250] or [[300,250],[640,480]] - * }); - * - */ getFloorObjectForAuction(bidRequestRef) { const mediaTypesSizes = { banner: deepAccess(bidRequestRef, 'mediaTypes.banner.sizes', null), @@ -447,16 +415,6 @@ export const spec = { logInfo('getFloorObjectForAuction returning : ', JSON.parse(JSON.stringify(ret))); return ret; }, - /** - * Interpret the response if the array contains BIDDER elements, in the format: [ [bidder1 bid 1, bidder1 bid 2], [bidder2 bid 1, bidder2 bid 2] ] - * NOte that in singleRequest mode this will be called once, else it will be called for each adSlot's response - * - * Updated April 2019 to return all bids, not just the one we decide is the 'winner' - * - * @param serverResponse - * @param request - * @returns {*} - */ interpretResponse(serverResponse, request) { if (request && request.bidderRequest && request.bidderRequest.bids) { this.loadWhitelabelData(request.bidderRequest.bids[0]); } let startTime = new Date().getTime(); @@ -478,15 +436,12 @@ export const spec = { enhancedAdserverTargeting = true; } logInfo('enhancedAdserverTargeting', enhancedAdserverTargeting); - serverResponse.seatbid = injectAdIdsIntoAllBidResponses(serverResponse.seatbid); // we now make sure that each bid in the bidresponse has a unique (within page) adId attribute. - serverResponse.seatbid = this.removeSingleBidderMultipleBids(serverResponse.seatbid); let ozOmpFloorDollars = this.getWhitelabelConfigItem('ozone.oz_omp_floor'); // valid only if a dollar value (typeof == 'number') let addOzOmpFloorDollars = typeof ozOmpFloorDollars === 'number'; let ozWhitelistAdserverKeys = this.getWhitelabelConfigItem('ozone.oz_whitelist_adserver_keys'); let useOzWhitelistAdserverKeys = isArray(ozWhitelistAdserverKeys) && ozWhitelistAdserverKeys.length > 0; - for (let i = 0; i < serverResponse.seatbid.length; i++) { let sb = serverResponse.seatbid[i]; for (let j = 0; j < sb.bid.length; j++) { @@ -499,17 +454,30 @@ export const spec = { let isVideo = false; let bidType = deepAccess(thisBid, 'ext.prebid.type'); logInfo(`this bid type is : ${bidType}`, j); + let adserverTargeting = {}; if (bidType === VIDEO) { isVideo = true; + this.setBidMediaTypeIfNotExist(thisBid, VIDEO); videoContext = this.getVideoContextForBidId(thisBid.bidId, request.bidderRequest.bids); // should be instream or outstream (or null if error) if (videoContext === 'outstream') { - logInfo('going to attach a renderer to OUTSTREAM video : ', j); + logInfo('going to set thisBid.mediaType = VIDEO & attach a renderer to OUTSTREAM video : ', j); thisBid.renderer = newRenderer(thisBid.bidId); } else { - logInfo('bid is not an outstream video, will not attach a renderer: ', j); + logInfo('bid is not an outstream video, will set thisBid.mediaType = VIDEO and thisBid.vastUrl and not attach a renderer: ', j); + thisBid.vastUrl = `https://${deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_host', 'missing_host')}${deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_path', 'missing_path')}?id=${deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_id', 'missing_id')}`; // need to see if this works ok for ozone + adserverTargeting['hb_cache_host'] = deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_host', 'no-host'); + adserverTargeting['hb_cache_path'] = deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_path', 'no-path'); + if (!thisBid.hasOwnProperty('videoCacheKey')) { + let videoCacheUuid = deepAccess(thisBid, 'ext.prebid.targeting.hb_uuid', 'no_hb_uuid'); + logInfo(`Adding videoCacheKey: ${videoCacheUuid}`); + thisBid.videoCacheKey = videoCacheUuid; + } else { + logInfo('videoCacheKey already exists on the bid object, will not add it'); + } } + } else { + this.setBidMediaTypeIfNotExist(thisBid, BANNER); } - let adserverTargeting = {}; if (enhancedAdserverTargeting) { let allBidsForThisBidid = ozoneGetAllBidsForBidId(thisBid.bidId, serverResponse.seatbid); logInfo('Going to iterate allBidsForThisBidId', allBidsForThisBidid); @@ -552,7 +520,8 @@ export const spec = { adserverTargeting[whitelabelPrefix + '_auc_id'] = String(request.bidderRequest.auctionId); adserverTargeting[whitelabelPrefix + '_winner'] = String(winningSeat); adserverTargeting[whitelabelPrefix + '_bid'] = 'true'; - + adserverTargeting[whitelabelPrefix + '_cache_id'] = deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_id', 'no-id'); + adserverTargeting[whitelabelPrefix + '_uuid'] = deepAccess(thisBid, 'ext.prebid.targeting.hb_uuid', 'no-id'); if (enhancedAdserverTargeting) { adserverTargeting[whitelabelPrefix + '_imp_id'] = String(winningBid.impid); adserverTargeting[whitelabelPrefix + '_pb_v'] = OZONEVERSION; @@ -570,26 +539,24 @@ export const spec = { } } let endTime = new Date().getTime(); - logInfo(`interpretResponse going to return at time ${endTime} (took ${endTime - startTime}ms) Time from buildRequests Start -> interpretRequests End = ${endTime - this.propertyBag.buildRequestsStart}ms`, arrAllBids); + logInfo(`interpretResponse going to return at time ${endTime} (took ${endTime - startTime}ms) Time from buildRequests Start -> interpretRequests End = ${endTime - this.propertyBag.buildRequestsStart}ms`); + logInfo('interpretResponse arrAllBids (serialised): ', JSON.parse(JSON.stringify(arrAllBids))); // this is ok to log because the renderer has not been attached yet return arrAllBids; }, - /** - * Use this to get all config values - * Now it's getting complicated with whitelabeling, this simplifies the code for getting config values. - * eg. to get whitelabelled version you just sent the ozone default string like ozone.oz_omp_floor - * @param ozoneVersion string like 'ozone.oz_omp_floor' - * @return {string|object} - */ + setBidMediaTypeIfNotExist(thisBid, mediaType) { + if (!thisBid.hasOwnProperty('mediaType')) { + logInfo(`setting thisBid.mediaType = ${mediaType}`); + thisBid.mediaType = mediaType; + } else { + logInfo(`found value for thisBid.mediaType: ${thisBid.mediaType}`); + } + }, getWhitelabelConfigItem(ozoneVersion) { - if (this.propertyBag.whitelabel.bidder == 'ozone') { return config.getConfig(ozoneVersion); } + if (this.propertyBag.whitelabel.bidder === 'ozone') { return config.getConfig(ozoneVersion); } let whitelabelledSearch = ozoneVersion.replace('ozone', this.propertyBag.whitelabel.bidder); whitelabelledSearch = whitelabelledSearch.replace('oz_', this.propertyBag.whitelabel.keyPrefix + '_'); return config.getConfig(whitelabelledSearch); }, - /** - * If a bidder bids for > 1 size for an adslot, allow only the highest bid - * @param seatbid object (serverResponse.seatbid) - */ removeSingleBidderMultipleBids(seatbid) { var ret = []; for (let i = 0; i < seatbid.length; i++) { @@ -620,7 +587,7 @@ export const spec = { } if (optionsType.iframeEnabled) { var arrQueryString = []; - if (document.location.search.match(/pbjs_debug=true/)) { + if (config.getConfig('debug')) { arrQueryString.push('pbjs_debug=true'); } arrQueryString.push('gdpr=' + (deepAccess(gdprConsent, 'gdprApplies', false) ? '1' : '0')); @@ -633,7 +600,6 @@ export const spec = { arrQueryString.push('siteId=' + this.cookieSyncBag.siteId); arrQueryString.push('cb=' + Date.now()); arrQueryString.push('bidder=' + this.propertyBag.whitelabel.bidder); - var strQueryString = arrQueryString.join('&'); if (strQueryString.length > 0) { strQueryString = '?' + strQueryString; @@ -645,11 +611,6 @@ export const spec = { }]; } }, - /** - * Find the bid matching the bidId in the request object - * get instream or outstream if this was a video request else null - * @return object|null - */ getBidRequestForBidId(bidId, arrBids) { for (let i = 0; i < arrBids.length; i++) { if (arrBids[i].bidId === bidId) { // bidId in the request comes back as impid in the seatbid bids @@ -658,13 +619,6 @@ export const spec = { } return null; }, - /** - * Locate the bid inside the arrBids for this bidId, then discover the video context, and return it. - * IF the bid cannot be found return null, else return a string. - * @param bidId - * @param arrBids - * @return string|null - */ getVideoContextForBidId(bidId, arrBids) { let requestBid = this.getBidRequestForBidId(bidId, arrBids); if (requestBid != null) { @@ -672,11 +626,6 @@ export const spec = { } return null; }, - /** - * This is used for cookie sync, not auction call - * Look for pubcid & all the other IDs according to http://prebid.org/dev-docs/modules/userId.html - * @return map - */ findAllUserIds(bidRequest) { var ret = {}; let searchKeysSingle = ['pubcid', 'tdid', 'idl_env', 'criteoId', 'lotamePanoramaId', 'fabrickId']; @@ -710,10 +659,6 @@ export const spec = { if (sharedid) { ret['sharedid'] = sharedid; } - let sharedidthird = deepAccess(bidRequest.userId, 'sharedid.third'); - if (sharedidthird) { - ret['sharedidthird'] = sharedidthird; - } } if (!ret.hasOwnProperty('pubcid')) { let pubcid = deepAccess(bidRequest, 'crumbs.pubcid'); @@ -723,20 +668,9 @@ export const spec = { } return ret; }, - /** - * Convenient method to get the value we need for the placementId - ONLY from the bidRequest - NOT taking into account any GET override ID - * @param bidRequest - * @return string - */ getPlacementId(bidRequest) { return (bidRequest.params.placementId).toString(); }, - /** - * GET parameter introduced in 2.2.0 : ozstoredrequest - * IF the GET parameter exists then it must validate for placementId correctly - * IF there's a $_GET['ozstoredrequest'] & it's valid then return this. Else return null. - * @returns null|string - */ getPlacementIdOverrideFromGetParam() { let whitelabelPrefix = this.propertyBag.whitelabel.keyPrefix; let arr = this.getGetParametersAsObject(); @@ -750,68 +684,19 @@ export const spec = { } return null; }, - /** - * Generate an object we can append to the auction request, containing user data formatted correctly for different ssps - * http://prebid.org/dev-docs/modules/userId.html - * @param validBidRequests - * @return {Array} - */ - generateEids(validBidRequests) { - let eids; - const bidRequest = validBidRequests[0]; - if (bidRequest && bidRequest.userId) { - eids = bidRequest.userIdAsEids; - this.handleTTDId(eids, validBidRequests); - } - return eids; - }, - handleTTDId(eids, validBidRequests) { - let ttdId = null; - let adsrvrOrgId = config.getConfig('adsrvrOrgId'); - if (isStr(deepAccess(validBidRequests, '0.userId.tdid'))) { - ttdId = validBidRequests[0].userId.tdid; - } else if (adsrvrOrgId && isStr(adsrvrOrgId.TDID)) { - ttdId = adsrvrOrgId.TDID; - } - if (ttdId !== null) { - eids.push({ - 'source': 'adserver.org', - 'uids': [{ - 'id': ttdId, - 'atype': 1, - 'ext': { - 'rtiPartner': 'TDID' - } - }] - }); - } - }, getGetParametersAsObject() { - let items = location.search.substr(1).split('&'); - let ret = {}; - let tmp = null; - for (let index = 0; index < items.length; index++) { - tmp = items[index].split('='); - ret[tmp[0]] = tmp[1]; - } - return ret; + let parsed = parseUrl(getRefererInfo().page); + logInfo('getGetParametersAsObject found:', parsed.search); + return parsed.search; }, - /** - * Do we have to block this request? Could be due to config values (no longer checking gdpr) - * @return {boolean|*[]} true = block the request, else false - */ blockTheRequest() { let ozRequest = this.getWhitelabelConfigItem('ozone.oz_request'); if (typeof ozRequest == 'boolean' && !ozRequest) { - logWarn(`Will not allow auction : ${this.propertyBag.whitelabel.keyPrefix}one.${this.propertyBag.whitelabel.keyPrefix}_request is set to false`); + logWarn(`Will not allow auction : ${this.propertyBag.whitelabel.keyPrefix}_request is set to false`); return true; } return false; }, - /** - * This returns a random ID for this page. It starts off with the current ms timestamp then appends a random component - * @return {string} - */ getPageId: function() { if (this.propertyBag.pageId == null) { let randPart = ''; @@ -829,14 +714,6 @@ export const spec = { ret = this._unpackVideoConfigIntoIABformat(ret, childConfig); return ret; }, - /** - * - * look in ONE object to get video config (we need to call this multiple times, so child settings override parent) - * @param ret - * @param objConfig - * @return {*} - * @private - */ _unpackVideoConfigIntoIABformat(ret, objConfig) { let arrVideoKeysAllowed = ['mimes', 'minduration', 'maxduration', 'protocols', 'w', 'h', 'startdelay', 'placement', 'linearity', 'skip', 'skipmin', 'skipafter', 'sequence', 'battr', 'maxextended', 'minbitrate', 'maxbitrate', 'boxingallowed', 'playbackmethod', 'playbackend', 'delivery', 'pos', 'companionad', 'api', 'companiontype']; for (const key in objConfig) { @@ -865,14 +742,6 @@ export const spec = { objRet = this._addVideoDefaults(objRet, childConfig, true); // child config will override parent config return objRet; }, - /** - * modify objRet, adding in default values - * @param objRet - * @param objConfig - * @param addIfMissing - * @return {*} - * @private - */ _addVideoDefaults(objRet, objConfig, addIfMissing) { let context = deepAccess(objConfig, 'context'); if (context === 'outstream') { @@ -889,15 +758,38 @@ export const spec = { objRet.skip = skippable ? 1 : 0; } return objRet; + }, + getLoggableBidObject(bid) { + let logObj = { + ad: bid.ad, + adId: bid.adId, + adUnitCode: bid.adUnitCode, + adm: bid.adm, + adomain: bid.adomain, + adserverTargeting: bid.adserverTargeting, + auctionId: bid.auctionId, + bidId: bid.bidId, + bidder: bid.bidder, + bidderCode: bid.bidderCode, + cpm: bid.cpm, + creativeId: bid.creativeId, + crid: bid.crid, + currency: bid.currency, + h: bid.h, + w: bid.w, + impid: bid.impid, + mediaType: bid.mediaType, + params: bid.params, + price: bid.price, + transactionId: bid.transactionId, + ttl: bid.ttl + }; + if (bid.hasOwnProperty('floorData')) { + logObj.floorData = bid.floorData; + } + return logObj; } }; - -/** - * add a page-level-unique adId element to all server response bids. - * NOTE that this is destructive - it mutates the serverResponse object sent in as a parameter - * @param seatbid object (serverResponse.seatbid) - * @returns seatbid object - */ export function injectAdIdsIntoAllBidResponses(seatbid) { logInfo('injectAdIdsIntoAllBidResponses', seatbid); for (let i = 0; i < seatbid.length; i++) { @@ -908,7 +800,6 @@ export function injectAdIdsIntoAllBidResponses(seatbid) { } return seatbid; } - export function checkDeepArray(Arr) { if (Array.isArray(Arr)) { if (Array.isArray(Arr[0])) { @@ -920,7 +811,6 @@ export function checkDeepArray(Arr) { return Arr; } } - export function defaultSize(thebidObj) { if (!thebidObj) { logInfo('defaultSize received empty bid obj! going to return fixed default size'); @@ -935,13 +825,6 @@ export function defaultSize(thebidObj) { returnObject.defaultHeight = checkDeepArray(sizes)[1]; return returnObject; } - -/** - * Do the messy searching for the best bid response in the serverResponse.seatbid array matching the requestBid.bidId - * @param requestBid - * @param serverResponseSeatBid - * @returns {*} bid object - */ export function ozoneGetWinnerForRequestBid(requestBidId, serverResponseSeatBid) { let thisBidWinner = null; let winningSeat = null; @@ -960,13 +843,6 @@ export function ozoneGetWinnerForRequestBid(requestBidId, serverResponseSeatBid) } return {'seat': winningSeat, 'bid': thisBidWinner}; } - -/** - * Get a list of all the bids, for this bidId. The keys in the response object will be {seatname} OR {seatname}{w}x{h} if seatname already exists - * @param matchBidId - * @param serverResponseSeatBid - * @returns {} = {ozone|320x600:{obj}, ozone|320x250:{obj}, appnexus|300x250:{obj}, ... } - */ export function ozoneGetAllBidsForBidId(matchBidId, serverResponseSeatBid) { let objBids = {}; for (let j = 0; j < serverResponseSeatBid.length; j++) { @@ -986,21 +862,13 @@ export function ozoneGetAllBidsForBidId(matchBidId, serverResponseSeatBid) { } return objBids; } - -/** - * Round the bid price down according to the granularity - * @param price - * @param mediaType = video, banner or native - */ export function getRoundedBid(price, mediaType) { const mediaTypeGranularity = config.getConfig(`mediaTypePriceGranularity.${mediaType}`); // might be string or object or nothing; if set then this takes precedence over 'priceGranularity' let objBuckets = config.getConfig('customPriceBucket'); // this is always an object - {} if strBuckets is not 'custom' let strBuckets = config.getConfig('priceGranularity'); // priceGranularity value, always a string ** if priceGranularity is set to an object then it's always 'custom' ** let theConfigObject = getGranularityObject(mediaType, mediaTypeGranularity, strBuckets, objBuckets); let theConfigKey = getGranularityKeyName(mediaType, mediaTypeGranularity, strBuckets); - logInfo('getRoundedBid. price:', price, 'mediaType:', mediaType, 'configkey:', theConfigKey, 'configObject:', theConfigObject, 'mediaTypeGranularity:', mediaTypeGranularity, 'strBuckets:', strBuckets); - let priceStringsObj = getPriceBucketString( price, theConfigObject, @@ -1021,13 +889,6 @@ export function getRoundedBid(price, mediaType) { } return priceStringsObj['auto']; } - -/** - * return the key to use to get the value out of the priceStrings object, taking into account anything at - * config.priceGranularity level or config.mediaType.xxx level - * I've noticed that the key specified by prebid core : config.getConfig('priceGranularity') does not properly - * take into account the 2-levels of config - */ export function getGranularityKeyName(mediaType, mediaTypeGranularity, strBuckets) { if (typeof mediaTypeGranularity === 'string') { return mediaTypeGranularity; @@ -1040,11 +901,6 @@ export function getGranularityKeyName(mediaType, mediaTypeGranularity, strBucket } return 'auto'; // fall back to a default key - should literally never be needed. } - -/** - * return the object to use to create the custom value of the priceStrings object, taking into account anything at - * config.priceGranularity level or config.mediaType.xxx level - */ export function getGranularityObject(mediaType, mediaTypeGranularity, strBuckets, objBuckets) { if (typeof mediaTypeGranularity === 'object') { return mediaTypeGranularity; @@ -1054,12 +910,6 @@ export function getGranularityObject(mediaType, mediaTypeGranularity, strBuckets } return ''; } - -/** - * We expect to be able to find a standard set of properties on winning bid objects; add them here. - * @param seatBid - * @returns {*} - */ export function ozoneAddStandardProperties(seatBid, defaultWidth, defaultHeight) { seatBid.cpm = seatBid.price; seatBid.bidId = seatBid.impid; @@ -1073,12 +923,6 @@ export function ozoneAddStandardProperties(seatBid, defaultWidth, defaultHeight) seatBid.ttl = 300; return seatBid; } - -/** - * - * @param objVideo will be like {"playerSize":[640,480],"mimes":["video/mp4"],"context":"outstream"} or POSSIBLY {"playerSize":[[640,480]],"mimes":["video/mp4"],"context":"outstream"} - * @return object {w,h} or null - */ export function getWidthAndHeightFromVideoObject(objVideo) { let playerSize = getPlayerSizeFromObject(objVideo); if (!playerSize) { @@ -1098,11 +942,6 @@ export function getWidthAndHeightFromVideoObject(objVideo) { } return ({'w': playerSize[0], 'h': playerSize[1]}); } - -/** - * @param objVideo will be like {"playerSize":[640,480],"mimes":["video/mp4"],"context":"outstream"} or POSSIBLY {"playerSize":[[640,480]],"mimes":["video/mp4"],"context":"outstream"} - * @return object {w,h} or null - */ export function playerSizeIsNestedArray(objVideo) { let playerSize = getPlayerSizeFromObject(objVideo); if (!playerSize) { @@ -1113,12 +952,6 @@ export function playerSizeIsNestedArray(objVideo) { } return (playerSize[0] && typeof playerSize[0] === 'object'); } - -/** - * Common functionality when looking at a video object, to get the playerSize - * @param objVideo - * @returns {*} - */ function getPlayerSizeFromObject(objVideo) { logInfo('getPlayerSizeFromObject received object', objVideo); let playerSize = deepAccess(objVideo, 'playerSize'); @@ -1135,10 +968,6 @@ function getPlayerSizeFromObject(objVideo) { } return playerSize; } -/* - Rendering video ads - create a renderer instance, mark it as not loaded, set a renderer function. - The renderer function will not assume that the renderer script is loaded - it will push() the ultimate render function call - */ function newRenderer(adUnitCode, rendererOptions = {}) { let isLoaded = window.ozoneVideo; logInfo(`newRenderer going to set loaded to ${isLoaded ? 'true' : 'false'}`); @@ -1151,16 +980,18 @@ function newRenderer(adUnitCode, rendererOptions = {}) { try { renderer.setRender(outstreamRender); } catch (err) { - logError('Prebid Error when calling setRender on renderer', JSON.parse(JSON.stringify(renderer)), err); + logError('Prebid Error when calling setRender on renderer', renderer, err); } + logInfo('returning renderer object'); return renderer; } function outstreamRender(bid) { - logInfo('outstreamRender called. Going to push the call to window.ozoneVideo.outstreamRender(bid) bid =', JSON.parse(JSON.stringify(bid))); + logInfo('outstreamRender called. Going to push the call to window.ozoneVideo.outstreamRender(bid) bid = (first static, then reference)'); + logInfo(JSON.parse(JSON.stringify(spec.getLoggableBidObject(bid)))); bid.renderer.push(() => { + logInfo('Going to execute window.ozoneVideo.outstreamRender'); window.ozoneVideo.outstreamRender(bid); }); } - registerBidder(spec); logInfo(`*BidAdapter ${OZONEVERSION} was loaded`); diff --git a/modules/ozoneBidAdapter.md b/modules/ozoneBidAdapter.md index ca18c962219..6f4cf752f22 100644 --- a/modules/ozoneBidAdapter.md +++ b/modules/ozoneBidAdapter.md @@ -72,3 +72,40 @@ adUnits = [{ }] }]; ``` + +//Instream Video adUnit + +adUnits = [{ + code: 'video1', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream', + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + playbackmethod: [2] + } + }, + bids: [{ + bidder: 'ozone', + params: { + publisherId: 'OZONENUK0001', + placementId: '8000000328', // or 999 + siteId: '4204204201', + video: { + skippable: true, + playback_method: ['auto_play_sound_off'] + }, + customData: [{ + "settings": {}, + "targeting": { + "key": "value", + "key2": ["value1", "value2"] + } + } + ] + + } + }] + }; +``` diff --git a/test/spec/modules/ozoneBidAdapter_spec.js b/test/spec/modules/ozoneBidAdapter_spec.js index b29494e0b54..4c7e330b237 100644 --- a/test/spec/modules/ozoneBidAdapter_spec.js +++ b/test/spec/modules/ozoneBidAdapter_spec.js @@ -7,12 +7,6 @@ import * as utils from '../../../src/utils.js'; const OZONEURI = 'https://elb.the-ozone-project.com/openrtb2/auction'; const BIDDER_CODE = 'ozone'; -/* - -NOTE - use firefox console to deep copy the objects to use here - - */ -var originalPropertyBag = {'pageId': null}; var validBidRequests = [ { adUnitCode: 'div-gpt-ad-1460505748561-0', @@ -147,7 +141,6 @@ var validBidRequestsWithUserIdData = [ }] } ] - } ]; var validBidRequestsMinimal = [ @@ -176,7 +169,6 @@ var validBidRequestsNoSizes = [ transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } ]; - var validBidRequestsWithBannerMediaType = [ { adUnitCode: 'div-gpt-ad-1460505748561-0', @@ -205,7 +197,6 @@ var validBidRequestsWithNonBannerMediaTypesAndValidOutstreamVideo = [ transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } ]; - var validBidRequests1OutstreamVideo2020 = [ { 'bidder': 'ozone', @@ -288,7 +279,6 @@ var validBidRequests1OutstreamVideo2020 = [ 'bidderWinsCount': 0 } ]; - var validBidderRequest1OutstreamVideo2020 = { bidderRequest: { auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', @@ -392,84 +382,79 @@ var validBidderRequest1OutstreamVideo2020 = { } }; var validBidderRequest = { - bidderRequest: { + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + auctionStart: 1536838908986, + bidderCode: 'ozone', + bidderRequestId: '1c1586b27a1b5c8', + bids: [{ + adUnitCode: 'div-gpt-ad-1460505748561-0', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', - auctionStart: 1536838908986, - bidderCode: 'ozone', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', - bids: [{ - adUnitCode: 'div-gpt-ad-1460505748561-0', - auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', - bidId: '2899ec066a91ff8', - bidRequestsCount: 1, - bidder: 'ozone', - bidderRequestId: '1c1586b27a1b5c8', - crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, - sizes: [[300, 250], [300, 600]], - transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' - }], - doneCbCallCount: 1, - start: 1536838908987, - timeout: 3000 - } + crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, + sizes: [[300, 250], [300, 600]], + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + }], + doneCbCallCount: 1, + start: 1536838908987, + timeout: 3000 }; - var bidderRequestWithFullGdpr = { - bidderRequest: { + auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', + auctionStart: 1536838908986, + bidderCode: 'ozone', + bidderRequestId: '1c1586b27a1b5c8', + bids: [{ + adUnitCode: 'div-gpt-ad-1460505748561-0', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', - auctionStart: 1536838908986, - bidderCode: 'ozone', + bidId: '2899ec066a91ff8', + bidRequestsCount: 1, + bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', - bids: [{ - adUnitCode: 'div-gpt-ad-1460505748561-0', - auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', - bidId: '2899ec066a91ff8', - bidRequestsCount: 1, - bidder: 'ozone', - bidderRequestId: '1c1586b27a1b5c8', - crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, - sizes: [[300, 250], [300, 600]], - transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' - }], - doneCbCallCount: 1, - start: 1536838908987, - timeout: 3000, - gdprConsent: { - 'consentString': 'BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA', - 'vendorData': { - 'metadata': 'BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA', - 'gdprApplies': true, - 'hasGlobalScope': false, - 'cookieVersion': '1', - 'created': '2019-05-31T12:46:48.825', - 'lastUpdated': '2019-05-31T12:46:48.825', - 'cmpId': '28', - 'cmpVersion': '1', - 'consentLanguage': 'en', - 'consentScreen': '1', - 'vendorListVersion': 148, - 'maxVendorId': 631, - 'purposeConsents': { - '1': true, - '2': true, - '3': true, - '4': true, - '5': true - }, - 'vendorConsents': { - '468': true, - '522': true, - '524': true, /* 524 is ozone */ - '565': true, - '591': true - } + crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, + sizes: [[300, 250], [300, 600]], + transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' + }], + doneCbCallCount: 1, + start: 1536838908987, + timeout: 3000, + gdprConsent: { + 'consentString': 'BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA', + 'vendorData': { + 'metadata': 'BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA', + 'gdprApplies': true, + 'hasGlobalScope': false, + 'cookieVersion': '1', + 'created': '2019-05-31T12:46:48.825', + 'lastUpdated': '2019-05-31T12:46:48.825', + 'cmpId': '28', + 'cmpVersion': '1', + 'consentLanguage': 'en', + 'consentScreen': '1', + 'vendorListVersion': 148, + 'maxVendorId': 631, + 'purposeConsents': { + '1': true, + '2': true, + '3': true, + '4': true, + '5': true }, - 'gdprApplies': true - }, } + 'vendorConsents': { + '468': true, + '522': true, + '524': true, /* 524 is ozone */ + '565': true, + '591': true + } + }, + 'gdprApplies': true + } }; - var gdpr1 = { 'consentString': 'BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA', 'vendorData': { @@ -502,7 +487,6 @@ var gdpr1 = { }, 'gdprApplies': true }; - var bidderRequestWithPartialGdpr = { bidderRequest: { auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', @@ -547,7 +531,6 @@ var bidderRequestWithPartialGdpr = { } } }; - var validResponse = { 'body': { 'id': 'd6198807-7a53-4141-b2db-d2cb754d68ba', @@ -604,7 +587,6 @@ var validResponse = { }, 'headers': {} }; - var validResponse2Bids = { 'body': { 'id': 'd6198807-7a53-4141-b2db-d2cb754d68ba', @@ -691,9 +673,6 @@ var validResponse2Bids = { }, 'headers': {} }; -/* -A bidder returns a bid for both sizes in an adunit - */ var validResponse2BidsSameAdunit = { 'body': { 'id': 'd6198807-7a53-4141-b2db-d2cb754d68ba', @@ -780,14 +759,6 @@ var validResponse2BidsSameAdunit = { }, 'headers': {} }; -/* - -SPECIAL CONSIDERATION FOR VIDEO TESTS: - -DO NOT USE _validVideoResponse directly - the interpretResponse function will modify it (adding a renderer!!!) so all -subsequent calls will already have a renderer attached!!! - -*/ function getCleanValidVideoResponse() { return JSON.parse(JSON.stringify(_validVideoResponse)); } @@ -873,7 +844,6 @@ var _validVideoResponse = { }, 'headers': {} }; - var validBidResponse1adWith2Bidders = { 'body': { 'id': '91221f96-b931-4acc-8f05-c2a1186fa5ac', @@ -964,11 +934,6 @@ var validBidResponse1adWith2Bidders = { }, 'headers': {} }; - -/* -testing 2 ads, 2 bidders, one bidder bids for both slots in one adunit - */ - var multiRequest1 = [ { 'bidder': 'ozone', @@ -1101,182 +1066,178 @@ var multiRequest1 = [ 'bidderWinsCount': 0 } ]; - var multiBidderRequest1 = { - bidderRequest: { - 'bidderCode': 'ozone', - 'auctionId': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', - 'bidderRequestId': '1d03a1dfc563fc', - 'bids': [ - { - 'bidder': 'ozone', - 'params': { - 'publisherId': 'OZONERUP0001', - 'siteId': '4204204201', - 'placementId': '0420420421', - 'customData': [ - { - 'settings': {}, - 'targeting': { - 'sens': 'f', - 'pt1': '/uk', - 'pt2': 'uk', - 'pt3': 'network-front', - 'pt4': 'ng', - 'pt5': [ - 'uk' - ], - 'pt7': 'desktop', - 'pt8': [ - 'tfmqxwj7q', - 'txeh7uyo0', - 't8nxz6qzd', - 't8nyiude5', - 'sek9ghqwi' - ], - 'pt9': '|k0xw2vqzp33kklb3j5w4|||' - } - } - ] - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 300, - 250 + 'bidderCode': 'ozone', + 'auctionId': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', + 'bidderRequestId': '1d03a1dfc563fc', + 'bids': [ + { + 'bidder': 'ozone', + 'params': { + 'publisherId': 'OZONERUP0001', + 'siteId': '4204204201', + 'placementId': '0420420421', + 'customData': [ + { + 'settings': {}, + 'targeting': { + 'sens': 'f', + 'pt1': '/uk', + 'pt2': 'uk', + 'pt3': 'network-front', + 'pt4': 'ng', + 'pt5': [ + 'uk' ], - [ - 300, - 600 - ] - ] + 'pt7': 'desktop', + 'pt8': [ + 'tfmqxwj7q', + 'txeh7uyo0', + 't8nxz6qzd', + 't8nyiude5', + 'sek9ghqwi' + ], + 'pt9': '|k0xw2vqzp33kklb3j5w4|||' + } } - }, - 'adUnitCode': 'mpu', - 'transactionId': '6480bac7-31b5-4723-9145-ad8966660651', - 'sizes': [ - [ - 300, - 250 - ], - [ - 300, - 600 - ] - ], - 'bidId': '2d30e86db743a8', - 'bidderRequestId': '1d03a1dfc563fc', - 'auctionId': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', - 'src': 'client', - 'bidRequestsCount': 1, - 'bidderRequestsCount': 1, - 'bidderWinsCount': 0 + ] }, - { - 'bidder': 'ozone', - 'params': { - 'publisherId': 'OZONERUP0001', - 'siteId': '4204204201', - 'placementId': '0420420421', - 'customData': [ - { - 'settings': {}, - 'targeting': { - 'sens': 'f', - 'pt1': '/uk', - 'pt2': 'uk', - 'pt3': 'network-front', - 'pt4': 'ng', - 'pt5': [ - 'uk' - ], - 'pt7': 'desktop', - 'pt8': [ - 'tfmqxwj7q', - 'penl4dfdk', - 't8nxz6qzd', - 't8nyiude5', - 'sek9ghqwi' - ], - 'pt9': '|k0xw2vqzp33kklb3j5w4|||' - } - } - ] - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 728, - 90 - ], - [ - 970, - 250 - ] + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ], + [ + 300, + 600 ] - } - }, - 'adUnitCode': 'leaderboard', - 'transactionId': 'a49988e6-ae7c-46c4-9598-f18db49892a0', - 'sizes': [ - [ - 728, - 90 - ], - [ - 970, - 250 ] + } + }, + 'adUnitCode': 'mpu', + 'transactionId': '6480bac7-31b5-4723-9145-ad8966660651', + 'sizes': [ + [ + 300, + 250 ], - 'bidId': '3025f169863b7f8', - 'bidderRequestId': '1d03a1dfc563fc', - 'auctionId': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', - 'src': 'client', - 'bidRequestsCount': 1, - 'bidderRequestsCount': 1, - 'bidderWinsCount': 0 - } - ], - 'auctionStart': 1592918645574, - 'timeout': 3000, - 'refererInfo': { - 'referer': 'http://ozone.ardm.io/adapter/2.4.0/620x350-switch.html?guardian=true&pbjs_debug=true', - 'reachedTop': true, - 'numIframes': 0, - 'stack': [ - 'http://ozone.ardm.io/adapter/2.4.0/620x350-switch.html?guardian=true&pbjs_debug=true' - ] + [ + 300, + 600 + ] + ], + 'bidId': '2d30e86db743a8', + 'bidderRequestId': '1d03a1dfc563fc', + 'auctionId': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 }, - 'gdprConsent': { - 'consentString': 'BOvy5sFO1dBa2AKAiBENDP-AAAAwVrv7_77-_9f-_f__9uj3Gr_v_f__32ccL5tv3h_7v-_7fi_-0nV4u_1tft9ydk1-5ctDztp507iakiPHmqNeb9n_mz1eZpRP58E09j53z7Ew_v8_v-b7BCPN_Y3v-8K96kA', - 'vendorData': { - 'metadata': 'BOvy5sFO1dBa2AKAiBENDPA', - 'gdprApplies': true, - 'hasGlobalConsent': false, - 'hasGlobalScope': false, - 'purposeConsents': { - '1': true, - '2': true, - '3': true, - '4': true, - '5': true - }, - 'vendorConsents': { - '1': true, - '2': true, - '3': false, - '4': true, - '5': true + { + 'bidder': 'ozone', + 'params': { + 'publisherId': 'OZONERUP0001', + 'siteId': '4204204201', + 'placementId': '0420420421', + 'customData': [ + { + 'settings': {}, + 'targeting': { + 'sens': 'f', + 'pt1': '/uk', + 'pt2': 'uk', + 'pt3': 'network-front', + 'pt4': 'ng', + 'pt5': [ + 'uk' + ], + 'pt7': 'desktop', + 'pt8': [ + 'tfmqxwj7q', + 'penl4dfdk', + 't8nxz6qzd', + 't8nyiude5', + 'sek9ghqwi' + ], + 'pt9': '|k0xw2vqzp33kklb3j5w4|||' + } + } + ] + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 728, + 90 + ], + [ + 970, + 250 + ] + ] } }, - 'gdprApplies': true + 'adUnitCode': 'leaderboard', + 'transactionId': 'a49988e6-ae7c-46c4-9598-f18db49892a0', + 'sizes': [ + [ + 728, + 90 + ], + [ + 970, + 250 + ] + ], + 'bidId': '3025f169863b7f8', + 'bidderRequestId': '1d03a1dfc563fc', + 'auctionId': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + } + ], + 'auctionStart': 1592918645574, + 'timeout': 3000, + 'refererInfo': { + 'referer': 'http://ozone.ardm.io/adapter/2.4.0/620x350-switch.html?guardian=true&pbjs_debug=true', + 'reachedTop': true, + 'numIframes': 0, + 'stack': [ + 'http://ozone.ardm.io/adapter/2.4.0/620x350-switch.html?guardian=true&pbjs_debug=true' + ] + }, + 'gdprConsent': { + 'consentString': 'BOvy5sFO1dBa2AKAiBENDP-AAAAwVrv7_77-_9f-_f__9uj3Gr_v_f__32ccL5tv3h_7v-_7fi_-0nV4u_1tft9ydk1-5ctDztp507iakiPHmqNeb9n_mz1eZpRP58E09j53z7Ew_v8_v-b7BCPN_Y3v-8K96kA', + 'vendorData': { + 'metadata': 'BOvy5sFO1dBa2AKAiBENDPA', + 'gdprApplies': true, + 'hasGlobalConsent': false, + 'hasGlobalScope': false, + 'purposeConsents': { + '1': true, + '2': true, + '3': true, + '4': true, + '5': true + }, + 'vendorConsents': { + '1': true, + '2': true, + '3': false, + '4': true, + '5': true + } }, - 'start': 1592918645578 - } + 'gdprApplies': true + }, + 'start': 1592918645578 }; - var multiResponse1 = { 'body': { 'id': '592ee33b-fb2e-4c00-b2d5-383e99cac57f', @@ -1488,11 +1449,6 @@ var multiResponse1 = { }, 'headers': {} }; - -/* ---------------------end of 2 slots, 2 ---------------------------- - */ - describe('ozone Adapter', function () { describe('isBidRequestValid', function () { let validBidReq = { @@ -1503,13 +1459,10 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should return true when required params found', function () { expect(spec.isBidRequestValid(validBidReq)).to.equal(true); }); - var validBidReq2 = { - bidder: BIDDER_CODE, params: { placementId: '1310000099', @@ -1519,11 +1472,9 @@ describe('ozone Adapter', function () { }, siteId: 1234567890 } - it('should return true when required params found and all optional params are valid', function () { expect(spec.isBidRequestValid(validBidReq2)).to.equal(true); }); - var xEmptyPlacement = { bidder: BIDDER_CODE, params: { @@ -1532,11 +1483,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should not validate empty placementId', function () { expect(spec.isBidRequestValid(xEmptyPlacement)).to.equal(false); }); - var xMissingPlacement = { bidder: BIDDER_CODE, params: { @@ -1544,11 +1493,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should not validate missing placementId', function () { expect(spec.isBidRequestValid(xMissingPlacement)).to.equal(false); }); - var xBadPlacement = { bidder: BIDDER_CODE, params: { @@ -1557,11 +1504,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should not validate placementId with a non-numeric value', function () { expect(spec.isBidRequestValid(xBadPlacement)).to.equal(false); }); - var xBadPlacementTooShort = { bidder: BIDDER_CODE, params: { @@ -1570,11 +1515,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should not validate placementId with a numeric value of wrong length', function () { expect(spec.isBidRequestValid(xBadPlacementTooShort)).to.equal(false); }); - var xBadPlacementTooLong = { bidder: BIDDER_CODE, params: { @@ -1583,11 +1526,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should not validate placementId with a numeric value of wrong length', function () { expect(spec.isBidRequestValid(xBadPlacementTooLong)).to.equal(false); }); - var xMissingPublisher = { bidder: BIDDER_CODE, params: { @@ -1595,11 +1536,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should not validate missing publisherId', function () { expect(spec.isBidRequestValid(xMissingPublisher)).to.equal(false); }); - var xMissingSiteId = { bidder: BIDDER_CODE, params: { @@ -1607,11 +1546,9 @@ describe('ozone Adapter', function () { placementId: '1234567890', } }; - it('should not validate missing sitetId', function () { expect(spec.isBidRequestValid(xMissingSiteId)).to.equal(false); }); - var xBadPublisherTooShort = { bidder: BIDDER_CODE, params: { @@ -1620,11 +1557,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should not validate publisherId being too short', function () { expect(spec.isBidRequestValid(xBadPublisherTooShort)).to.equal(false); }); - var xBadPublisherTooLong = { bidder: BIDDER_CODE, params: { @@ -1633,11 +1568,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should not validate publisherId being too long', function () { expect(spec.isBidRequestValid(xBadPublisherTooLong)).to.equal(false); }); - var publisherNumericOk = { bidder: BIDDER_CODE, params: { @@ -1646,11 +1579,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should validate publisherId being 12 digits', function () { expect(spec.isBidRequestValid(publisherNumericOk)).to.equal(true); }); - var xEmptyPublisher = { bidder: BIDDER_CODE, params: { @@ -1659,11 +1590,9 @@ describe('ozone Adapter', function () { siteId: '1234567890' } }; - it('should not validate empty publisherId', function () { expect(spec.isBidRequestValid(xEmptyPublisher)).to.equal(false); }); - var xBadSite = { bidder: BIDDER_CODE, params: { @@ -1672,37 +1601,15 @@ describe('ozone Adapter', function () { siteId: '12345Z' } }; - it('should not validate bad siteId', function () { expect(spec.isBidRequestValid(xBadSite)).to.equal(false); }); - - var xBadSiteTooLong = { - bidder: BIDDER_CODE, - params: { - placementId: '1234567890', - publisherId: '9876abcd12-3', - siteId: '12345678901' - } - }; - it('should not validate siteId too long', function () { expect(spec.isBidRequestValid(xBadSite)).to.equal(false); }); - - var xBadSiteTooShort = { - bidder: BIDDER_CODE, - params: { - placementId: '1234567890', - publisherId: '9876abcd12-3', - siteId: '123456789' - } - }; - it('should not validate siteId too short', function () { expect(spec.isBidRequestValid(xBadSite)).to.equal(false); }); - var allNonStrings = { bidder: BIDDER_CODE, params: { @@ -1711,11 +1618,9 @@ describe('ozone Adapter', function () { siteId: 1234567890 } }; - it('should validate all numeric values being sent as non-string numbers', function () { expect(spec.isBidRequestValid(allNonStrings)).to.equal(true); }); - var emptySiteId = { bidder: BIDDER_CODE, params: { @@ -1724,11 +1629,9 @@ describe('ozone Adapter', function () { siteId: '' } }; - it('should not validate siteId being empty string (it is required now)', function () { expect(spec.isBidRequestValid(emptySiteId)).to.equal(false); }); - var xBadCustomData = { bidder: BIDDER_CODE, params: { @@ -1738,12 +1641,10 @@ describe('ozone Adapter', function () { 'customData': 'this aint gonna work' } }; - it('should not validate customData not being an array', function () { expect(spec.isBidRequestValid(xBadCustomData)).to.equal(false); }); - - var xBadCustomData_OLD_CUSTOMDATA_VALUE = { + var xBadCustomDataOldCustomdataValue = { bidder: BIDDER_CODE, params: { 'placementId': '1234567890', @@ -1752,12 +1653,10 @@ describe('ozone Adapter', function () { 'customData': {'gender': 'bart', 'age': 'low'} } }; - it('should not validate customData being an object, not an array', function () { - expect(spec.isBidRequestValid(xBadCustomData_OLD_CUSTOMDATA_VALUE)).to.equal(false); + expect(spec.isBidRequestValid(xBadCustomDataOldCustomdataValue)).to.equal(false); }); - - var xBadCustomData_zerocd = { + var xBadCustomDataZerocd = { bidder: BIDDER_CODE, params: { 'placementId': '1111111110', @@ -1766,12 +1665,10 @@ describe('ozone Adapter', function () { 'customData': [] } }; - it('should not validate customData array having no elements', function () { - expect(spec.isBidRequestValid(xBadCustomData_zerocd)).to.equal(false); + expect(spec.isBidRequestValid(xBadCustomDataZerocd)).to.equal(false); }); - - var xBadCustomData_notargeting = { + var xBadCustomDataNotargeting = { bidder: BIDDER_CODE, params: { 'placementId': '1234567890', @@ -1781,10 +1678,9 @@ describe('ozone Adapter', function () { } }; it('should not validate customData[] having no "targeting"', function () { - expect(spec.isBidRequestValid(xBadCustomData_notargeting)).to.equal(false); + expect(spec.isBidRequestValid(xBadCustomDataNotargeting)).to.equal(false); }); - - var xBadCustomData_tgt_not_obj = { + var xBadCustomDataTgtNotObj = { bidder: BIDDER_CODE, params: { 'placementId': '1234567890', @@ -1794,9 +1690,8 @@ describe('ozone Adapter', function () { } }; it('should not validate customData[0].targeting not being an object', function () { - expect(spec.isBidRequestValid(xBadCustomData_tgt_not_obj)).to.equal(false); + expect(spec.isBidRequestValid(xBadCustomDataTgtNotObj)).to.equal(false); }); - var xBadCustomParams = { bidder: BIDDER_CODE, params: { @@ -1821,11 +1716,9 @@ describe('ozone Adapter', function () { mimes: ['video/mp4']} } }; - it('should not validate video without context attribute', function () { expect(spec.isBidRequestValid(xBadVideoContext2)).to.equal(false); }); - let validVideoBidReq = { bidder: BIDDER_CODE, params: { @@ -1839,7 +1732,6 @@ describe('ozone Adapter', function () { 'context': 'outstream'}, } }; - it('should validate video outstream being sent', function () { expect(spec.isBidRequestValid(validVideoBidReq)).to.equal(true); }); @@ -1849,47 +1741,44 @@ describe('ozone Adapter', function () { expect(spec.isBidRequestValid(instreamVid)).to.equal(true); }); }); - describe('buildRequests', function () { + beforeEach(function () { + config.resetConfig() + }); it('sends bid request to OZONEURI via POST', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); expect(request.url).to.equal(OZONEURI); expect(request.method).to.equal('POST'); }); - it('sends data as a string', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); expect(request.data).to.be.a('string'); }); - it('sends all bid parameters', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); }); - it('adds all parameters inside the ext object only', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); expect(request.data).to.be.a('string'); var data = JSON.parse(request.data); expect(data.imp[0].ext.ozone.customData).to.be.an('array'); expect(request).not.to.have.key('lotameData'); expect(request).not.to.have.key('customData'); }); - it('adds all parameters inside the ext object only - lightning', function () { let localBidReq = JSON.parse(JSON.stringify(validBidRequests)); - const request = spec.buildRequests(localBidReq, validBidderRequest.bidderRequest); + const request = spec.buildRequests(localBidReq, validBidderRequest); expect(request.data).to.be.a('string'); var data = JSON.parse(request.data); expect(data.imp[0].ext.ozone.customData).to.be.an('array'); expect(request).not.to.have.key('lotameData'); expect(request).not.to.have.key('customData'); }); - it('ignores ozoneData in & after version 2.1.1', function () { let validBidRequestsWithOzoneData = JSON.parse(JSON.stringify(validBidRequests)); validBidRequestsWithOzoneData[0].params.ozoneData = {'networkID': '3048', 'dfpSiteID': 'd.thesun', 'sectionID': 'homepage', 'path': '/', 'sec_id': 'null', 'sec': 'sec', 'topics': 'null', 'kw': 'null', 'aid': 'null', 'search': 'null', 'article_type': 'null', 'hide_ads': '', 'article_slug': 'null'}; - const request = spec.buildRequests(validBidRequestsWithOzoneData, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsWithOzoneData, validBidderRequest); expect(request.data).to.be.a('string'); var data = JSON.parse(request.data); expect(data.imp[0].ext.ozone.customData).to.be.an('array'); @@ -1897,43 +1786,36 @@ describe('ozone Adapter', function () { expect(request).not.to.have.key('lotameData'); expect(request).not.to.have.key('customData'); }); - it('has correct bidder', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); expect(request.bidderRequest.bids[0].bidder).to.equal(BIDDER_CODE); }); - it('handles mediaTypes element correctly', function () { - const request = spec.buildRequests(validBidRequestsWithBannerMediaType, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsWithBannerMediaType, validBidderRequest); expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); }); - it('handles no ozone or custom data', function () { - const request = spec.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsMinimal, validBidderRequest); expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); }); - it('handles video mediaType element correctly, with outstream video', function () { - const request = spec.buildRequests(validBidRequests1OutstreamVideo2020, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests1OutstreamVideo2020, validBidderRequest); expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); }); - it('should not crash when there is no sizes element at all', function () { - const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); }); - it('should be able to handle non-single requests', function () { config.setConfig({'ozone': {'singleRequest': false}}); - const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); expect(request).to.be.a('array'); expect(request[0]).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); config.setConfig({'ozone': {'singleRequest': true}}); }); - it('should add gdpr consent information to the request when ozone is true', function () { let consentString = 'BOcocyaOcocyaAfEYDENCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NphLgA=='; - let bidderRequest = validBidderRequest.bidderRequest; + let bidderRequest = validBidderRequest; bidderRequest.gdprConsent = { consentString: consentString, gdprApplies: true, @@ -1944,16 +1826,14 @@ describe('ozone Adapter', function () { purposeConsents: {1: true, 2: true, 3: true, 4: true, 5: true} } } - const request = spec.buildRequests(validBidRequestsNoSizes, bidderRequest); const payload = JSON.parse(request.data); expect(payload.regs.ext.gdpr).to.equal(1); expect(payload.user.ext.consent).to.equal(consentString); }); - it('should add gdpr consent information to the request when vendorData is missing vendorConsents (Mirror)', function () { let consentString = 'BOcocyaOcocyaAfEYDENCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NphLgA=='; - let bidderRequest = validBidderRequest.bidderRequest; + let bidderRequest = validBidderRequest; bidderRequest.gdprConsent = { consentString: consentString, gdprApplies: true, @@ -1962,16 +1842,14 @@ describe('ozone Adapter', function () { gdprApplies: true } } - const request = spec.buildRequests(validBidRequestsNoSizes, bidderRequest); const payload = JSON.parse(request.data); expect(payload.regs.ext.gdpr).to.equal(1); expect(payload.user.ext.consent).to.equal(consentString); }); - it('should set regs.ext.gdpr flag to 0 when gdprApplies is false', function () { let consentString = 'BOcocyaOcocyaAfEYDENCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NphLgA=='; - let bidderRequest = validBidderRequest.bidderRequest; + let bidderRequest = validBidderRequest; bidderRequest.gdprConsent = { consentString: consentString, gdprApplies: false, @@ -1982,15 +1860,13 @@ describe('ozone Adapter', function () { purposeConsents: {1: true, 2: true, 3: true, 4: true, 5: true} } }; - const request = spec.buildRequests(validBidRequestsNoSizes, bidderRequest); const payload = JSON.parse(request.data); expect(payload.regs.ext.gdpr).to.equal(0); }); - it('should not have imp[N].ext.ozone.userId', function () { let consentString = 'BOcocyaOcocyaAfEYDENCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NphLgA=='; - let bidderRequest = validBidderRequest.bidderRequest; + let bidderRequest = validBidderRequest; bidderRequest.gdprConsent = { consentString: consentString, gdprApplies: false, @@ -2001,7 +1877,6 @@ describe('ozone Adapter', function () { purposeConsents: {1: true, 2: true, 3: true, 4: true, 5: true} } }; - let bidRequests = validBidRequests; bidRequests[0]['userId'] = { 'digitrustid': {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, @@ -2019,7 +1894,6 @@ describe('ozone Adapter', function () { expect(firstBid).to.not.have.property('userId'); delete validBidRequests[0].userId; // tidy up now, else it will screw with other tests }); - it('should pick up the value of pubcid when built using the pubCommonId module (not userId)', function () { let bidRequests = validBidRequests; bidRequests[0]['userId'] = { @@ -2031,23 +1905,13 @@ describe('ozone Adapter', function () { 'sharedid': {'id': '01EAJWWNEPN3CYMM5N8M5VXY22', 'third': '01EAJWWNEPN3CYMM5N8M5VXY22'} }; bidRequests[0]['userIdAsEids'] = validBidRequestsWithUserIdData[0]['userIdAsEids']; - const request = spec.buildRequests(bidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(bidRequests, validBidderRequest); const payload = JSON.parse(request.data); expect(payload.ext.ozone.pubcid).to.equal(bidRequests[0]['crumbs']['pubcid']); delete validBidRequests[0].userId; // tidy up now, else it will screw with other tests }); - it('should add a user.ext.eids object to contain user ID data in the new location (Nov 2019) Updated Aug 2020', function() { - const request = spec.buildRequests(validBidRequestsWithUserIdData, validBidderRequest.bidderRequest); - /* - 'pubcid': '12345678', - 'tdid': '1111tdid', - 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, - 'criteoId': '1111criteoId', - 'idl_env': 'liverampId', - 'parrableId': {'eid': '01.5678.parrableid'} - */ - + const request = spec.buildRequests(validBidRequestsWithUserIdData, validBidderRequest); const payload = JSON.parse(request.data); expect(payload.user).to.exist; expect(payload.user.ext).to.exist; @@ -2067,12 +1931,11 @@ describe('ozone Adapter', function () { expect(payload.user.ext.eids[6]['source']).to.equal('parrableId'); expect(payload.user.ext.eids[6]['uids'][0]['id']['eid']).to.equal('01.5678.parrableid'); }); - it('replaces the auction url for a config override', function () { spec.propertyBag.whitelabel = null; let fakeOrigin = 'http://sometestendpoint'; config.setConfig({'ozone': {'endpointOverride': {'origin': fakeOrigin}}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); expect(request.url).to.equal(fakeOrigin + '/openrtb2/auction'); expect(request.method).to.equal('POST'); const data = JSON.parse(request.data); @@ -2080,12 +1943,11 @@ describe('ozone Adapter', function () { config.setConfig({'ozone': {'kvpPrefix': null, 'endpointOverride': null}}); spec.propertyBag.whitelabel = null; }); - it('replaces the FULL auction url for a config override', function () { spec.propertyBag.whitelabel = null; let fakeurl = 'http://sometestendpoint/myfullurl'; config.setConfig({'ozone': {'endpointOverride': {'auctionUrl': fakeurl}}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); expect(request.url).to.equal(fakeurl); expect(request.method).to.equal('POST'); const data = JSON.parse(request.data); @@ -2093,7 +1955,6 @@ describe('ozone Adapter', function () { config.setConfig({'ozone': {'kvpPrefix': null, 'endpointOverride': null}}); spec.propertyBag.whitelabel = null; }); - it('replaces the renderer url for a config override', function () { spec.propertyBag.whitelabel = null; let fakeUrl = 'http://renderer.com'; @@ -2107,9 +1968,8 @@ describe('ozone Adapter', function () { spec.propertyBag.whitelabel = null; }); it('should generate all the adservertargeting keys correctly named', function () { - var specMock = utils.deepClone(spec); config.setConfig({'ozone': {'kvpPrefix': 'xx'}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(result[0].adserverTargeting).to.have.own.property('xx_appnexus_crid'); expect(utils.deepAccess(result[0].adserverTargeting, 'xx_appnexus_crid')).to.equal('98493581'); @@ -2118,33 +1978,28 @@ describe('ozone Adapter', function () { expect(utils.deepAccess(result[0].adserverTargeting, 'xx_size')).to.equal('300x600'); expect(utils.deepAccess(result[0].adserverTargeting, 'xx_pb_r')).to.equal('0.50'); expect(utils.deepAccess(result[0].adserverTargeting, 'xx_bid')).to.equal('true'); - config.resetConfig(); }); it('should create a meta object on each bid returned', function () { - var specMock = utils.deepClone(spec); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(result[0]).to.have.own.property('meta'); expect(result[0].meta.advertiserDomains[0]).to.equal('http://prebid.org'); - config.resetConfig(); }); - it('replaces the kvp prefix ', function () { spec.propertyBag.whitelabel = null; config.setConfig({'ozone': {'kvpPrefix': 'test'}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const data = JSON.parse(request.data); expect(data.ext.ozone).to.haveOwnProperty('test_rw'); config.setConfig({'ozone': {'kvpPrefix': null}}); spec.propertyBag.whitelabel = null; }); - it('handles an alias ', function () { spec.propertyBag.whitelabel = null; config.setConfig({'lmc': {'kvpPrefix': 'test'}}); let br = JSON.parse(JSON.stringify(validBidRequests)); br[0]['bidder'] = 'lmc'; - const request = spec.buildRequests(br, validBidderRequest.bidderRequest); + const request = spec.buildRequests(br, validBidderRequest); const data = JSON.parse(request.data); expect(data.ext.lmc).to.haveOwnProperty('test_rw'); config.setConfig({'lmc': {'kvpPrefix': null}}); // I cant remove the key so set the value to null @@ -2155,7 +2010,7 @@ describe('ozone Adapter', function () { specMock.getGetParametersAsObject = function() { return {'oztestmode': 'mytestvalue_123'}; }; - const request = specMock.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = specMock.buildRequests(validBidRequests, validBidderRequest); const data = JSON.parse(request.data); expect(data.imp[0].ext.ozone.customData).to.be.an('array'); expect(data.imp[0].ext.ozone.customData[0].targeting.oztestmode).to.equal('mytestvalue_123'); @@ -2165,7 +2020,7 @@ describe('ozone Adapter', function () { specMock.getGetParametersAsObject = function() { return {ozf: '1', ozpf: '0', ozrp: '2', ozip: '123'}; }; - const request = specMock.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = specMock.buildRequests(validBidRequests, validBidderRequest); const data = JSON.parse(request.data); expect(data.ext.ozone.ozf).to.equal(1); expect(data.ext.ozone.ozpf).to.equal(0); @@ -2177,7 +2032,7 @@ describe('ozone Adapter', function () { specMock.getGetParametersAsObject = function() { return {ozf: 'false', ozpf: 'true', ozrp: 'xyz', ozip: 'hello'}; }; - const request = specMock.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = specMock.buildRequests(validBidRequests, validBidderRequest); const data = JSON.parse(request.data); expect(data.ext.ozone.ozf).to.equal(0); expect(data.ext.ozone.ozpf).to.equal(1); @@ -2189,7 +2044,7 @@ describe('ozone Adapter', function () { specMock.getGetParametersAsObject = function() { return {'oztestmode': 'mytestvalue_123'}; }; - const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); + const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest); const data = JSON.parse(request.data); expect(data.imp[0].ext.ozone.customData).to.be.an('array'); expect(data.imp[0].ext.ozone.customData[0].targeting.oztestmode).to.equal('mytestvalue_123'); @@ -2199,17 +2054,16 @@ describe('ozone Adapter', function () { specMock.getGetParametersAsObject = function() { return {}; }; - let request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); + let request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest); let url = request.url; expect(url).to.equal('https://elb.the-ozone-project.com/openrtb2/auction'); let cookieUrl = specMock.getCookieSyncUrl(); expect(cookieUrl).to.equal('https://elb.the-ozone-project.com/static/load-cookie.html'); - specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { return {'auction': 'dev', 'cookiesync': 'dev'}; }; - request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); + request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest); url = request.url; expect(url).to.equal('https://test.ozpr.net/openrtb2/auction'); cookieUrl = specMock.getCookieSyncUrl(); @@ -2220,7 +2074,7 @@ describe('ozone Adapter', function () { specMock.getGetParametersAsObject = function() { return {'ozstoredrequest': '1122334455'}; // 10 digits are valid }; - const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); + const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest); const data = JSON.parse(request.data); expect(data.ext.ozone.oz_rw).to.equal(1); expect(data.imp[0].ext.prebid.storedrequest.id).to.equal('1122334455'); @@ -2230,71 +2084,65 @@ describe('ozone Adapter', function () { specMock.getGetParametersAsObject = function() { return {'ozstoredrequest': 'BADVAL'}; // 10 digits are valid }; - const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); + const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest); const data = JSON.parse(request.data); expect(data.ext.ozone.oz_rw).to.equal(0); expect(data.imp[0].ext.prebid.storedrequest.id).to.equal('1310000099'); }); - it('should pick up the config value of coppa & set it in the request', function () { config.setConfig({'coppa': true}); - const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); const payload = JSON.parse(request.data); expect(payload.regs).to.include.keys('coppa'); expect(payload.regs.coppa).to.equal(1); - config.resetConfig(); }); it('should pick up the config value of coppa & only set it in the request if its true', function () { config.setConfig({'coppa': false}); - const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); const payload = JSON.parse(request.data); expect(utils.deepAccess(payload, 'regs.coppa')).to.be.undefined; - config.resetConfig(); }); it('should handle oz_omp_floor correctly', function () { config.setConfig({'ozone': {'oz_omp_floor': 1.56}}); - const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); const payload = JSON.parse(request.data); expect(utils.deepAccess(payload, 'ext.ozone.oz_omp_floor')).to.equal(1.56); - config.resetConfig(); }); it('should ignore invalid oz_omp_floor values', function () { config.setConfig({'ozone': {'oz_omp_floor': '1.56'}}); - const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); const payload = JSON.parse(request.data); expect(utils.deepAccess(payload, 'ext.ozone.oz_omp_floor')).to.be.undefined; - config.resetConfig(); }); it('should should contain a unique page view id in the auction request which persists across calls', function () { - let request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + let request = spec.buildRequests(validBidRequests, validBidderRequest); let payload = JSON.parse(request.data); expect(utils.deepAccess(payload, 'ext.ozone.pv')).to.be.a('string'); - request = spec.buildRequests(validBidRequests1OutstreamVideo2020, validBidderRequest.bidderRequest); + request = spec.buildRequests(validBidRequests1OutstreamVideo2020, validBidderRequest); let payload2 = JSON.parse(request.data); expect(utils.deepAccess(payload2, 'ext.ozone.pv')).to.be.a('string'); expect(utils.deepAccess(payload2, 'ext.ozone.pv')).to.equal(utils.deepAccess(payload, 'ext.ozone.pv')); }); it('should indicate that the whitelist was used when it contains valid data', function () { config.setConfig({'ozone': {'oz_whitelist_adserver_keys': ['oz_ozappnexus_pb', 'oz_ozappnexus_imp_id']}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const payload = JSON.parse(request.data); expect(payload.ext.ozone.oz_kvp_rw).to.equal(1); - config.resetConfig(); }); it('should indicate that the whitelist was not used when it contains no data', function () { config.setConfig({'ozone': {'oz_whitelist_adserver_keys': []}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const payload = JSON.parse(request.data); expect(payload.ext.ozone.oz_kvp_rw).to.equal(0); - config.resetConfig(); }); it('should indicate that the whitelist was not used when it is not set in the config', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const payload = JSON.parse(request.data); expect(payload.ext.ozone.oz_kvp_rw).to.equal(0); }); it('should handle ortb2 site data', function () { - const ortb2 = { + let bidderRequest = JSON.parse(JSON.stringify(validBidderRequest)); + bidderRequest.ortb2 = { 'site': { 'name': 'example_ortb2_name', 'domain': 'page.example.com', @@ -2307,14 +2155,14 @@ describe('ozone Adapter', function () { 'search': 'drill' } }; - const request = spec.buildRequests(validBidRequests, {...validBidderRequest.bidderRequest, ortb2}); + const request = spec.buildRequests(validBidRequests, bidderRequest); const payload = JSON.parse(request.data); expect(payload.imp[0].ext.ozone.customData[0].targeting.name).to.equal('example_ortb2_name'); expect(payload.user.ext).to.not.have.property('gender'); - config.resetConfig(); }); it('should add ortb2 site data when there is no customData already created', function () { - const ortb2 = { + let bidderRequest = JSON.parse(JSON.stringify(validBidderRequest)); + bidderRequest.ortb2 = { 'site': { 'name': 'example_ortb2_name', 'domain': 'page.example.com', @@ -2327,25 +2175,25 @@ describe('ozone Adapter', function () { 'search': 'drill' } }; - const request = spec.buildRequests(validBidRequestsNoCustomData, {...validBidderRequest.bidderRequest, ortb2}); + const request = spec.buildRequests(validBidRequestsNoCustomData, bidderRequest); const payload = JSON.parse(request.data); expect(payload.imp[0].ext.ozone.customData[0].targeting.name).to.equal('example_ortb2_name'); expect(payload.imp[0].ext.ozone.customData[0].targeting).to.not.have.property('gender') - config.resetConfig(); }); it('should add ortb2 user data to the user object', function () { - const ortb2 = { + let bidderRequest = JSON.parse(JSON.stringify(validBidderRequest)); + bidderRequest.ortb2 = { 'user': { - 'gender': 'who knows these days' + 'gender': 'I identify as a box of rocks' } }; - const request = spec.buildRequests(validBidRequests, {...validBidderRequest.bidderRequest, ortb2}); + const request = spec.buildRequests(validBidRequests, bidderRequest); const payload = JSON.parse(request.data); - expect(payload.user.gender).to.equal('who knows these days'); - config.resetConfig(); + expect(payload.user.gender).to.equal('I identify as a box of rocks'); }); it('should not override the user.ext.consent string even if this is set in config ortb2', function () { - const ortb2 = { + let bidderRequest = JSON.parse(JSON.stringify(bidderRequestWithFullGdpr)); + bidderRequest.ortb2 = { 'user': { 'ext': { 'consent': 'this is the consent override that shouldnt work', @@ -2353,15 +2201,14 @@ describe('ozone Adapter', function () { } } }; - const request = spec.buildRequests(validBidRequests, {...bidderRequestWithFullGdpr.bidderRequest, ortb2}); + const request = spec.buildRequests(validBidRequests, bidderRequest); const payload = JSON.parse(request.data); expect(payload.user.ext.consent2).to.equal('this should be set'); expect(payload.user.ext.consent).to.equal('BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA'); - config.resetConfig(); }); it('should have openrtb video params', function() { let allowed = ['mimes', 'minduration', 'maxduration', 'protocols', 'w', 'h', 'startdelay', 'placement', 'linearity', 'skip', 'skipmin', 'skipafter', 'sequence', 'battr', 'maxextended', 'minbitrate', 'maxbitrate', 'boxingallowed', 'playbackmethod', 'playbackend', 'delivery', 'pos', 'companionad', 'api', 'companiontype', 'ext']; - const request = spec.buildRequests(validBidRequests1OutstreamVideo2020, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests1OutstreamVideo2020, validBidderRequest); const payload = JSON.parse(request.data); const vid = (payload.imp[0].video); const keys = Object.keys(vid); @@ -2391,13 +2238,11 @@ describe('ozone Adapter', function () { }); let localBidRequest = JSON.parse(JSON.stringify(validBidRequestsWithBannerMediaType)); localBidRequest[0].getFloor = function(x) { return {'currency': 'USD', 'floor': 0.8} }; - const request = spec.buildRequests(localBidRequest, validBidderRequest.bidderRequest); + const request = spec.buildRequests(localBidRequest, validBidderRequest); const payload = JSON.parse(request.data); expect(utils.deepAccess(payload, 'imp.0.floor.banner.currency')).to.equal('USD'); expect(utils.deepAccess(payload, 'imp.0.floor.banner.floor')).to.equal(0.8); - config.resetConfig(); }); - it('handles schain object in each bidrequest (will be the same in each br)', function () { let br = JSON.parse(JSON.stringify(validBidRequests)); let schainConfigObject = { @@ -2412,44 +2257,44 @@ describe('ozone Adapter', function () { ] }; br[0]['schain'] = schainConfigObject; - const request = spec.buildRequests(br, validBidderRequest.bidderRequest); + const request = spec.buildRequests(br, validBidderRequest); const data = JSON.parse(request.data); expect(data.source.ext).to.haveOwnProperty('schain'); expect(data.source.ext.schain).to.deep.equal(schainConfigObject); // .deep.equal() : Target object deeply (but not strictly) equals `{a: 1}` }); }); - describe('interpretResponse', function () { + beforeEach(function () { + config.resetConfig() + }) it('should build bid array', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(result.length).to.equal(1); }); - it('should have all relevant fields', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); const bid = result[0]; expect(bid.cpm).to.equal(validResponse.body.seatbid[0].bid[0].cpm); expect(bid.width).to.equal(validResponse.body.seatbid[0].bid[0].width); expect(bid.height).to.equal(validResponse.body.seatbid[0].bid[0].height); }); - it('should build bid array with gdpr', function () { - let validBR = JSON.parse(JSON.stringify(bidderRequestWithFullGdpr.bidderRequest)); + let validBR = JSON.parse(JSON.stringify(bidderRequestWithFullGdpr)); validBR.gdprConsent = {'gdprApplies': 1, 'consentString': 'This is the gdpr consent string'}; const request = spec.buildRequests(validBidRequests, validBR); // works the old way, with GDPR not enforced by default const result = spec.interpretResponse(validResponse, request); expect(result.length).to.equal(1); }); it('should build bid array with usp/CCPA', function () { - let validBR = JSON.parse(JSON.stringify(bidderRequestWithFullGdpr.bidderRequest)); + let validBR = JSON.parse(JSON.stringify(bidderRequestWithFullGdpr)); validBR.uspConsent = '1YNY'; const request = spec.buildRequests(validBidRequests, validBR); const payload = JSON.parse(request.data); - expect(payload.user.ext.uspConsent).to.equal('1YNY'); + expect(payload.user.ext.uspConsent).not.to.exist; + expect(payload.regs.ext.us_privacy).to.equal('1YNY'); }); - it('should build bid array with only partial gdpr', function () { var validBidderRequestWithGdpr = bidderRequestWithPartialGdpr.bidderRequest; validBidderRequestWithGdpr.gdprConsent = {'gdprApplies': 1, 'consentString': 'This is the gdpr consent string'}; @@ -2457,26 +2302,22 @@ describe('ozone Adapter', function () { const payload = JSON.parse(request.data); expect(payload.user.ext.consent).to.be.a('string'); }); - it('should fail ok if no seatbid in server response', function () { const result = spec.interpretResponse({}, {}); expect(result).to.be.an('array'); expect(result).to.be.empty; }); - it('should fail ok if seatbid is not an array', function () { const result = spec.interpretResponse({'body': {'seatbid': 'nothing_here'}}, {}); expect(result).to.be.an('array'); expect(result).to.be.empty; }); - it('should have video renderer for outstream video', function () { const request = spec.buildRequests(validBidRequests1OutstreamVideo2020, validBidderRequest1OutstreamVideo2020.bidderRequest); const result = spec.interpretResponse(getCleanValidVideoResponse(), validBidderRequest1OutstreamVideo2020); const bid = result[0]; expect(bid.renderer).to.be.an.instanceOf(Renderer); }); - it('should have NO video renderer for instream video', function () { let instreamRequestsObj = JSON.parse(JSON.stringify(validBidRequests1OutstreamVideo2020)); instreamRequestsObj[0].mediaTypes.video.context = 'instream'; @@ -2487,99 +2328,87 @@ describe('ozone Adapter', function () { const bid = result[0]; expect(bid.hasOwnProperty('renderer')).to.be.false; }); - it('should correctly parse response where there are more bidders than ad slots', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validBidResponse1adWith2Bidders, request); expect(result.length).to.equal(2); }); - it('should have a ttl of 600', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(result[0].ttl).to.equal(300); }); - it('should handle oz_omp_floor_dollars correctly, inserting 1 as necessary', function () { config.setConfig({'ozone': {'oz_omp_floor': 0.01}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_omp')).to.equal('1'); - config.resetConfig(); }); it('should handle oz_omp_floor_dollars correctly, inserting 0 as necessary', function () { config.setConfig({'ozone': {'oz_omp_floor': 2.50}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_omp')).to.equal('0'); - config.resetConfig(); }); it('should handle missing oz_omp_floor_dollars correctly, inserting nothing', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_omp')).to.be.undefined; }); it('should handle ext.bidder.ozone.floor correctly, setting flr & rid as necessary', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); let vres = JSON.parse(JSON.stringify(validResponse)); vres.body.seatbid[0].bid[0].ext.bidder.ozone = {floor: 1, ruleId: 'ZjbsYE1q'}; const result = spec.interpretResponse(vres, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_flr')).to.equal(1); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_rid')).to.equal('ZjbsYE1q'); - config.resetConfig(); }); it('should handle ext.bidder.ozone.floor correctly, inserting 0 as necessary', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); let vres = JSON.parse(JSON.stringify(validResponse)); vres.body.seatbid[0].bid[0].ext.bidder.ozone = {floor: 0, ruleId: 'ZjbXXE1q'}; const result = spec.interpretResponse(vres, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_flr')).to.equal(0); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_rid')).to.equal('ZjbXXE1q'); - config.resetConfig(); }); it('should handle ext.bidder.ozone.floor correctly, inserting nothing as necessary', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); let vres = JSON.parse(JSON.stringify(validResponse)); vres.body.seatbid[0].bid[0].ext.bidder.ozone = {}; const result = spec.interpretResponse(vres, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_flr', null)).to.equal(null); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_rid', null)).to.equal(null); - config.resetConfig(); }); it('should handle ext.bidder.ozone.floor correctly, when bidder.ozone is not there', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); let vres = JSON.parse(JSON.stringify(validResponse)); const result = spec.interpretResponse(vres, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_flr', null)).to.equal(null); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_rid', null)).to.equal(null); - config.resetConfig(); }); it('should handle a valid whitelist, removing items not on the list & leaving others', function () { config.setConfig({'ozone': {'oz_whitelist_adserver_keys': ['oz_appnexus_crid', 'oz_appnexus_adId']}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_adv')).to.be.undefined; expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_adId')).to.equal('2899ec066a91ff8-0-oz-0'); - config.resetConfig(); }); it('should ignore a whitelist if enhancedAdserverTargeting is false', function () { config.setConfig({'ozone': {'oz_whitelist_adserver_keys': ['oz_appnexus_crid', 'oz_appnexus_imp_id'], 'enhancedAdserverTargeting': false}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_adv')).to.be.undefined; expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_imp_id')).to.be.undefined; - config.resetConfig(); }); it('should correctly handle enhancedAdserverTargeting being false', function () { config.setConfig({'ozone': {'enhancedAdserverTargeting': false}}); - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_adv')).to.be.undefined; expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_imp_id')).to.be.undefined; - config.resetConfig(); }); it('should add flr into ads request if floor exists in the auction response', function () { - const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest); let validres = JSON.parse(JSON.stringify(validResponse2Bids)); validres.body.seatbid[0].bid[0].ext.bidder.ozone = {'floor': 1}; const result = spec.interpretResponse(validres, request); @@ -2587,7 +2416,7 @@ describe('ozone Adapter', function () { expect(utils.deepAccess(result[1].adserverTargeting, 'oz_appnexus_flr', '')).to.equal(''); }); it('should add rid into ads request if ruleId exists in the auction response', function () { - const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest); let validres = JSON.parse(JSON.stringify(validResponse2Bids)); validres.body.seatbid[0].bid[0].ext.bidder.ozone = {'ruleId': 123}; const result = spec.interpretResponse(validres, request); @@ -2595,13 +2424,13 @@ describe('ozone Adapter', function () { expect(utils.deepAccess(result[1].adserverTargeting, 'oz_appnexus_rid', '')).to.equal(''); }); it('should add oz_ozappnexus_sid (cid value) for all appnexus bids', function () { - const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest); let validres = JSON.parse(JSON.stringify(validResponse2BidsSameAdunit)); const result = spec.interpretResponse(validres, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_ozappnexus_sid')).to.equal(result[0].cid); }); it('should add unique adId values to each bid', function() { - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequests, validBidderRequest); let validres = JSON.parse(JSON.stringify(validResponse2BidsSameAdunit)); const result = spec.interpretResponse(validres, request); expect(result.length).to.equal(1); @@ -2610,7 +2439,7 @@ describe('ozone Adapter', function () { }); it('should correctly process an auction with 2 adunits & multiple bidders one of which bids for both adslots', function() { let validres = JSON.parse(JSON.stringify(multiResponse1)); - let request = spec.buildRequests(multiRequest1, multiBidderRequest1.bidderRequest); + let request = spec.buildRequests(multiRequest1, multiBidderRequest1); let result = spec.interpretResponse(validres, request); expect(result.length).to.equal(4); // one of the 5 bids will have been removed expect(result[1]['price']).to.equal(0.521); @@ -2620,15 +2449,28 @@ describe('ozone Adapter', function () { validres = JSON.parse(JSON.stringify(multiResponse1)); validres.body.seatbid[0].bid[1].price = 1.1; validres.body.seatbid[0].bid[1].cpm = 1.1; - request = spec.buildRequests(multiRequest1, multiBidderRequest1.bidderRequest); + request = spec.buildRequests(multiRequest1, multiBidderRequest1); result = spec.interpretResponse(validres, request); expect(result[1]['price']).to.equal(1.1); expect(result[1]['impid']).to.equal('3025f169863b7f8'); expect(result[1]['id']).to.equal('18552976939844681'); expect(result[1]['adserverTargeting']['oz_ozappnexus_adId']).to.equal('3025f169863b7f8-0-oz-1'); }); + it('should add mediaType: banner for a banner ad', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + const result = spec.interpretResponse(validResponse, request); + expect(result[0].mediaType).to.equal('banner'); + }); + it('should add mediaType: video for a video ad', function () { + let instreamRequestsObj = JSON.parse(JSON.stringify(validBidRequests1OutstreamVideo2020)); + instreamRequestsObj[0].mediaTypes.video.context = 'instream'; + let instreamBidderReq = JSON.parse(JSON.stringify(validBidderRequest1OutstreamVideo2020)); + instreamBidderReq.bidderRequest.bids[0].mediaTypes.video.context = 'instream'; + const result = spec.interpretResponse(getCleanValidVideoResponse(), instreamBidderReq); + const bid = result[0]; + expect(bid.mediaType).to.equal('video'); + }); }); - describe('userSyncs', function () { it('should fail gracefully if no server response', function () { const result = spec.getUserSyncs('bad', false, gdpr1); @@ -2639,7 +2481,7 @@ describe('ozone Adapter', function () { expect(result).to.be.empty; }); it('should append the various values if they exist', function() { - spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.getUserSyncs({iframeEnabled: true}, 'good server response', gdpr1); expect(result).to.be.an('array'); expect(result[0].url).to.include('publisherId=9876abcd12-3'); @@ -2648,19 +2490,18 @@ describe('ozone Adapter', function () { expect(result[0].url).to.include('gdpr_consent=BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA'); }); it('should append ccpa (usp data)', function() { - spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.getUserSyncs({iframeEnabled: true}, 'good server response', gdpr1, '1YYN'); expect(result).to.be.an('array'); expect(result[0].url).to.include('usp_consent=1YYN'); }); it('should use "" if no usp is sent to cookieSync', function() { - spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.getUserSyncs({iframeEnabled: true}, 'good server response', gdpr1); expect(result).to.be.an('array'); expect(result[0].url).to.include('usp_consent=&'); }); }); - describe('video object utils', function () { it('should find width & height from video object', function () { let obj = {'playerSize': [640, 480], 'mimes': ['video/mp4'], 'context': 'outstream'}; @@ -2715,7 +2556,7 @@ describe('ozone Adapter', function () { expect(result).to.be.null; }); it('should add oz_appnexus_dealid into ads request if dealid exists in the auction response', function () { - const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest); let validres = JSON.parse(JSON.stringify(validResponse2Bids)); validres.body.seatbid[0].bid[0].dealid = '1234'; const result = spec.interpretResponse(validres, request); @@ -2723,7 +2564,6 @@ describe('ozone Adapter', function () { expect(utils.deepAccess(result[1].adserverTargeting, 'oz_appnexus_dealid', '')).to.equal(''); }); }); - describe('default size', function () { it('should should return default sizes if no obj is sent', function () { let obj = ''; @@ -2732,7 +2572,6 @@ describe('ozone Adapter', function () { expect(result.defaultWidth).to.equal(300); }); }); - describe('getGranularityKeyName', function() { it('should return a string granularity as-is', function() { const result = getGranularityKeyName('', 'this is it', ''); @@ -2747,7 +2586,6 @@ describe('ozone Adapter', function () { expect(result).to.equal('string buckets'); }); }); - describe('getGranularityObject', function() { it('should return an object as-is', function() { const result = getGranularityObject('', {'name': 'mark'}, '', ''); @@ -2758,19 +2596,19 @@ describe('ozone Adapter', function () { expect(result.name).to.equal('rupert'); }); }); - describe('blockTheRequest', function() { + beforeEach(function () { + config.resetConfig() + }) it('should return true if oz_request is false', function() { config.setConfig({'ozone': {'oz_request': false}}); - let result = spec.blockTheRequest(bidderRequestWithFullGdpr); + let result = spec.blockTheRequest(); expect(result).to.be.true; - config.resetConfig(); }); it('should return false if oz_request is true', function() { config.setConfig({'ozone': {'oz_request': true}}); - let result = spec.blockTheRequest(bidderRequestWithFullGdpr); + let result = spec.blockTheRequest(); expect(result).to.be.false; - config.resetConfig(); }); }); describe('getPageId', function() { @@ -2876,11 +2714,10 @@ describe('ozone Adapter', function () { expect(response[1].bid.length).to.equal(2); }); }); - /** - * spec.getWhitelabelConfigItem test - get a config value for a whitelabelled bidder, - * from a standard ozone.oz_xxxx_yyy string - */ describe('getWhitelabelConfigItem', function() { + beforeEach(function () { + config.resetConfig() + }) it('should fetch the whitelabelled equivalent config value correctly', function () { var specMock = utils.deepClone(spec); config.setConfig({'ozone': {'oz_omp_floor': 'ozone-floor-value'}}); @@ -2896,7 +2733,26 @@ describe('ozone Adapter', function () { let testKey2 = 'ozone.singleRequest'; let markbidder_config2 = specMock.getWhitelabelConfigItem(testKey2); expect(markbidder_config2).to.equal('markbidder-singlerequest-value'); - config.resetConfig(); + }); + }); + describe('setBidMediaTypeIfNotExist', function() { + it('should leave the bid object alone if it already contains mediaType', function() { + let thisBid = {mediaType: 'marktest'}; + spec.setBidMediaTypeIfNotExist(thisBid, 'replacement'); + expect(thisBid.mediaType).to.equal('marktest'); + }); + it('should change the bid object if it doesnt already contain mediaType', function() { + let thisBid = {someKey: 'someValue'}; + spec.setBidMediaTypeIfNotExist(thisBid, 'replacement'); + expect(thisBid.mediaType).to.equal('replacement'); + }); + }); + describe('getLoggableBidObject', function() { + it('should return an object without a "renderer" element', function () { + let obj = {'renderer': {}, 'somevalue': '', 'h': 100}; + let ret = spec.getLoggableBidObject(obj); + expect(ret).to.not.have.own.property('renderer'); + expect(ret.h).to.equal(100); }); }); }); From 969f0502d6a53d7af56ec1ed02a9ed40216e94be Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Tue, 9 Aug 2022 19:45:26 +0300 Subject: [PATCH 039/246] Adkernel Bid Adapter: DisplayIo alias (#8803) Adaptor alias for DisplayIo network --- modules/adkernelBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 78df319c5ee..78f00784462 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -94,7 +94,8 @@ export const spec = { {code: 'turktelekom'}, {code: 'felixads'}, {code: 'motionspots'}, - {code: 'sonic_twist'} + {code: 'sonic_twist'}, + {code: 'displayioads'} ], supportedMediaTypes: [BANNER, VIDEO, NATIVE], From 01fe272dd20d0f58b645f74278be6795a579110a Mon Sep 17 00:00:00 2001 From: Saar Amrani <89377180+saar120@users.noreply.github.com> Date: Tue, 9 Aug 2022 20:16:13 +0300 Subject: [PATCH 040/246] Vidazoo Bid Adapter: pass referrer to server + pubProvidedId support (#8797) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(module): VidazooBidAdapter - send top query params to server * feat(module): pass referrer to server. * feat(module): added pubProvidedId to supported ID systems. Co-authored-by: Udi Talias Co-authored-by: roman --- modules/vidazooBidAdapter.js | 2 ++ test/spec/modules/vidazooBidAdapter_spec.js | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index 7e2000e455a..1f84c9f4b16 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -22,6 +22,7 @@ export const SUPPORTED_ID_SYSTEMS = { 'parrableId': 1, 'pubcid': 1, 'tdid': 1, + 'pubProvidedId': 1 }; const storage = getStorageManager({ gvlid: GVLID, bidderCode: BIDDER_CODE }); @@ -72,6 +73,7 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { cb: Date.now(), bidFloor: bidFloor, bidId: bidId, + referrer: bidderRequest.refererInfo.ref, adUnitCode: adUnitCode, publisherId: pId, sessionId: sId, diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index dc178a9c485..1b4ed4b170d 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -48,7 +48,8 @@ const BIDDER_REQUEST = { }, 'uspConsent': 'consent_string', 'refererInfo': { - 'page': 'https://www.greatsite.com' + 'page': 'https://www.greatsite.com', + 'ref': 'https://www.somereferrer.com' } }; @@ -168,6 +169,7 @@ describe('VidazooBidAdapter', function () { usPrivacy: 'consent_string', sizes: ['300x250', '300x600'], url: 'https%3A%2F%2Fwww.greatsite.com', + referrer: 'https://www.somereferrer.com', cb: 1000, bidFloor: 0.1, bidId: '2d52001cabd527', From ac5f36d648bc5a2e3e1d500c979c648585d28fed Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 9 Aug 2022 13:39:28 -0400 Subject: [PATCH 041/246] Update aniviewBidAdapter.js (#8802) --- modules/aniviewBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/aniviewBidAdapter.js b/modules/aniviewBidAdapter.js index e97a2531def..00280379133 100644 --- a/modules/aniviewBidAdapter.js +++ b/modules/aniviewBidAdapter.js @@ -203,7 +203,7 @@ function interpretResponse(serverResponse, bidRequest) { let xml = new window.DOMParser().parseFromString(xmlStr, 'text/xml'); if (xml && xml.getElementsByTagName('parsererror').length == 0) { let cpmData = getCpmData(xml); - if (cpmData && cpmData.cpm > 0) { + if (cpmData.cpm > 0) { bidResponse.requestId = bidRequest.data.bidId; bidResponse.ad = ''; bidResponse.cpm = cpmData.cpm; From 1cae444020a6535e1396fea9f0f71c6a943c172d Mon Sep 17 00:00:00 2001 From: abermanov-zeta <95416296+abermanov-zeta@users.noreply.github.com> Date: Tue, 9 Aug 2022 20:14:12 +0200 Subject: [PATCH 042/246] Zeta Global BidAdapter: update the bid endpoint. (#8801) * Zeta Global BidAdapter: update the bid endpoint. * Zeta Global BidAdapter: fix tests. --- modules/zeta_global_sspBidAdapter.js | 2 +- test/spec/modules/zeta_global_sspBidAdapter_spec.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/zeta_global_sspBidAdapter.js b/modules/zeta_global_sspBidAdapter.js index 86b28021bab..d80265b28bb 100644 --- a/modules/zeta_global_sspBidAdapter.js +++ b/modules/zeta_global_sspBidAdapter.js @@ -5,7 +5,7 @@ import {config} from '../src/config.js'; import {parseDomain} from '../src/refererDetection.js'; const BIDDER_CODE = 'zeta_global_ssp'; -const ENDPOINT_URL = 'https://ssp.disqus.com/bid'; +const ENDPOINT_URL = 'https://ssp.disqus.com/bid/prebid'; const USER_SYNC_URL_IFRAME = 'https://ssp.disqus.com/sync?type=iframe'; const USER_SYNC_URL_IMAGE = 'https://ssp.disqus.com/sync?type=image'; const DEFAULT_CUR = 'USD'; diff --git a/test/spec/modules/zeta_global_sspBidAdapter_spec.js b/test/spec/modules/zeta_global_sspBidAdapter_spec.js index e67629cb4f7..a70c1c79828 100644 --- a/test/spec/modules/zeta_global_sspBidAdapter_spec.js +++ b/test/spec/modules/zeta_global_sspBidAdapter_spec.js @@ -271,7 +271,7 @@ describe('Zeta Ssp Bid Adapter', function () { it('Test required params in banner request', function () { const request = spec.buildRequests(bannerRequest, bannerRequest[0]); const payload = JSON.parse(request.data); - expect(request.url).to.eql('https://ssp.disqus.com/bid?shortname=test_shortname'); + expect(request.url).to.eql('https://ssp.disqus.com/bid/prebid?shortname=test_shortname'); expect(payload.ext.sid).to.eql('publisherId'); expect(payload.ext.tags.someTag).to.eql(444); expect(payload.ext.tags.shortname).to.be.undefined; @@ -280,7 +280,7 @@ describe('Zeta Ssp Bid Adapter', function () { it('Test required params in video request', function () { const request = spec.buildRequests(videoRequest, videoRequest[0]); const payload = JSON.parse(request.data); - expect(request.url).to.eql('https://ssp.disqus.com/bid?shortname=test_shortname'); + expect(request.url).to.eql('https://ssp.disqus.com/bid/prebid?shortname=test_shortname'); expect(payload.ext.sid).to.eql('publisherId'); expect(payload.ext.tags.someTag).to.eql(444); expect(payload.ext.tags.shortname).to.be.undefined; From 34d44793e124ae76efcf4840a79ce5f2a778e64d Mon Sep 17 00:00:00 2001 From: Mikael Lundin Date: Tue, 9 Aug 2022 20:21:24 +0200 Subject: [PATCH 043/246] Adnuntius Bid Adapter: allow Adnuntius to read dimensions (#8806) * package lock fix. * Add dimensions to prebid. --- modules/adnuntiusBidAdapter.js | 4 +++- test/spec/modules/adnuntiusBidAdapter_spec.js | 10 +++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index b9499af7d9d..0ae8411c073 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -89,7 +89,9 @@ export const spec = { networks[network].adUnits = networks[network].adUnits || []; if (bidderRequest && bidderRequest.refererInfo) networks[network].context = bidderRequest.refererInfo.page; if (adnMeta) networks[network].metaData = adnMeta; - networks[network].adUnits.push({ ...targeting, auId: bid.params.auId, targetId: bid.bidId }); + const adUnit = { ...targeting, auId: bid.params.auId, targetId: bid.bidId } + if (bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) adUnit.dimensions = bid.mediaTypes.banner.sizes + networks[network].adUnits.push(adUnit); } const networkKeys = Object.keys(networks) diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index 403ba63d7d1..2d5ea630f0f 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -13,7 +13,7 @@ describe('adnuntiusBidAdapter', function () { const meta = [{ key: 'usi', value: usi }] before(() => { - const storage = getStorageManager({gvlid: GVLID, moduleName: 'adnuntius'}) + const storage = getStorageManager({ gvlid: GVLID, moduleName: 'adnuntius' }) storage.setDataInLocalStorage('adn.metaData', JSON.stringify(meta)) }); @@ -46,7 +46,11 @@ describe('adnuntiusBidAdapter', function () { auId: '8b6bc', network: 'adnuntius', }, - + mediaTypes: { + banner: { + sizes: [[640, 480], [600, 400]], + } + }, } ] @@ -228,7 +232,7 @@ describe('adnuntiusBidAdapter', function () { expect(request[0]).to.have.property('url'); expect(request[0].url).to.equal(ENDPOINT_URL); expect(request[0]).to.have.property('data'); - expect(request[0].data).to.equal('{\"adUnits\":[{\"auId\":\"8b6bc\",\"targetId\":\"123\"}],\"metaData\":{\"usi\":\"' + usi + '\"}}'); + expect(request[0].data).to.equal('{\"adUnits\":[{\"auId\":\"8b6bc\",\"targetId\":\"123\",\"dimensions\":[[640,480],[600,400]]}],\"metaData\":{\"usi\":\"' + usi + '\"}}'); }); it('Test Video requests', function () { From 8de67ab45c6db4790c0adf4509ed98345f9f34e3 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Wed, 10 Aug 2022 07:36:33 -0700 Subject: [PATCH 044/246] Sharethrough Bid Adapter: Fix `bcat` and `badv` from First Party Data (#8808) * [PGE-178206885] Fix bcat and badv via first party data PGE-178206885 * Fix version number --- modules/sharethroughBidAdapter.js | 6 +++--- test/spec/modules/sharethroughBidAdapter_spec.js | 11 ++++++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index 731fc5c3a4e..cfbd94096bd 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -4,7 +4,7 @@ import { config } from '../src/config.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { createEidsArray } from './userId/eids.js'; -const VERSION = '4.1.1'; +const VERSION = '4.2.0'; const BIDDER_CODE = 'sharethrough'; const SUPPLY_ID = 'WYu2BXv1'; @@ -58,8 +58,8 @@ export const sharethroughAdapterSpec = { schain: bidRequests[0].schain, }, }, - bcat: deepAccess(bidderRequest.ortb2Imp, 'bcat') || bidRequests[0].params.bcat || [], - badv: bidRequests[0].params.badv || [], + bcat: deepAccess(bidderRequest.ortb2, 'bcat') || bidRequests[0].params.bcat || [], + badv: deepAccess(bidderRequest.ortb2, 'badv') || bidRequests[0].params.badv || [], test: 0, }; diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index 7addd9ee786..bb5e8e69b6e 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -480,6 +480,8 @@ describe('sharethrough adapter spec', function () { }, }, }, + bcat: ['IAB1', 'IAB2-1'], + badv: ['domain1.com', 'domain2.com'], }; it('should include first party data in open rtb request, site section', () => { @@ -493,13 +495,20 @@ describe('sharethrough adapter spec', function () { }); it('should include first party data in open rtb request, user section', () => { - const openRtbReq = spec.buildRequests(bidRequests, {...bidderRequest, ortb2: firstPartyData})[0].data; + const openRtbReq = spec.buildRequests(bidRequests, { ...bidderRequest, ortb2: firstPartyData })[0].data; expect(openRtbReq.user.yob).to.equal(firstPartyData.user.yob); expect(openRtbReq.user.gender).to.equal(firstPartyData.user.gender); expect(openRtbReq.user.ext.data).to.deep.equal(firstPartyData.user.ext.data); expect(openRtbReq.user.ext.eids).not.to.be.undefined; }); + + it('should include first party data in open rtb request, ORTB blocked section', () => { + const openRtbReq = spec.buildRequests(bidRequests, { ...bidderRequest, ortb2: firstPartyData })[0].data; + + expect(openRtbReq.bcat).to.deep.equal(firstPartyData.bcat); + expect(openRtbReq.badv).to.deep.equal(firstPartyData.badv); + }); }); }); From 2227454e6f21915f951b59dd2e39a032516c8273 Mon Sep 17 00:00:00 2001 From: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Date: Wed, 10 Aug 2022 20:38:56 +0530 Subject: [PATCH 045/246] resolved merge config issue (#8791) Co-authored-by: pm-azhar-mulla --- src/config.js | 12 ++++++------ test/spec/config_spec.js | 4 ++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/config.js b/src/config.js index 30ab8f35e2c..5e89210dfcb 100644 --- a/src/config.js +++ b/src/config.js @@ -386,7 +386,11 @@ export function newConfig() { option = Object.assign({}, defaults[topic], option); } - topicalConfig[topic] = config[topic] = option; + try { + topicalConfig[topic] = config[topic] = option; + } catch (e) { + logWarn(`Cannot set config for property ${topic} : `, e) + } }); callSubscribers(topicalConfig); @@ -525,11 +529,7 @@ export function newConfig() { return; } - const mergedConfig = Object.keys(obj).reduce((accum, key) => { - const prevConf = _getConfig()[key] || {}; - accum[key] = mergeDeep(prevConf, obj[key]); - return accum; - }, {}); + const mergedConfig = mergeDeep(_getConfig(), obj); setConfig({ ...mergedConfig }); return mergedConfig; diff --git a/test/spec/config_spec.js b/test/spec/config_spec.js index 294fce2fa9b..d7f6b9de6c0 100644 --- a/test/spec/config_spec.js +++ b/test/spec/config_spec.js @@ -596,6 +596,7 @@ describe('config API', function () { } setConfig({ + bidderTimeout: 2000, ortb2: { user: { data: [userObj1, userObj2] @@ -609,6 +610,7 @@ describe('config API', function () { }); const rtd = { + bidderTimeout: 3000, ortb2: { user: { data: [userObj1] @@ -623,11 +625,13 @@ describe('config API', function () { mergeConfig(rtd); let ortb2Config = getConfig('ortb2'); + let bidderTimeout = getConfig('bidderTimeout'); expect(ortb2Config.user.data).to.deep.include.members([userObj1, userObj2]); expect(ortb2Config.site.content.data).to.deep.include.members([siteObj1]); expect(ortb2Config.user.data).to.have.lengthOf(2); expect(ortb2Config.site.content.data).to.have.lengthOf(1); + expect(bidderTimeout).to.equal(3000); }); it('should not corrupt global configuration with bidder configuration', () => { From b273c5712fe7311a1cf117ffa42ff143f9f43271 Mon Sep 17 00:00:00 2001 From: Wls-demo <67785512+Wls-demo@users.noreply.github.com> Date: Wed, 10 Aug 2022 19:00:12 +0300 Subject: [PATCH 046/246] Boldwin Bid Adapter: added endpointId param (#8798) * new boldwin bid adapter * fix * Restarting ci / circleci * changes * fixed conflicts * added endpointId param * kick off CircleCI tests Co-authored-by: Aiholkin Co-authored-by: Vladislav Isaiko Co-authored-by: Mykhailo Yaremchuk Co-authored-by: Chris Huie --- modules/boldwinBidAdapter.js | 15 ++++++++++++--- modules/boldwinBidAdapter.md | 17 +++++++++++++++++ test/spec/modules/boldwinBidAdapter_spec.js | 3 ++- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/modules/boldwinBidAdapter.js b/modules/boldwinBidAdapter.js index ec580f8c3c2..ec9aeb2b888 100644 --- a/modules/boldwinBidAdapter.js +++ b/modules/boldwinBidAdapter.js @@ -46,7 +46,7 @@ export const spec = { supportedMediaTypes: [BANNER, VIDEO, NATIVE], isBidRequestValid: (bid) => { - return Boolean(bid.bidId && bid.params && bid.params.placementId); + return Boolean(bid.bidId && bid.params && (bid.params.placementId || bid.params.endpointId)); }, buildRequests: (validBidRequests = [], bidderRequest) => { @@ -85,7 +85,7 @@ export const spec = { for (let i = 0; i < len; i++) { let bid = validBidRequests[i]; - const { mediaTypes } = bid; + const { mediaTypes, params } = bid; const placement = {}; let sizes; if (mediaTypes) { @@ -114,9 +114,18 @@ export const spec = { placement.native = mediaTypes[NATIVE]; } } + + const { placementId, endpointId } = params; + if (placementId) { + placement.placementId = placementId; + placement.type = 'publisher'; + } else if (endpointId) { + placement.endpointId = endpointId; + placement.type = 'network'; + } + placements.push({ ...placement, - placementId: bid.params.placementId, bidId: bid.bidId, sizes: sizes || [], wPlayer: sizes ? sizes[0] : 0, diff --git a/modules/boldwinBidAdapter.md b/modules/boldwinBidAdapter.md index 5e2a5b139b3..47f4b4ffe78 100644 --- a/modules/boldwinBidAdapter.md +++ b/modules/boldwinBidAdapter.md @@ -47,5 +47,22 @@ Module that connects to boldwin demand sources } ] } + // Will return static test banner + { + code: 'endpointId_0', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [ + { + bidder: 'boldwin', + params: { + endpointId: 'testBanner', + } + } + ] + }, ]; ``` diff --git a/test/spec/modules/boldwinBidAdapter_spec.js b/test/spec/modules/boldwinBidAdapter_spec.js index afb5f935621..5b51183ea6d 100644 --- a/test/spec/modules/boldwinBidAdapter_spec.js +++ b/test/spec/modules/boldwinBidAdapter_spec.js @@ -59,11 +59,12 @@ describe('BoldwinBidAdapter', function () { expect(data.gdpr).to.not.exist; expect(data.ccpa).to.not.exist; let placement = data['placements'][0]; - expect(placement).to.have.keys('placementId', 'bidId', 'adFormat', 'sizes', 'hPlayer', 'wPlayer', 'schain', 'bidFloor'); + expect(placement).to.have.keys('placementId', 'bidId', 'adFormat', 'sizes', 'hPlayer', 'wPlayer', 'schain', 'bidFloor', 'type'); expect(placement.placementId).to.equal('testBanner'); expect(placement.bidId).to.equal('23fhj33i987f'); expect(placement.adFormat).to.equal(BANNER); expect(placement.schain).to.be.an('object'); + expect(placement.type).to.exist.and.to.equal('publisher'); }); it('Returns valid data for mediatype video', function () { From c64d0bae03e0e9904481f8f7ccc879f2eab3b2cf Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Wed, 10 Aug 2022 11:01:14 -0700 Subject: [PATCH 047/246] ShowHeroes Bid Adapter: add new endpoint (#8816) * add ShowHeroes Adapter * ShowHeroes adapter - expanded outstream support * Revert "ShowHeroes adapter - expanded outstream support" This reverts commit bfcdb913b52012b5afbf95a84956b906518a4b51. * ShowHeroes adapter - expanded outstream support * ShowHeroes adapter - fixes (#4222) * ShowHeroes adapter - banner and outstream fixes (#4222) * ShowHeroes adapter - description and outstream changes (#4222) * ShowHeroes adapter - increase test coverage and small fix * ShowHeroes Adapter - naming convention issue * Mixed AdUnits declaration support * ITDEV-4723 PrebidJS adapter support with SupplyChain module object * ITDEV-4723 Fix tests * ITDEV-4723 New entry point * showheroes-bsBidAdapter: Add support for advertiserDomains * showheroes-bsBidAdapter: hotfix for outstream render * showheroes-bsBidAdapter: update renderer url * showheroes-bsBidAdapter: use only the necessary fields from the gdprConsent * ShowHeroes adapter - added a new endpoint * ShowHeroes adapter - unit tests * Update showheroes-bsBidAdapter.md * kick off tests Co-authored-by: Eldengrof Co-authored-by: veranevera Co-authored-by: Elizaveta Voziyanova <44549195+h2p4x8@users.noreply.github.com> --- modules/showheroes-bsBidAdapter.js | 178 +++++++++++++----- modules/showheroes-bsBidAdapter.md | 49 +++++ .../modules/showheroes-bsBidAdapter_spec.js | 175 ++++++++++++++++- 3 files changed, 352 insertions(+), 50 deletions(-) diff --git a/modules/showheroes-bsBidAdapter.js b/modules/showheroes-bsBidAdapter.js index c1987a32c80..98451ebbb2f 100644 --- a/modules/showheroes-bsBidAdapter.js +++ b/modules/showheroes-bsBidAdapter.js @@ -1,4 +1,11 @@ -import { deepAccess, getBidIdParameter, getWindowTop, logError } from '../src/utils.js'; +import { + deepAccess, + getBidIdParameter, + getWindowTop, + triggerPixel, + logInfo, + logError +} from '../src/utils.js'; import { config } from '../src/config.js'; import { Renderer } from '../src/Renderer.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; @@ -7,6 +14,7 @@ import { loadExternalScript } from '../src/adloader.js'; const PROD_ENDPOINT = 'https://bs.showheroes.com/api/v1/bid'; const STAGE_ENDPOINT = 'https://bid-service.stage.showheroes.com/api/v1/bid'; +const VIRALIZE_ENDPOINT = 'https://ads.viralize.tv/prebid-sh/'; const PROD_PUBLISHER_TAG = 'https://static.showheroes.com/publishertag.js'; const STAGE_PUBLISHER_TAG = 'https://pubtag.stage.showheroes.com/publishertag.js'; const PROD_VL = 'https://video-library.showheroes.com'; @@ -26,12 +34,13 @@ export const spec = { aliases: ['showheroesBs'], supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid: function(bid) { - return !!bid.params.playerId; + return !!bid.params.playerId || !!bid.params.unitId; }, buildRequests: function(validBidRequests, bidderRequest) { let adUnits = []; - const pageURL = validBidRequests[0].params.contentPageUrl || bidderRequest.refererInfo.page; + const pageURL = validBidRequests[0].params.contentPageUrl || bidderRequest.refererInfo.referer; const isStage = !!validBidRequests[0].params.stage; + const isViralize = !!validBidRequests[0].params.unitId; const isOutstream = deepAccess(validBidRequests[0], 'mediaTypes.video.context') === 'outstream'; const isCustomRender = deepAccess(validBidRequests[0], 'params.outstreamOptions.customRender'); const isNodeRender = deepAccess(validBidRequests[0], 'params.outstreamOptions.slot') || deepAccess(validBidRequests[0], 'params.outstreamOptions.iframe'); @@ -40,12 +49,19 @@ export const spec = { const isBanner = !!validBidRequests[0].mediaTypes.banner || (isOutstream && !(isCustomRender || isNativeRender || isNodeRender)); const defaultSchain = validBidRequests[0].schain || {}; + const consentData = bidderRequest.gdprConsent || {}; + const gdprConsent = { + apiVersion: consentData.apiVersion || 2, + gdprApplies: consentData.gdprApplies || 0, + consentString: consentData.consentString || '', + } + validBidRequests.forEach((bid) => { const videoSizes = getVideoSizes(bid); const bannerSizes = getBannerSizes(bid); const vpaidMode = getBidIdParameter('vpaidMode', bid.params); - const makeBids = (type, size) => { + const makeBids = (type, size, isViralize) => { let context = ''; let streamType = 2; @@ -61,53 +77,79 @@ export const spec = { } } - const consentData = bidderRequest.gdprConsent || {}; - - const gdprConsent = { - apiVersion: consentData.apiVersion || 2, - gdprApplies: consentData.gdprApplies || 0, - consentString: consentData.consentString || '', - } - - return { + let rBid = { type: streamType, adUnitCode: bid.adUnitCode, bidId: bid.bidId, - mediaType: type, context: context, - playerId: getBidIdParameter('playerId', bid.params), auctionId: bidderRequest.auctionId, bidderCode: BIDDER_CODE, - gdprConsent: gdprConsent, start: +new Date(), timeout: 3000, - size: { - width: size[0], - height: size[1] - }, params: bid.params, - schain: bid.schain || defaultSchain, + schain: bid.schain || defaultSchain }; + + if (isViralize) { + rBid.unitId = getBidIdParameter('unitId', bid.params); + rBid.sizes = size; + rBid.mediaTypes = { + [type]: {'context': context} + }; + } else { + rBid.playerId = getBidIdParameter('playerId', bid.params); + rBid.mediaType = type; + rBid.size = { + width: size[0], + height: size[1] + }; + rBid.gdprConsent = gdprConsent; + } + + return rBid; }; - videoSizes.forEach((size) => { - adUnits.push(makeBids(VIDEO, size)); - }); + if (isViralize) { + if (videoSizes && videoSizes[0]) { + adUnits.push(makeBids(VIDEO, videoSizes, isViralize)); + } + if (bannerSizes && bannerSizes[0]) { + adUnits.push(makeBids(BANNER, bannerSizes, isViralize)); + } + } else { + videoSizes.forEach((size) => { + adUnits.push(makeBids(VIDEO, size)); + }); - bannerSizes.forEach((size) => { - adUnits.push(makeBids(BANNER, size)); - }); + bannerSizes.forEach((size) => { + adUnits.push(makeBids(BANNER, size)); + }); + } }); - return { - url: isStage ? STAGE_ENDPOINT : PROD_ENDPOINT, - method: 'POST', - options: {contentType: 'application/json', accept: 'application/json'}, - data: { + let endpointUrl; + let data; + + const QA = validBidRequests[0].params.qa || {}; + + if (isViralize) { + endpointUrl = VIRALIZE_ENDPOINT; + data = { + 'bidRequests': adUnits, + 'context': { + 'gdprConsent': gdprConsent, + 'schain': defaultSchain, + 'pageURL': QA.pageURL || encodeURIComponent(pageURL) + } + } + } else { + endpointUrl = isStage ? STAGE_ENDPOINT : PROD_ENDPOINT; + + data = { 'user': [], 'meta': { 'adapterVersion': 2, - 'pageURL': encodeURIComponent(pageURL), + 'pageURL': QA.pageURL || encodeURIComponent(pageURL), 'vastCacheEnabled': (!!config.getConfig('cache') && !isBanner && !outstreamOptions) || false, 'isDesktop': getWindowTop().document.documentElement.clientWidth > 700, 'xmlAndTag': !!(isOutstream && isCustomRender) || false, @@ -116,6 +158,13 @@ export const spec = { 'requests': adUnits, 'debug': validBidRequests[0].params.debug || false, } + } + + return { + url: QA.endpoint || endpointUrl, + method: 'POST', + options: {contentType: 'application/json', accept: 'application/json'}, + data: data }; }, interpretResponse: function(response, request) { @@ -149,33 +198,53 @@ export const spec = { } return syncs; }, + + onBidWon(bid) { + if (bid.callbacks) { + triggerPixel(bid.callbacks.won); + } + logInfo( + `Showheroes adapter won the auction. Bid id: ${bid.bidId || bid.requestId}` + ); + }, }; function createBids(bidRes, reqData) { - if (bidRes && (!Array.isArray(bidRes.bids) || bidRes.bids.length < 1)) { + if (!bidRes) { + return []; + } + const responseBids = bidRes.bids || bidRes.bidResponses; + if (!Array.isArray(responseBids) || responseBids.length < 1) { return []; } const bids = []; const bidMap = {}; - (reqData.requests || []).forEach((bid) => { + (reqData.requests || reqData.bidRequests || []).forEach((bid) => { bidMap[bid.bidId] = bid; }); - bidRes.bids.forEach(function (bid) { - const reqBid = bidMap[bid.bidId]; + responseBids.forEach(function (bid) { + const requestId = bid.bidId || bid.requestId; + const reqBid = bidMap[requestId]; const currentBidParams = reqBid.params; + const isViralize = !!reqBid.params.unitId; + const size = { + width: bid.width || bid.size.width, + height: bid.height || bid.size.height + }; + let bidUnit = {}; bidUnit.cpm = bid.cpm; - bidUnit.requestId = bid.bidId; + bidUnit.requestId = requestId; bidUnit.adUnitCode = reqBid.adUnitCode; bidUnit.currency = bid.currency; bidUnit.mediaType = bid.mediaType || VIDEO; bidUnit.ttl = TTL; - bidUnit.creativeId = 'c_' + bid.bidId; + bidUnit.creativeId = 'c_' + requestId; bidUnit.netRevenue = true; - bidUnit.width = bid.size.width; - bidUnit.height = bid.size.height; + bidUnit.width = size.width; + bidUnit.height = size.height; bidUnit.meta = { advertiserDomains: bid.adomain || [] }; @@ -185,24 +254,26 @@ function createBids(bidRes, reqData) { content: bid.vastXml, }; } - if (bid.vastTag) { - bidUnit.vastUrl = bid.vastTag; + if (bid.vastTag || bid.vastUrl) { + bidUnit.vastUrl = bid.vastTag || bid.vastUrl; } if (bid.mediaType === BANNER) { bidUnit.ad = getBannerHtml(bid, reqBid, reqData); } else if (bid.context === 'outstream') { const renderer = Renderer.install({ - id: bid.bidId, + id: requestId, url: 'https://static.showheroes.com/renderer.js', adUnitCode: reqBid.adUnitCode, config: { playerId: reqBid.playerId, - width: bid.size.width, - height: bid.size.height, + width: size.width, + height: size.height, vastUrl: bid.vastTag, vastXml: bid.vastXml, + ad: bid.ad, debug: reqData.debug, - isStage: !!reqData.meta.stage, + isStage: reqData.meta && !!reqData.meta.stage, + isViralize: isViralize, customRender: getBidIdParameter('customRender', currentBidParams.outstreamOptions), slot: getBidIdParameter('slot', currentBidParams.outstreamOptions), iframe: getBidIdParameter('iframe', currentBidParams.outstreamOptions), @@ -218,7 +289,12 @@ function createBids(bidRes, reqData) { } function outstreamRender(bid) { - const embedCode = createOutstreamEmbedCode(bid); + let embedCode; + if (bid.renderer.config.isViralize) { + embedCode = createOutstreamEmbedCodeV2(bid); + } else { + embedCode = createOutstreamEmbedCode(bid); + } if (typeof bid.renderer.config.customRender === 'function') { bid.renderer.config.customRender(bid, embedCode); } else { @@ -266,6 +342,12 @@ function createOutstreamEmbedCode(bid) { return fragment; } +function createOutstreamEmbedCodeV2(bid) { + const range = document.createRange(); + range.selectNode(document.getElementsByTagName('body')[0]); + return range.createContextualFragment(getBidIdParameter('ad', bid.renderer.config)); +} + function getBannerHtml (bid, reqBid, reqData) { const isStage = !!reqData.meta.stage; const urls = getEnvURLs(isStage); diff --git a/modules/showheroes-bsBidAdapter.md b/modules/showheroes-bsBidAdapter.md index cde652e9d83..a32a77a2525 100644 --- a/modules/showheroes-bsBidAdapter.md +++ b/modules/showheroes-bsBidAdapter.md @@ -125,3 +125,52 @@ Module that connects to ShowHeroes demand source to fetch bids. } ]; ``` + +# Test Parameters (V2) +``` + var adUnits = [ + { + code: 'video', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream', + } + }, + bids: [ + { + bidder: "showheroes-bs", + params: { + unitId: 'AACBWAcof-611K4U', + vpaidMode: true // by default is 'false' + } + } + ] + }, + { + code: 'video', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'outstream', + } + }, + bids: [ + { + bidder: "showheroes-bs", + params: { + unitId: 'AACBTwsZVANd9NlB', + + outstreamOptions: { + // Required for the outstream renderer to exact node, one of + iframe: 'iframe_id', + // or + slot: 'slot_id' + } + } + } + ] + } + ]; +``` + diff --git a/test/spec/modules/showheroes-bsBidAdapter_spec.js b/test/spec/modules/showheroes-bsBidAdapter_spec.js index 69e8343dfc9..1aa7b132221 100644 --- a/test/spec/modules/showheroes-bsBidAdapter_spec.js +++ b/test/spec/modules/showheroes-bsBidAdapter_spec.js @@ -48,6 +48,18 @@ const bidRequestCommonParams = { 'auctionId': '43aa080090a47f', } +const bidRequestCommonParamsV2 = { + 'bidder': 'showheroes-bs', + 'params': { + 'unitId': 'AACBWAcof-611K4U', + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[640, 480]], + 'bidId': '38b373e1e31c18', + 'bidderRequestId': '12e3ade2543ba6', + 'auctionId': '43aa080090a47f', +} + const bidRequestVideo = { ...bidRequestCommonParams, ...{ @@ -72,6 +84,30 @@ const bidRequestOutstream = { } } +const bidRequestVideoV2 = { + ...bidRequestCommonParamsV2, + ...{ + 'mediaTypes': { + 'video': { + 'playerSize': [640, 480], + 'context': 'instream', + } + } + } +} + +const bidRequestOutstreamV2 = { + ...bidRequestCommonParamsV2, + ...{ + 'mediaTypes': { + 'video': { + 'playerSize': [640, 480], + 'context': 'outstream' + } + } + } +} + const bidRequestVideoVpaid = { ...bidRequestCommonParams, ...{ @@ -129,12 +165,19 @@ describe('shBidAdapter', function () { describe('isBidRequestValid', function () { it('should return true when required params found', function () { - const request = { + const requestV1 = { 'params': { 'playerId': '47427aa0-f11a-4d24-abca-1295a46a46cd', } } - expect(spec.isBidRequestValid(request)).to.equal(true) + expect(spec.isBidRequestValid(requestV1)).to.equal(true) + + const requestV2 = { + 'params': { + 'unitId': 'AACBTwsZVANd9NlB', + } + } + expect(spec.isBidRequestValid(requestV2)).to.equal(true) }) it('should return false when required params are not passed', function () { @@ -149,6 +192,9 @@ describe('shBidAdapter', function () { it('sends bid request to ENDPOINT via POST', function () { const request = spec.buildRequests([bidRequestVideo], bidderRequest) expect(request.method).to.equal('POST') + + const requestV2 = spec.buildRequests([bidRequestVideoV2], bidderRequest) + expect(requestV2.method).to.equal('POST') }) it('check sizes formats', function () { @@ -268,6 +314,30 @@ describe('shBidAdapter', function () { expect(payload2).to.have.property('type', 5); }) + it('should attach valid params to the payload when type is video (instream V2)', function () { + const request = spec.buildRequests([bidRequestVideoV2], bidderRequest) + const payload = request.data.bidRequests[0]; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('unitId', 'AACBWAcof-611K4U'); + expect(payload.mediaTypes).to.eql({ + [VIDEO]: { + 'context': 'instream' + } + }); + }) + + it('should attach valid params to the payload when type is video (outstream V2)', function () { + const request = spec.buildRequests([bidRequestOutstreamV2], bidderRequest) + const payload = request.data.bidRequests[0]; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('unitId', 'AACBWAcof-611K4U'); + expect(payload.mediaTypes).to.eql({ + [VIDEO]: { + 'context': 'outstream' + } + }); + }) + it('passes gdpr if present', function () { const request = spec.buildRequests([bidRequestVideo], {...bidderRequest, ...gdpr}) const payload = request.data.requests[0]; @@ -275,6 +345,13 @@ describe('shBidAdapter', function () { expect(payload.gdprConsent).to.eql(gdpr.gdprConsent) }) + it('passes gdpr if present (V2)', function () { + const request = spec.buildRequests([bidRequestVideoV2], {...bidderRequest, ...gdpr}) + const context = request.data.context; + expect(context).to.be.an('object'); + expect(context.gdprConsent).to.eql(gdpr.gdprConsent) + }) + it('passes schain object if present', function() { const request = spec.buildRequests([{ ...bidRequestVideo, @@ -284,6 +361,16 @@ describe('shBidAdapter', function () { expect(payload).to.be.an('object'); expect(payload.schain).to.eql(schain.schain); }) + + it('passes schain object if present (V2)', function() { + const request = spec.buildRequests([{ + ...bidRequestVideoV2, + ...schain + }], bidderRequest) + const context = request.data.context; + expect(context).to.be.an('object'); + expect(context.schain).to.eql(schain.schain); + }) }) describe('interpretResponse', function () { @@ -327,6 +414,39 @@ describe('shBidAdapter', function () { }], }; + const basicResponseV2 = { + 'requestId': '38b373e1e31c18', + 'adUnitCode': 'adunit-code-1', + 'cpm': 1, + 'currency': 'EUR', + 'width': 640, + 'height': 480, + 'advertiserDomain': [], + 'callbacks': { + 'won': ['https://api-n729.qa.viralize.com/track/?ver=15&session_id=01ecd03ce381505ccdeb88e555b05001&category=request_session&type=event&request_session_id=01ecd03ce381505ccdeb88e555b05001&label=prebid_won&reason=ok'] + }, + 'mediaType': 'video', + 'adomain': adomain, + }; + + const vastUrl = 'https://api-n729.qa.viralize.com/vast/?zid=AACBWAcof-611K4U&u=https://example.org/?foo=bar&gdpr=0&cs=XXXXXXXXXXXXXXXXXXXX&sid=01ecd03ce381505ccdeb88e555b05001&width=300&height=200&prebidmode=1' + + const responseVideoV2 = { + 'bidResponses': [{ + ...basicResponseV2, + 'context': 'instream', + 'vastUrl': vastUrl, + }], + }; + + const responseVideoOutstreamV2 = { + 'bidResponses': [{ + ...basicResponseV2, + 'context': 'outstream', + 'ad': '', + }], + }; + it('should get correct bid response when type is video', function () { const request = spec.buildRequests([bidRequestVideo], bidderRequest) const expectedResponse = [ @@ -356,6 +476,31 @@ describe('shBidAdapter', function () { expect(result).to.deep.equal(expectedResponse) }) + it('should get correct bid response when type is video (V2)', function () { + const request = spec.buildRequests([bidRequestVideoV2], bidderRequest) + const expectedResponse = [ + { + 'cpm': 1, + 'creativeId': 'c_38b373e1e31c18', + 'adUnitCode': 'adunit-code-1', + 'currency': 'EUR', + 'width': 640, + 'height': 480, + 'mediaType': 'video', + 'netRevenue': true, + 'vastUrl': vastUrl, + 'requestId': '38b373e1e31c18', + 'ttl': 300, + 'meta': { + 'advertiserDomains': adomain + } + } + ] + + const result = spec.interpretResponse({'body': responseVideoV2}, request) + expect(result).to.deep.equal(expectedResponse) + }) + it('should get correct bid response when type is banner', function () { const request = spec.buildRequests([bidRequestBanner], bidderRequest) @@ -396,6 +541,32 @@ describe('shBidAdapter', function () { expect(spots.length).to.equal(1) }) + it('should get correct bid response when type is outstream (slot V2)', function () { + const bidRequestV2 = JSON.parse(JSON.stringify(bidRequestOutstreamV2)); + const slotId = 'testSlot2' + bidRequestV2.params.outstreamOptions = { + slot: slotId + } + + const container = document.createElement('div') + container.setAttribute('id', slotId) + document.body.appendChild(container) + + const request = spec.buildRequests([bidRequestV2], bidderRequest) + + const result = spec.interpretResponse({'body': responseVideoOutstreamV2}, request) + const bid = result[0] + expect(bid).to.have.property('mediaType', VIDEO); + + const renderer = bid.renderer + expect(renderer).to.be.an('object') + expect(renderer.id).to.equal(bidRequestV2.bidId) + renderer.render(bid) + + const scripts = container.querySelectorAll('#testScript') + expect(scripts.length).to.equal(1) + }) + it('should get correct bid response when type is outstream (iframe)', function () { const bidRequest = JSON.parse(JSON.stringify(bidRequestOutstream)); const slotId = 'testIframe' From 9afa2b04fff85cbc4d5a22daf3b506be2a86884c Mon Sep 17 00:00:00 2001 From: Samuel Adu Date: Wed, 10 Aug 2022 20:05:04 +0100 Subject: [PATCH 048/246] Yahoossp bid adapter: Fix schain data handling (#8817) * Yahoossp bid adapter: Fix schain data handling * Add missing trailing semicolon Co-authored-by: slimkrazy --- modules/yahoosspBidAdapter.js | 3 ++- test/spec/modules/yahoosspBidAdapter_spec.js | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/modules/yahoosspBidAdapter.js b/modules/yahoosspBidAdapter.js index 926c3317f1d..d1b7b08c202 100644 --- a/modules/yahoosspBidAdapter.js +++ b/modules/yahoosspBidAdapter.js @@ -281,7 +281,8 @@ function generateOpenRtbObject(bidderRequest, bid) { outBoundBidRequest = appendFirstPartyData(outBoundBidRequest, bid); }; - if (deepAccess(bid, 'schain')) { + const schainData = deepAccess(bid, 'schain.nodes'); + if (isArray(schainData) && schainData.length > 0) { outBoundBidRequest.source.ext.schain = bid.schain; outBoundBidRequest.source.ext.schain.nodes[0].rid = outBoundBidRequest.id; }; diff --git a/test/spec/modules/yahoosspBidAdapter_spec.js b/test/spec/modules/yahoosspBidAdapter_spec.js index 4dce1358fae..6e52e9c57db 100644 --- a/test/spec/modules/yahoosspBidAdapter_spec.js +++ b/test/spec/modules/yahoosspBidAdapter_spec.js @@ -334,6 +334,19 @@ describe('YahooSSP Bid Adapter:', () => { }); describe('Schain module support:', () => { + it('should not include schain data when schain array is empty', function () { + const { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const globalSchain = { + ver: '1.0', + complete: 1, + nodes: [] + }; + bidRequest.schain = globalSchain; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const schain = data.source.ext.schain; + expect(schain).to.be.undefined; + }); + it('should send Global or Bidder specific schain', function () { const { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); const globalSchain = { From 2ee4d3f21f5767734222d0298d3b538e6cc3c859 Mon Sep 17 00:00:00 2001 From: caseywhitmire <60086994+caseywhitmire@users.noreply.github.com> Date: Wed, 10 Aug 2022 17:43:13 -0700 Subject: [PATCH 049/246] Various files: fix LGTM trailing semi-colon (#8811) * akamaiDapRtdProvider: fix LGTM trailing semi-colon * newspassidBidAdapter: fix LGTM trailing semi-colon * native: fix LGTM trailing semi-colon * kueezBidAdapter: fix LGTM trailing semi-colon * shinezBidAdapter: fix LGTM trailing semi-colon * openxOrtbBidAdapter: fix LGTM trailing semi-colon * bliinkBidAdapter: fix LGTM trailing semi-colon * c1xBidAdapter: fix LGTM trailing semi-colon * improvedigitalBidAdapter: fix LGTM trailing semi-colon * weboramaRtdProvider: fix LGTM trailing semi-colon * 1plusXRtdProvider: fix LGTM trailing semi-colon * byDataAnalyticsAdapter: fix LGTM trailing semi-colon * index: fix LGTM trailing semi-colon * userId/index: fix LGTM trailing semi-colon * taboolaBidAdapter: fix LGTM trailing semi-colon * mediafuseBidAdapter: fix LGTM trailing semi-colon * videoheroesBidAdapter: fix LGTM trailing semi-colon * gulpfile: fix LGTM trailing semi-colon * criteoBidAdapter: fix LGTM trailing semi-colon * eplanningBidAdapter: fix LGTM trailing semi-colon * hadronAnalyticsAdapter: fix LGTM trailing semi-colon * config: fix LGTM trailing semi-colon * imuIdSystem: fix LGTM trailing semi-colon * spotxBidAdapter: fix LGTM trailing semi-colon --- gulpfile.js | 10 +++++----- modules/1plusXRtdProvider.js | 2 +- modules/akamaiDapRtdProvider.js | 4 ++-- modules/bliinkBidAdapter.js | 2 +- modules/byDataAnalyticsAdapter.js | 2 +- modules/c1xBidAdapter.js | 2 +- modules/criteoBidAdapter.js | 2 +- modules/eplanningBidAdapter.js | 6 +++--- modules/hadronAnalyticsAdapter.js | 4 ++-- modules/improvedigitalBidAdapter.js | 4 ++-- modules/imuIdSystem.js | 2 +- modules/kueezBidAdapter.js | 4 ++-- modules/mediafuseBidAdapter.js | 16 ++++++++-------- modules/newspassidBidAdapter.js | 2 +- modules/openxOrtbBidAdapter.js | 6 +++--- modules/prebidServerBidAdapter/index.js | 14 +++++++------- modules/shinezBidAdapter.js | 4 ++-- modules/spotxBidAdapter.js | 10 +++++----- modules/taboolaBidAdapter.js | 2 +- modules/userId/index.js | 2 +- modules/videoheroesBidAdapter.js | 2 +- modules/weboramaRtdProvider.js | 6 +++--- src/config.js | 2 +- src/native.js | 2 +- 24 files changed, 56 insertions(+), 56 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 3425850286d..09de874e389 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -246,7 +246,7 @@ function bundle(dev, moduleArr) { const dependencies = new Set(); [coreFile].concat(moduleFiles).map(name => path.basename(name)).forEach((file) => { (depGraph[file] || []).forEach((dep) => dependencies.add(helpers.getBuiltPath(dev, dep))); - }) + }); const entries = [coreFile].concat(Array.from(dependencies), moduleFiles); @@ -449,7 +449,7 @@ gulp.task('build-bundle-verbose', gulp.series(makeWebpackPkg({ // public tasks (dependencies are needed for each task since they can be ran on their own) gulp.task('test-only', test); -gulp.task('test-all-features-disabled', testTaskMaker({disableFeatures: require('./features.json'), oneBrowser: 'chrome', watch: false})) +gulp.task('test-all-features-disabled', testTaskMaker({disableFeatures: require('./features.json'), oneBrowser: 'chrome', watch: false})); gulp.task('test', gulp.series(clean, lint, gulp.series('test-all-features-disabled', 'test-only'))); gulp.task('test-coverage', gulp.series(clean, testCoverage)); @@ -464,12 +464,12 @@ gulp.task('serve', gulp.series(clean, lint, gulp.parallel('build-bundle-dev', wa gulp.task('serve-fast', gulp.series(clean, gulp.parallel('build-bundle-dev', watchFast))); gulp.task('serve-prod', gulp.series(clean, gulp.parallel('build-bundle-prod', startLocalServer))); gulp.task('serve-and-test', gulp.series(clean, gulp.parallel('build-bundle-dev', watchFast, testTaskMaker({watch: true})))); -gulp.task('serve-e2e', gulp.series(clean, 'build-bundle-prod', gulp.parallel(() => startIntegServer(), startLocalServer))) -gulp.task('serve-e2e-dev', gulp.series(clean, 'build-bundle-dev', gulp.parallel(() => startIntegServer(true), startLocalServer))) +gulp.task('serve-e2e', gulp.series(clean, 'build-bundle-prod', gulp.parallel(() => startIntegServer(), startLocalServer))); +gulp.task('serve-e2e-dev', gulp.series(clean, 'build-bundle-dev', gulp.parallel(() => startIntegServer(true), startLocalServer))); gulp.task('default', gulp.series(clean, 'build-bundle-prod')); -gulp.task('e2e-test-only', () => runWebdriver({file: argv.file})) +gulp.task('e2e-test-only', () => runWebdriver({file: argv.file})); gulp.task('e2e-test', gulp.series(clean, 'build-bundle-prod', testTaskMaker({e2e: true}))); // other tasks gulp.task(bundleToStdout); diff --git a/modules/1plusXRtdProvider.js b/modules/1plusXRtdProvider.js index 5affafcf9d3..9d010c8d298 100644 --- a/modules/1plusXRtdProvider.js +++ b/modules/1plusXRtdProvider.js @@ -138,7 +138,7 @@ export const updateBidderConfig = (bidder, ortb2Updates, bidderConfigs) => { if (site) { // Legacy : cf. comment on buildOrtb2Updates first lines - const currentSite = deepAccess(bidderConfigCopy, 'ortb2.site') + const currentSite = deepAccess(bidderConfigCopy, 'ortb2.site'); const updatedSite = mergeDeep(currentSite, site); deepSetValue(bidderConfigCopy, 'ortb2.site', updatedSite); } diff --git a/modules/akamaiDapRtdProvider.js b/modules/akamaiDapRtdProvider.js index f167cb7f3ea..1c2af70d737 100644 --- a/modules/akamaiDapRtdProvider.js +++ b/modules/akamaiDapRtdProvider.js @@ -202,7 +202,7 @@ export const dapUtils = { dapUtils.dapTokenize(configAsync, config.identity, onDone, function(token, status, xhr, onDone) { item.expires_at = now + DAP_DEFAULT_TOKEN_TTL; - let exp = dapUtils.dapExtractExpiryFromToken(token) + let exp = dapUtils.dapExtractExpiryFromToken(token); if (typeof exp == 'number') { item.expires_at = exp - 10; } @@ -303,7 +303,7 @@ export const dapUtils = { dapUtils.dapEncryptedMembership(configAsync, token, onDone, function(encToken, status, xhr, onDone) { item.expires_at = now + DAP_DEFAULT_TOKEN_TTL; - let exp = dapUtils.dapExtractExpiryFromToken(encToken) + let exp = dapUtils.dapExtractExpiryFromToken(encToken); if (typeof exp == 'number') { item.expires_at = exp - 10; } diff --git a/modules/bliinkBidAdapter.js b/modules/bliinkBidAdapter.js index 495862013de..127b534c989 100644 --- a/modules/bliinkBidAdapter.js +++ b/modules/bliinkBidAdapter.js @@ -96,7 +96,7 @@ export function getKeywords() { * @return {({cpm, netRevenue: boolean, requestId, width: number, currency, ttl: number, creativeId, height: number}&{mediaType: string, vastXml})|null} */ export const buildBid = (bidResponse) => { - const mediaType = deepAccess(bidResponse, 'creative.media_type') + const mediaType = deepAccess(bidResponse, 'creative.media_type'); if (!mediaType) return null; let bid; diff --git a/modules/byDataAnalyticsAdapter.js b/modules/byDataAnalyticsAdapter.js index ccf57942d7a..84479ee618b 100644 --- a/modules/byDataAnalyticsAdapter.js +++ b/modules/byDataAnalyticsAdapter.js @@ -266,7 +266,7 @@ ascAdapter.getVisitorData = function (data = {}) { } const { clientId } = initOptions; - var userId = storage.getDataFromLocalStorage('userId') + var userId = storage.getDataFromLocalStorage('userId'); if (!userId) { userId = generateUid(); storage.setDataInLocalStorage('userId', userId); diff --git a/modules/c1xBidAdapter.js b/modules/c1xBidAdapter.js index 8b579f52299..8c9407825ba 100644 --- a/modules/c1xBidAdapter.js +++ b/modules/c1xBidAdapter.js @@ -78,7 +78,7 @@ export const c1xAdapter = { url: URL, data: payloadString, bids: bidIdTags - }) + }); return bidRequest; }, diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 590062c76bd..82035d1550e 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -513,7 +513,7 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { ext: { schain: schain } - } + }; }; request.user = { ext: bidderRequest.userExt diff --git a/modules/eplanningBidAdapter.js b/modules/eplanningBidAdapter.js index a6adab1e9b9..0d37b72f4ad 100644 --- a/modules/eplanningBidAdapter.js +++ b/modules/eplanningBidAdapter.js @@ -38,14 +38,14 @@ export const spec = { const spaces = getSpaces(bidRequests, urlConfig.ml); // TODO: do the fallbacks make sense here? const pageUrl = bidderRequest.refererInfo.page || bidderRequest.refererInfo.topmostLocation; - const domain = bidderRequest.refererInfo.domain || window.location.host + const domain = bidderRequest.refererInfo.domain || window.location.host; if (urlConfig.t) { url = 'https://' + urlConfig.isv + '/layers/t_pbjs_2.json'; params = {}; } else { url = 'https://' + (urlConfig.sv || DEFAULT_SV) + '/pbjs/1/' + urlConfig.ci + '/' + dfpClientId + '/' + domain + '/' + sec; // TODO: does the fallback make sense here? - const referrerUrl = bidderRequest.refererInfo.ref || bidderRequest.refererInfo.topmostLocation + const referrerUrl = bidderRequest.refererInfo.ref || bidderRequest.refererInfo.topmostLocation; if (storage.hasLocalStorage()) { registerViewabilityAllBids(bidRequests); @@ -150,7 +150,7 @@ export const spec = { return syncs; }, -} +}; function getUserAgent() { return window.navigator.userAgent; diff --git a/modules/hadronAnalyticsAdapter.js b/modules/hadronAnalyticsAdapter.js index 073dd15ea49..52829cf754d 100644 --- a/modules/hadronAnalyticsAdapter.js +++ b/modules/hadronAnalyticsAdapter.js @@ -10,7 +10,7 @@ import {getRefererInfo} from '../src/refererDetection.js'; * hadronAnalyticsAdapter.js - Audigent Hadron Analytics Adapter */ -const HADRON_ANALYTICS_URL = 'https://analytics.hadron.ad.gt/api/v1/analytics' +const HADRON_ANALYTICS_URL = 'https://analytics.hadron.ad.gt/api/v1/analytics'; const HADRONID_ANALYTICS_VER = 'pbadgt0'; const DEFAULT_PARTNER_ID = 0; const AU_GVLID = 561; @@ -152,7 +152,7 @@ hadronAnalyticsAdapter.enableAnalytics = function(conf = {}) { } hadronAnalyticsAdapter.originEnableAnalytics(conf); -} +}; function flush() { // Don't send anything if no partner id was declared diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js index a362f85654c..4996f0efaf0 100644 --- a/modules/improvedigitalBidAdapter.js +++ b/modules/improvedigitalBidAdapter.js @@ -123,7 +123,7 @@ export const spec = { if (bidderRequest) { // GDPR - const gdprConsent = deepAccess(bidderRequest, 'gdprConsent') + const gdprConsent = deepAccess(bidderRequest, 'gdprConsent'); if (gdprConsent) { if (typeof gdprConsent.gdprApplies === 'boolean') { deepSetValue(request, 'regs.ext.gdpr', Number(gdprConsent.gdprApplies)); @@ -508,7 +508,7 @@ const ID_REQUEST = { const url = deepAccess(bidderRequest, 'refererInfo.page'); if (url) { site.page = url; - site.domain = bidderRequest.refererInfo.domain + site.domain = bidderRequest.refererInfo.domain; } const configSiteSettings = config.getConfig('site') || {}; const fpdSiteSettings = deepAccess(bidderRequest, 'ortb2.site') || {}; diff --git a/modules/imuIdSystem.js b/modules/imuIdSystem.js index 24b11dc1667..41ff95b6702 100644 --- a/modules/imuIdSystem.js +++ b/modules/imuIdSystem.js @@ -146,7 +146,7 @@ export const imuIdSubmodule = { } if (!localData.id) { - return {callback: callImuidApi(apiUrl)} + return {callback: callImuidApi(apiUrl)}; } if (localData.expired) { callImuidApi(apiUrl)(); diff --git a/modules/kueezBidAdapter.js b/modules/kueezBidAdapter.js index 4e0278603fd..24d339d1938 100644 --- a/modules/kueezBidAdapter.js +++ b/modules/kueezBidAdapter.js @@ -309,7 +309,7 @@ function generateSharedParams(sharedParams, bidderRequest) { wrapper_type: 'prebidjs', wrapper_vendor: '$$PREBID_GLOBAL$$', wrapper_version: '$prebid.version$' - } + }; if (syncEnabled) { const allowedSyncMethod = getSyncMethod(filterSettings, bidderCode); @@ -352,7 +352,7 @@ function generateSharedParams(sharedParams, bidderRequest) { params.userIds = JSON.stringify(userIds); } - return params + return params; } /** diff --git a/modules/mediafuseBidAdapter.js b/modules/mediafuseBidAdapter.js index 5a96c8b286c..e61c2e65c39 100644 --- a/modules/mediafuseBidAdapter.js +++ b/modules/mediafuseBidAdapter.js @@ -177,7 +177,7 @@ export const spec = { payload['iab_support'] = { omidpn: 'Mediafuse', omidpv: '$prebid.version$' - } + }; } if (member > 0) { @@ -185,7 +185,7 @@ export const spec = { } if (appDeviceObjBid) { - payload.device = appDeviceObj + payload.device = appDeviceObj; } if (appIdObjBid) { payload.app = appIdObj; @@ -227,7 +227,7 @@ export const spec = { } if (bidderRequest && bidderRequest.uspConsent) { - payload.us_privacy = bidderRequest.uspConsent + payload.us_privacy = bidderRequest.uspConsent; } if (bidderRequest && bidderRequest.refererInfo) { @@ -237,7 +237,7 @@ export const spec = { rd_top: bidderRequest.refererInfo.reachedTop, rd_ifs: bidderRequest.refererInfo.numIframes, rd_stk: bidderRequest.refererInfo.stack.map((url) => encodeURIComponent(url)).join(',') - } + }; payload.referrer_detection = refererinfo; } @@ -386,7 +386,7 @@ export const spec = { reloadViewabilityScriptWithCorrectParameters(bid); } } -} +}; function isPopulatedArray(arr) { return !!(isArray(arr) && arr.length > 0); @@ -404,7 +404,7 @@ function reloadViewabilityScriptWithCorrectParameters(bid) { if (viewJsPayload) { let prebidParams = 'pbjs_adid=' + bid.adId + ';pbjs_auc=' + bid.adUnitCode; - let jsTrackerSrc = getViewabilityScriptUrlFromPayload(viewJsPayload) + let jsTrackerSrc = getViewabilityScriptUrlFromPayload(viewJsPayload); let newJsTrackerSrc = jsTrackerSrc.replace('dom_id=%native_dom_id%', prebidParams); @@ -498,7 +498,7 @@ function formatRequest(payload, bidderRequest) { if (getParameterByName('apn_test').toUpperCase() === 'TRUE' || config.getConfig('apn_test') === true) { options.customHeaders = { 'X-Is-Test': 1 - } + }; } if (payload.tags.length > MAX_IMPS_PER_REQUEST) { @@ -733,7 +733,7 @@ function bidToTag(bid) { tag.code = bid.params.invCode; } tag.allow_smaller_sizes = bid.params.allowSmallerSizes || false; - tag.use_pmt_rule = bid.params.usePaymentRule || false + tag.use_pmt_rule = bid.params.usePaymentRule || false; tag.prebid = true; tag.disable_psa = true; let bidFloor = getBidFloor(bid); diff --git a/modules/newspassidBidAdapter.js b/modules/newspassidBidAdapter.js index ee6ece2b033..409cb55e6a0 100644 --- a/modules/newspassidBidAdapter.js +++ b/modules/newspassidBidAdapter.js @@ -66,7 +66,7 @@ export const spec = { this.loadConfiguredData(bid); logInfo('isBidRequestValid : ', config.getConfig(), bid); let adUnitCode = bid.adUnitCode; // adunit[n].code - let err1 = 'VALIDATION FAILED : missing {param} : siteId, placementId and publisherId are REQUIRED' + let err1 = 'VALIDATION FAILED : missing {param} : siteId, placementId and publisherId are REQUIRED'; if (!(bid.params.hasOwnProperty('placementId'))) { logError(err1.replace('{param}', 'placementId'), adUnitCode); return false; diff --git a/modules/openxOrtbBidAdapter.js b/modules/openxOrtbBidAdapter.js index bbe0e827b8e..62066ebe4f1 100644 --- a/modules/openxOrtbBidAdapter.js +++ b/modules/openxOrtbBidAdapter.js @@ -157,7 +157,7 @@ function createVideoRequest(bid, bidderRequest) { method: 'POST', url: REQUEST_URL, data: data - } + }; } function getBaseRequest(bid, bidderRequest) { @@ -191,7 +191,7 @@ function getBaseRequest(bid, bidderRequest) { utils.deepSetValue(req, 'ext.delDomain', bid.params.delDomain); } if (bid.params.test) { - req.test = 1 + req.test = 1; } if (bidderRequest.gdprConsent) { if (bidderRequest.gdprConsent.gdprApplies !== undefined) { @@ -309,7 +309,7 @@ function interpretResponse(resp, req) { response.meta.paf.content_id = utils.deepAccess(bid, 'ext.paf.content_id'); } - return response + return response; })]; }); diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 9c7c3c921b0..d99013c61f3 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -360,7 +360,7 @@ function _appendSiteAppDevice(request, pageUrl, accountId) { // ORTB specifies app OR site if (typeof config.getConfig('app') === 'object') { request.app = config.getConfig('app'); - request.app.publisher = {id: accountId} + request.app.publisher = {id: accountId}; } else { request.site = {}; if (isPlainObject(config.getConfig('site'))) { @@ -531,7 +531,7 @@ Object.assign(ORTB2.prototype, { if (bid.mediaTypes != null) { logWarn(`Prebid Server adapter does not (yet) support bidder-specific mediaTypes for the same adUnit. Size mapping configuration will be ignored for adUnit: ${adUnit.code}, bidder: ${bid.bidder}`); } - }) + }); // in case there is a duplicate imp.id, add '-2' suffix to the second imp.id. // e.g. if there are 2 adUnits (case of twin adUnit codes) with code 'test', @@ -599,7 +599,7 @@ Object.assign(ORTB2.prototype, { }, {}); } } - const nativeReq = deepAccess(adUnit, 'nativeOrtbRequest') + const nativeReq = deepAccess(adUnit, 'nativeOrtbRequest'); if (FEATURES.NATIVE && nativeReq) { const defaultRequest = { // TODO: determine best way to pass these and if we allow defaults @@ -727,7 +727,7 @@ Object.assign(ORTB2.prototype, { if (floor) { imp.bidfloor = floor.floor; - imp.bidfloorcur = floor.currency + imp.bidfloorcur = floor.currency; } if (imp.banner || imp.video || imp.native) { @@ -766,11 +766,11 @@ Object.assign(ORTB2.prototype, { } // This is no longer overwritten unless name and version explicitly overwritten by extPrebid (mergeDeep) - request.ext.prebid = Object.assign(request.ext.prebid, {channel: {name: 'pbjs', version: $$PREBID_GLOBAL$$.version}}) + request.ext.prebid = Object.assign(request.ext.prebid, {channel: {name: 'pbjs', version: $$PREBID_GLOBAL$$.version}}); // set debug flag if in debug mode if (getConfig('debug')) { - request.ext.prebid = Object.assign(request.ext.prebid, {debug: true}) + request.ext.prebid = Object.assign(request.ext.prebid, {debug: true}); } // s2sConfig video.ext.prebid is passed through openrtb to PBS @@ -1010,7 +1010,7 @@ Object.assign(ORTB2.prototype, { if (isPlainObject(ortb) && Array.isArray(ortb.assets)) { bidObject.native = { ortb, - } + }; } else { logError('prebid server native response contained no assets'); } diff --git a/modules/shinezBidAdapter.js b/modules/shinezBidAdapter.js index 923ecec7e4b..0ce2eed6479 100644 --- a/modules/shinezBidAdapter.js +++ b/modules/shinezBidAdapter.js @@ -382,7 +382,7 @@ function generateGeneralParams(generalObject, bidderRequest) { ua: navigator.userAgent, session_id: getBidIdParameter('auctionId', generalObject), tmax: timeout - } + }; const userIdsParam = getBidIdParameter('userId', generalObject); if (userIdsParam) { @@ -431,5 +431,5 @@ function generateGeneralParams(generalObject, bidderRequest) { generalParams.page_url = generalParams.page_url || config.getConfig('pageUrl') || deepAccess(window, 'location.href'); } - return generalParams + return generalParams; } diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index d044df74cfe..8a18d4973d3 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -268,7 +268,7 @@ export const spec = { ext: bid.userId.id5id.ext || {} }] } - ) + ); } // Add common id if available @@ -297,14 +297,14 @@ export const spec = { } }] } - ) + ); } // Only add the user object if it's not empty if (!isEmpty(userExt)) { requestPayload.user = { ext: userExt }; } - const urlQueryParams = 'src_sys=prebid' + const urlQueryParams = 'src_sys=prebid'; return { method: 'POST', url: URL + channelId + '?' + urlQueryParams, @@ -365,7 +365,7 @@ export const spec = { bid.vastXml = spotxBid.adm; } else { bid.cache_key = spotxBid.ext.cache_key; - bid.vastUrl = 'https://search.spotxchange.com/ad/vast.html?key=' + spotxBid.ext.cache_key + bid.vastUrl = 'https://search.spotxchange.com/ad/vast.html?key=' + spotxBid.ext.cache_key; bid.videoCacheKey = spotxBid.ext.cache_key; } @@ -453,7 +453,7 @@ function outstreamRender(bid) { loadExternalScript(easiUrl, BIDDER_CODE, undefined, undefined, attributes); } } catch (err) { - logError('[SPOTX][renderer] Error:' + err.message) + logError('[SPOTX][renderer] Error:' + err.message); } } } diff --git a/modules/taboolaBidAdapter.js b/modules/taboolaBidAdapter.js index e94e7aba7ca..8db98409a76 100644 --- a/modules/taboolaBidAdapter.js +++ b/modules/taboolaBidAdapter.js @@ -107,7 +107,7 @@ export const spec = { } if (config.getConfig('coppa')) { - regs.coppa = 1 + regs.coppa = 1; } const ortb2 = bidderRequest.ortb2 || { diff --git a/modules/userId/index.js b/modules/userId/index.js index f925e637178..14ebe6a57e8 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -843,7 +843,7 @@ function initSubmodules(dest, submodules, consentData, forceRefresh = false) { submodules = submodules.filter((submod) => !submod.config.storage || storageTypes.has(submod.config.storage.type)); if (!submodules.length) { - logWarn(`${MODULE_NAME} - no ID module is configured for one of the available storage types:`, Array.from(storageTypes)) + logWarn(`${MODULE_NAME} - no ID module is configured for one of the available storage types:`, Array.from(storageTypes)); return []; } diff --git a/modules/videoheroesBidAdapter.js b/modules/videoheroesBidAdapter.js index 089f1256937..4818d9e1c58 100644 --- a/modules/videoheroesBidAdapter.js +++ b/modules/videoheroesBidAdapter.js @@ -85,7 +85,7 @@ export const spec = { }; if (bidderRequest.refererInfo.ref) { - data.site.ref = bidderRequest.refererInfo.ref + data.site.ref = bidderRequest.refererInfo.ref; } if (bidderRequest.gdprConsent) { diff --git a/modules/weboramaRtdProvider.js b/modules/weboramaRtdProvider.js index 35440849db4..533e669fcaa 100644 --- a/modules/weboramaRtdProvider.js +++ b/modules/weboramaRtdProvider.js @@ -199,7 +199,7 @@ function initSubSection(moduleParams, subSection, ...requiredFields) { }); } catch (e) { logError(`unable to initialize: error on ${subSection} configuration: ${e}`); - return false + return false; } logMessage(`weborama ${subSection} initialized with success`); @@ -213,7 +213,7 @@ const globalDefaults = { sendToBidders: true, onData: () => { /* do nothing */ }, -} +}; /** normalize submodule configuration * @param {ModuleParams} moduleParams @@ -558,7 +558,7 @@ function getDataFromLocalStorage(weboDataConf, cacheGet, cacheSet, defaultLocalS } } - const profile = cacheGet() + const profile = cacheGet(); if (profile) { return [profile, false]; diff --git a/src/config.js b/src/config.js index 5e89210dfcb..0922a2a2bea 100644 --- a/src/config.js +++ b/src/config.js @@ -456,7 +456,7 @@ export function newConfig() { if (options.init) { if (topic === ALL_TOPICS) { - callback(getConfig()) + callback(getConfig()); } else { // eslint-disable-next-line standard/no-callback-literal callback({[topic]: getConfig(topic)}); diff --git a/src/native.js b/src/native.js index 4ba4505d441..bd20aa5d399 100644 --- a/src/native.js +++ b/src/native.js @@ -351,7 +351,7 @@ export function getNativeTargeting(bid, {index = auctionManager.index} = {}) { value = placeholder; } - let assetSendTargetingKeys = deepAccess(adUnit, `nativeParams.${asset}.sendTargetingKeys`) + let assetSendTargetingKeys = deepAccess(adUnit, `nativeParams.${asset}.sendTargetingKeys`); if (typeof assetSendTargetingKeys !== 'boolean') { assetSendTargetingKeys = deepAccess(adUnit, `nativeParams.ext.${asset}.sendTargetingKeys`); } From 865cb34533696ee5c2109926b86c390e9ef1507b Mon Sep 17 00:00:00 2001 From: Pgb-Criteo <92986445+Pgb-Criteo@users.noreply.github.com> Date: Thu, 11 Aug 2022 02:45:49 +0200 Subject: [PATCH 050/246] Criteo Adapter: Add Price floor support (#8815) --- modules/criteoBidAdapter.js | 41 +++++- test/spec/modules/criteoBidAdapter_spec.js | 145 ++++++++++++++++++++- 2 files changed, 184 insertions(+), 2 deletions(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 82035d1550e..c7f843f421d 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -502,6 +502,9 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { slot.video = video; } + + enrichSlotWithFloors(slot, bidRequest) + return slot; }), }; @@ -537,7 +540,7 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { return request; } -function parseSizes(sizes, parser) { +function parseSizes(sizes, parser = s => s) { if (sizes == undefined) { return []; } @@ -635,6 +638,42 @@ for (var i = 0; i < 10; ++i) { `; } +function enrichSlotWithFloors(slot, bidRequest) { + try { + const slotFloors = {}; + + if (bidRequest.getFloor) { + if (bidRequest.mediaTypes?.banner) { + slotFloors.banner = {}; + const bannerSizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes')) + bannerSizes.forEach(bannerSize => slotFloors.banner[parseSize(bannerSize).toString()] = bidRequest.getFloor({size: bannerSize, mediaType: BANNER})); + } + + if (bidRequest.mediaTypes?.video) { + slotFloors.video = {}; + const videoSizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.video.playerSize')) + videoSizes.forEach(videoSize => slotFloors.video[parseSize(videoSize).toString()] = bidRequest.getFloor({size: videoSize, mediaType: VIDEO})); + } + + if (bidRequest.mediaTypes?.native) { + slotFloors.native = {}; + slotFloors.native['*'] = bidRequest.getFloor({size: '*', mediaType: NATIVE}); + } + + if (Object.keys(slotFloors).length > 0) { + if (!slot.ext) { + slot.ext = {} + } + Object.assign(slot.ext, { + floors: slotFloors + }); + } + } + } catch (e) { + logError('Could not parse floors from Prebid: ' + e); + } +} + export function canFastBid(fastBidVersion) { return fastBidVersion !== FAST_BID_VERSION_NONE; } diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 19e97ea85bb..7252232676f 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -13,7 +13,7 @@ import * as utils from 'src/utils.js'; import * as refererDetection from 'src/refererDetection.js'; import { config } from '../../../src/config.js'; import * as storageManager from 'src/storageManager.js'; -import { NATIVE, VIDEO } from '../../../src/mediaTypes.js'; +import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes.js'; describe('The Criteo bidding adapter', function () { let utilsMock, sandbox; @@ -1238,6 +1238,149 @@ describe('The Criteo bidding adapter', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.regs.coppa).to.be.undefined; }); + + it('should properly build a banner request with floors', function () { + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + mediaTypes: { + banner: { + sizes: [[300, 250], [728, 90]] + } + }, + params: { + networkId: 456, + }, + + getFloor: inputParams => { + if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { + return { + currency: 'USD', + floor: 1.0}; + } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { + return { + currency: 'USD', + floor: 2.0}; + } else { + return {} + } + } + }, + ]; + const bidderRequest = {}; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.slots[0].ext.floors).to.deep.equal({ + 'banner': { + '300x250': {'currency': 'USD', 'floor': 1}, + '728x90': {'currency': 'USD', 'floor': 2} + }}); + }); + + it('should properly build a video request with several player sizes with floors', function () { + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + mediaTypes: { + video: { + playerSize: [[300, 250], [728, 90]] + } + }, + params: { + networkId: 456, + }, + + getFloor: inputParams => { + if (inputParams.mediaType === VIDEO && inputParams.size[0] === 300 && inputParams.size[1] === 250) { + return { + currency: 'USD', + floor: 1.0}; + } else if (inputParams.mediaType === VIDEO && inputParams.size[0] === 728 && inputParams.size[1] === 90) { + return { + currency: 'USD', + floor: 2.0}; + } else { + return {} + } + } + }, + ]; + const bidderRequest = {}; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.slots[0].ext.floors).to.deep.equal({ + 'video': { + '300x250': {'currency': 'USD', 'floor': 1}, + '728x90': {'currency': 'USD', 'floor': 2} + }}); + }); + + it('should properly build a multi format request with floors', function () { + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + mediaTypes: { + banner: { + sizes: [[300, 250], [728, 90]] + }, + video: { + playerSize: [640, 480], + }, + native: {} + }, + params: { + networkId: 456, + }, + ortb2Imp: { + ext: { + data: { + someContextAttribute: 'abc' + } + } + }, + + getFloor: inputParams => { + if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { + return { + currency: 'USD', + floor: 1.0}; + } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { + return { + currency: 'USD', + floor: 2.0}; + } else if (inputParams.mediaType === VIDEO && inputParams.size[0] === 640 && inputParams.size[1] === 480) { + return { + currency: 'EUR', + floor: 3.2}; + } else if (inputParams.mediaType === NATIVE && inputParams.size === '*') { + return { + currency: 'YEN', + floor: 4.99}; + } else { + return {} + } + } + }, + ]; + const bidderRequest = {}; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.slots[0].ext.data.someContextAttribute).to.deep.equal('abc'); + expect(request.data.slots[0].ext.floors).to.deep.equal({ + 'banner': { + '300x250': {'currency': 'USD', 'floor': 1}, + '728x90': {'currency': 'USD', 'floor': 2} + }, + 'video': { + '640x480': {'currency': 'EUR', 'floor': 3.2} + }, + 'native': { + '*': {'currency': 'YEN', 'floor': 4.99} + }}); + }); }); describe('interpretResponse', function () { From 5ef54f7b2234cba7595ea4446f68b821af670bd6 Mon Sep 17 00:00:00 2001 From: caseywhitmire <60086994+caseywhitmire@users.noreply.github.com> Date: Thu, 11 Aug 2022 05:11:28 -0700 Subject: [PATCH 051/246] Various files: fix LGTM trailing semi-colon (#8821) * Update ozoneBidAdapter * Update criteoBidAdapter * Update sspBCBidAdapter * Update pubmaticBidAdapter.js * Update ttdBidAdapter.js * Update tripleliftBidAdapter.js * Update colossussspBidAdapter.js * Update sirdataRtdProvider.js * Update smartxBidAdapter.js * Update mobfoxpbBidAdapter.js * Update 33acrossBidAdapter.js * Update gridNMBidAdapter.js * Update consentManagementUsp.js * Update adapterManager.js * Update adapterManager.js * Update apacdexBidAdapter.js * Update microadBidAdapter.js * Update pubmaticAnalyticsAdapter.js * Update hadronRtdProvider.js * Update adagioBidAdapter.js * Update operaadsBidAdapter.js * Update auction.js * Update appnexusBidAdapter.js * Update utils.js * Update adgenerationBidAdapter.js * Update pulsepointBidAdapter.js * Update consumableBidAdapter.js * Update boldwinBidAdapter.js * Update relaidoBidAdapter.js * Update rubiconBidAdapter.js * Update vrtcalBidAdapter.js * Update oguryBidAdapter.js * Update riseBidAdapter.js * Update gridBidAdapter.js * Update merkleIdSystem.js --- modules/33acrossBidAdapter.js | 6 +++--- modules/adagioBidAdapter.js | 2 +- modules/adgenerationBidAdapter.js | 2 +- modules/apacdexBidAdapter.js | 6 +++--- modules/appnexusBidAdapter.js | 18 +++++++++--------- modules/boldwinBidAdapter.js | 8 ++++---- modules/colossussspBidAdapter.js | 4 ++-- modules/consentManagementUsp.js | 2 +- modules/consumableBidAdapter.js | 2 +- modules/criteoBidAdapter.js | 2 +- modules/gridBidAdapter.js | 2 +- modules/gridNMBidAdapter.js | 2 +- modules/hadronRtdProvider.js | 2 +- modules/merkleIdSystem.js | 4 ++-- modules/microadBidAdapter.js | 2 +- modules/mobfoxpbBidAdapter.js | 4 ++-- modules/oguryBidAdapter.js | 2 +- modules/operaadsBidAdapter.js | 2 +- modules/ozoneBidAdapter.js | 4 ++-- modules/pubmaticAnalyticsAdapter.js | 2 +- modules/pubmaticBidAdapter.js | 8 ++++---- modules/pulsepointBidAdapter.js | 2 +- modules/relaidoBidAdapter.js | 4 ++-- modules/riseBidAdapter.js | 4 ++-- modules/rubiconBidAdapter.js | 4 ++-- modules/sirdataRtdProvider.js | 6 +++--- modules/smartxBidAdapter.js | 4 ++-- modules/sspBCBidAdapter.js | 4 ++-- modules/tripleliftBidAdapter.js | 2 +- modules/ttdBidAdapter.js | 8 ++++---- modules/vrtcalBidAdapter.js | 2 +- src/adapterManager.js | 6 +++--- src/auction.js | 4 ++-- src/utils.js | 2 +- 34 files changed, 69 insertions(+), 69 deletions(-) diff --git a/modules/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js index ad2b092baa8..89c00897571 100644 --- a/modules/33acrossBidAdapter.js +++ b/modules/33acrossBidAdapter.js @@ -322,7 +322,7 @@ function _createServerRequest({ bidRequests, gdprConsent = {}, uspConsent, pageU 'url': url, 'data': JSON.stringify(ttxRequest), 'options': options - } + }; } // BUILD REQUESTS: SET EXTENSIONS @@ -438,7 +438,7 @@ function _buildBannerORTB(bidRequest) { return { format, ext - } + }; } // BUILD REQUESTS: VIDEO @@ -452,7 +452,7 @@ function _buildVideoORTB(bidRequest) { ...videoBidderParams // Bidder Specific overrides }; - const video = {} + const video = {}; const { w, h } = _getSize(videoParams.playerSize[0]); video.w = w; diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index afc15bb2d17..5c07cbe2844 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -1124,7 +1124,7 @@ export const spec = { ...globalFeatures, print_number: getPrintNumber(adagioBid.adUnitCode, adagioBidderRequest).toString(), adunit_position: getSlotPosition(adagioBid.params.adUnitElementId) // adUnitElementId à déplacer ??? - } + }; adagioBid.params.pageviewId = internal.getPageviewId(); adagioBid.params.prebidVersion = '$prebid.version$'; diff --git a/modules/adgenerationBidAdapter.js b/modules/adgenerationBidAdapter.js index c91c4ad1ae0..21395724609 100644 --- a/modules/adgenerationBidAdapter.js +++ b/modules/adgenerationBidAdapter.js @@ -200,7 +200,7 @@ function createNativeAd(body) { native.clickTrackers = body.native_ad.link.clicktrackers || []; native.impressionTrackers = body.native_ad.imptrackers || []; if (body.beaconurl && body.beaconurl != '') { - native.impressionTrackers.push(body.beaconurl) + native.impressionTrackers.push(body.beaconurl); } } return native; diff --git a/modules/apacdexBidAdapter.js b/modules/apacdexBidAdapter.js index a6ab1ea03da..10593855c59 100644 --- a/modules/apacdexBidAdapter.js +++ b/modules/apacdexBidAdapter.js @@ -103,7 +103,7 @@ export const spec = { var pageUrl = _extractTopWindowUrlFromBidderRequest(bidderRequest); payload.site = {}; - payload.site.page = pageUrl + payload.site.page = pageUrl; payload.site.referrer = _extractTopWindowReferrerFromBidderRequest(bidderRequest); // TODO: does it make sense to fall back to window.location for the domain? payload.site.hostname = bidderRequest.refererInfo?.domain || parseDomain(pageUrl); @@ -124,12 +124,12 @@ export const spec = { // Apply schain. if (schain) { - payload.schain = schain + payload.schain = schain; } // Apply eids. if (eids) { - payload.eids = eids + payload.eids = eids; } // Apply geo diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 056326eb824..cf73b69991f 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -215,7 +215,7 @@ export const spec = { payload['iab_support'] = { omidpn: 'Appnexus', omidpv: '$prebid.version$' - } + }; } if (member > 0) { @@ -223,7 +223,7 @@ export const spec = { } if (appDeviceObjBid) { - payload.device = appDeviceObj + payload.device = appDeviceObj; } if (appIdObjBid) { payload.app = appIdObj; @@ -265,7 +265,7 @@ export const spec = { } if (bidderRequest && bidderRequest.uspConsent) { - payload.us_privacy = bidderRequest.uspConsent + payload.us_privacy = bidderRequest.uspConsent; } if (bidderRequest && bidderRequest.refererInfo) { @@ -275,7 +275,7 @@ export const spec = { rd_top: bidderRequest.refererInfo.reachedTop, rd_ifs: bidderRequest.refererInfo.numIframes, rd_stk: bidderRequest.refererInfo.stack.map((url) => encodeURIComponent(url)).join(',') - } + }; let pubPageUrl = bidderRequest.refererInfo.canonicalUrl; if (isStr(pubPageUrl) && pubPageUrl !== '') { refererinfo.rd_can = pubPageUrl; @@ -410,7 +410,7 @@ export const spec = { if (includes(s2sCfg.bidders, adUnit.bids[0].bidder)) { s2sEndpointUrl = deepAccess(s2sCfg, 'endpoint.p1Consent'); } - }) + }); } if (s2sEndpointUrl && s2sEndpointUrl.match('/openrtb2/prebid')) { @@ -455,7 +455,7 @@ export const spec = { reloadViewabilityScriptWithCorrectParameters(bid); } } -} +}; function isPopulatedArray(arr) { return !!(isArray(arr) && arr.length > 0); @@ -473,7 +473,7 @@ function reloadViewabilityScriptWithCorrectParameters(bid) { if (viewJsPayload) { let prebidParams = 'pbjs_adid=' + bid.adId + ';pbjs_auc=' + bid.adUnitCode; - let jsTrackerSrc = getViewabilityScriptUrlFromPayload(viewJsPayload) + let jsTrackerSrc = getViewabilityScriptUrlFromPayload(viewJsPayload); let newJsTrackerSrc = jsTrackerSrc.replace('dom_id=%native_dom_id%', prebidParams); @@ -567,7 +567,7 @@ function formatRequest(payload, bidderRequest) { if (getParameterByName('apn_test').toUpperCase() === 'TRUE' || config.getConfig('apn_test') === true) { options.customHeaders = { 'X-Is-Test': 1 - } + }; } if (payload.tags.length > MAX_IMPS_PER_REQUEST) { @@ -805,7 +805,7 @@ function bidToTag(bid) { tag.code = bid.params.invCode; } tag.allow_smaller_sizes = bid.params.allowSmallerSizes || false; - tag.use_pmt_rule = bid.params.usePaymentRule || false + tag.use_pmt_rule = bid.params.usePaymentRule || false; tag.prebid = true; tag.disable_psa = true; let bidFloor = getBidFloor(bid); diff --git a/modules/boldwinBidAdapter.js b/modules/boldwinBidAdapter.js index ec9aeb2b888..3915df8b976 100644 --- a/modules/boldwinBidAdapter.js +++ b/modules/boldwinBidAdapter.js @@ -57,7 +57,7 @@ export const spec = { let location; // TODO: this odd try-catch block was copied in several adapters; it doesn't seem to be correct for cross-origin try { - location = new URL(bidderRequest.refererInfo.page) + location = new URL(bidderRequest.refererInfo.page); winTop = window.top; } catch (e) { location = winTop.location; @@ -78,7 +78,7 @@ export const spec = { request.ccpa = bidderRequest.uspConsent; } if (bidderRequest.gdprConsent) { - request.gdpr = bidderRequest.gdprConsent + request.gdpr = bidderRequest.gdprConsent; } } const len = validBidRequests.length; @@ -91,10 +91,10 @@ export const spec = { if (mediaTypes) { if (mediaTypes[BANNER] && mediaTypes[BANNER].sizes) { placement.adFormat = BANNER; - sizes = mediaTypes[BANNER].sizes + sizes = mediaTypes[BANNER].sizes; } else if (mediaTypes[VIDEO] && mediaTypes[VIDEO].playerSize) { placement.adFormat = VIDEO; - sizes = mediaTypes[VIDEO].playerSize + sizes = mediaTypes[VIDEO].playerSize; placement.minduration = mediaTypes[VIDEO].minduration; placement.maxduration = mediaTypes[VIDEO].maxduration; placement.mimes = mediaTypes[VIDEO].mimes; diff --git a/modules/colossussspBidAdapter.js b/modules/colossussspBidAdapter.js index 4594b8c3ce6..9a0ff3a5422 100644 --- a/modules/colossussspBidAdapter.js +++ b/modules/colossussspBidAdapter.js @@ -105,8 +105,8 @@ export const spec = { request.ccpa = bidderRequest.uspConsent; } if (bidderRequest.gdprConsent) { - request.gdpr_consent = bidderRequest.gdprConsent.consentString || 'ALL' - request.gdpr_require = bidderRequest.gdprConsent.gdprApplies ? 1 : 0 + request.gdpr_consent = bidderRequest.gdprConsent.consentString || 'ALL'; + request.gdpr_require = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; } } diff --git a/modules/consentManagementUsp.js b/modules/consentManagementUsp.js index cde32a2f1c0..2b18e59032c 100644 --- a/modules/consentManagementUsp.js +++ b/modules/consentManagementUsp.js @@ -304,4 +304,4 @@ function enableConsentManagement(configFromUser = false) { loadConsentData(); // immediately look up consent data to make it available without requiring an auction } config.getConfig('consentManagement', config => setConsentConfig(config.consentManagement)); -setTimeout(() => !addedConsentHook && enableConsentManagement()) +setTimeout(() => !addedConsentHook && enableConsentManagement()); diff --git a/modules/consumableBidAdapter.js b/modules/consumableBidAdapter.js index 9a5f5f4675b..f6c470d82a9 100644 --- a/modules/consumableBidAdapter.js +++ b/modules/consumableBidAdapter.js @@ -4,7 +4,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'consumable'; -const BASE_URI = 'https://e.serverbid.com/api/v2' +const BASE_URI = 'https://e.serverbid.com/api/v2'; let siteId = 0; let bidder = 'consumable'; diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index c7f843f421d..1c269b2bba4 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -503,7 +503,7 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { slot.video = video; } - enrichSlotWithFloors(slot, bidRequest) + enrichSlotWithFloors(slot, bidRequest); return slot; }), diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 40b79784595..d1743ac63af 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -282,7 +282,7 @@ export const spec = { ext: { gdpr: gdprConsent.gdprApplies ? 1 : 0 } - } + }; } if (uspConsent) { diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index b44f94f0495..bfe5f3952bc 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -208,7 +208,7 @@ export const spec = { ext: { gdpr: gdprConsent.gdprApplies ? 1 : 0 } - } + }; } if (uspConsent) { diff --git a/modules/hadronRtdProvider.js b/modules/hadronRtdProvider.js index a6aa0197d55..0bd4e6f8344 100644 --- a/modules/hadronRtdProvider.js +++ b/modules/hadronRtdProvider.js @@ -30,7 +30,7 @@ export const storage = getStorageManager({gvlid: AU_GVLID, moduleName: SUBMODULE */ const urlAddParams = (url, params) => { return url + (url.indexOf('?') > -1 ? '&' : '?') + params -} +}; /** * Deep set an object unless value present. diff --git a/modules/merkleIdSystem.js b/modules/merkleIdSystem.js index 19602c27093..6a10c2a94eb 100644 --- a/modules/merkleIdSystem.js +++ b/modules/merkleIdSystem.js @@ -159,7 +159,7 @@ export const merkleIdSubmodule = { if (typeof configParams.endpoint !== 'string') { logWarn('User ID - merkleId submodule endpoint string is not defined'); - configParams.endpoint = ID_URL + configParams.endpoint = ID_URL; } if (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) { @@ -188,7 +188,7 @@ export const merkleIdSubmodule = { refreshNeeded = storedDate && (Date.now() - storedDate.getTime() > refreshInSeconds * 1000); if (refreshNeeded) { logInfo('User ID - merkleId needs refreshing id'); - const resp = generateId(configParams, configStorage) + const resp = generateId(configParams, configStorage); return {callback: resp}; } } diff --git a/modules/microadBidAdapter.js b/modules/microadBidAdapter.js index b2eaf96f414..1b31000df43 100644 --- a/modules/microadBidAdapter.js +++ b/modules/microadBidAdapter.js @@ -33,7 +33,7 @@ const AUDIENCE_IDS = [ {type: 13, bidKey: 'userId.idl_env'}, {type: 14, bidKey: 'userId.criteoId'}, {type: 15, bidKey: 'userId.pubcid'} -] +]; function createCBT() { const randomValue = Math.floor(Math.random() * Math.pow(10, 18)).toString(16); diff --git a/modules/mobfoxpbBidAdapter.js b/modules/mobfoxpbBidAdapter.js index df900318881..9ff50e2531f 100644 --- a/modules/mobfoxpbBidAdapter.js +++ b/modules/mobfoxpbBidAdapter.js @@ -69,7 +69,7 @@ export const spec = { request.ccpa = bidderRequest.uspConsent; } if (bidderRequest.gdprConsent) { - request.gdpr = bidderRequest.gdprConsent + request.gdpr = bidderRequest.gdprConsent; } } @@ -82,7 +82,7 @@ export const spec = { schain: bid.schain || {}, bidfloor: getBidFloor(bid) }; - const mediaType = bid.mediaTypes + const mediaType = bid.mediaTypes; if (mediaType && mediaType[BANNER] && mediaType[BANNER].sizes) { placement.traffic = BANNER; diff --git a/modules/oguryBidAdapter.js b/modules/oguryBidAdapter.js index e0630c7e412..f0ed3c24a31 100644 --- a/modules/oguryBidAdapter.js +++ b/modules/oguryBidAdapter.js @@ -209,6 +209,6 @@ export const spec = { onBidWon, getWindowContext, onTimeout -} +}; registerBidder(spec); diff --git a/modules/operaadsBidAdapter.js b/modules/operaadsBidAdapter.js index b75090cdec6..aa548debf32 100644 --- a/modules/operaadsBidAdapter.js +++ b/modules/operaadsBidAdapter.js @@ -549,7 +549,7 @@ function createImp(bidRequest) { const floorDetail = getBidFloor(bidRequest, { mediaType: mediaType || '*', size: size || '*' - }) + }); impItem.bidfloor = floorDetail.floor; impItem.bidfloorcur = floorDetail.currency; diff --git a/modules/ozoneBidAdapter.js b/modules/ozoneBidAdapter.js index 2f229720208..3e59c2c8def 100644 --- a/modules/ozoneBidAdapter.js +++ b/modules/ozoneBidAdapter.js @@ -16,7 +16,7 @@ import {getPriceBucketString} from '../src/cpmBucketManager.js'; import { Renderer } from '../src/Renderer.js'; import {getRefererInfo} from '../src/refererDetection.js'; const BIDDER_CODE = 'ozone'; -const ORIGIN = 'https://elb.the-ozone-project.com' // applies only to auction & cookie +const ORIGIN = 'https://elb.the-ozone-project.com'; // applies only to auction & cookie const AUCTIONURI = '/openrtb2/auction'; const OZONECOOKIESYNC = '/static/load-cookie.html'; const OZONE_RENDERER_URL = 'https://prebid.the-ozone-project.com/ozone-renderer.js'; @@ -98,7 +98,7 @@ export const spec = { this.loadWhitelabelData(bid); logInfo('isBidRequestValid : ', config.getConfig(), bid); let adUnitCode = bid.adUnitCode; // adunit[n].code - let err1 = 'VALIDATION FAILED : missing {param} : siteId, placementId and publisherId are REQUIRED' + let err1 = 'VALIDATION FAILED : missing {param} : siteId, placementId and publisherId are REQUIRED'; if (!(bid.params.hasOwnProperty('placementId'))) { logError(err1.replace('{param}', 'placementId'), adUnitCode); return false; diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index 48b570a683b..38bcc4521be 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -384,7 +384,7 @@ function bidResponseHandler(args) { if (bid.bidder && args.bidderCode && bid.bidder !== args.bidderCode) { bid = copyRequiredBidDetails(args); - cache.auctions[args.auctionId].adUnitCodes[args.adUnitCode].bids[args.requestId].push(bid) + cache.auctions[args.auctionId].adUnitCodes[args.adUnitCode].bids[args.requestId].push(bid); } bid.adId = args.adId; bid.source = formatSource(bid.source || args.source); diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index bdc8010618c..922fedd5092 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -608,7 +608,7 @@ function _addDealCustomTargetings(imp, bid) { if (dctr.substring(dctrLen, dctrLen - 1) === '|') { dctr = dctr.substring(0, dctrLen - 1); } - imp.ext['key_val'] = dctr.trim() + imp.ext['key_val'] = dctr.trim(); } else { logWarn(LOG_WARN_PREFIX + 'Ignoring param : dctr with value : ' + dctr + ', expects string-value, found empty or non-string value'); } @@ -811,7 +811,7 @@ function _checkMediaType(bid, newBid) { if (bid.ext && bid.ext['bidtype'] != undefined) { newBid.mediaType = MEDIATYPE[bid.ext.bidtype]; } else { - logInfo(LOG_WARN_PREFIX + 'bid.ext.bidtype does not exist, checking alternatively for mediaType') + logInfo(LOG_WARN_PREFIX + 'bid.ext.bidtype does not exist, checking alternatively for mediaType'); var adm = bid.adm; var admStr = ''; var videoRegex = new RegExp(/VAST\s+version/); @@ -1097,7 +1097,7 @@ export const spec = { if (bidderRequest && allowAlternateBidder == true) { let allowedBiddersList = bidderSettings.get(bidderRequest.bidderCode, 'allowedAlternateBidderCodes'); if (isArray(allowedBiddersList)) { - allowedBiddersList = allowedBiddersList.map(val => val.trim().toLowerCase()).filter(val => !!val).filter(uniques) + allowedBiddersList = allowedBiddersList.map(val => val.trim().toLowerCase()).filter(val => !!val).filter(uniques); biddersList = allowedBiddersList.includes('*') ? allBiddersList : [...biddersList, ...allowedBiddersList]; } else { biddersList = allBiddersList; @@ -1168,7 +1168,7 @@ export const spec = { mergeDeep(payload, {user: commonFpd.user}); } if (commonFpd.bcat) { - blockedIabCategories = blockedIabCategories.concat(commonFpd.bcat) + blockedIabCategories = blockedIabCategories.concat(commonFpd.bcat); } if (commonFpd.ext?.prebid?.bidderparams?.[bidderRequest.bidderCode]?.acat) { const acatParams = commonFpd.ext.prebid.bidderparams[bidderRequest.bidderCode].acat; diff --git a/modules/pulsepointBidAdapter.js b/modules/pulsepointBidAdapter.js index 7b66bb7600f..52931be0078 100644 --- a/modules/pulsepointBidAdapter.js +++ b/modules/pulsepointBidAdapter.js @@ -96,7 +96,7 @@ function bidResponseAvailable(request, response) { const idToImpMap = {}; const idToBidMap = {}; const idToSlotConfig = {}; - const bidResponse = response.body + const bidResponse = response.body; // extract the request bids and the response bids, keyed by impr-id const ortbRequest = request.data; ortbRequest.imp.forEach(imp => { diff --git a/modules/relaidoBidAdapter.js b/modules/relaidoBidAdapter.js index c9e9fc00eb7..2e6097dc100 100644 --- a/modules/relaidoBidAdapter.js +++ b/modules/relaidoBidAdapter.js @@ -71,11 +71,11 @@ function buildRequests(validBidRequests, bidderRequest) { } if (!bidder) { - bidder = bidRequest.bidder + bidder = bidRequest.bidder; } if (!bidder) { - bidder = bidRequest.bidder + bidder = bidRequest.bidder; } if (!count) { diff --git a/modules/riseBidAdapter.js b/modules/riseBidAdapter.js index 504de2e209a..7d4049bf1a2 100644 --- a/modules/riseBidAdapter.js +++ b/modules/riseBidAdapter.js @@ -384,7 +384,7 @@ function generateGeneralParams(generalObject, bidderRequest) { ua: navigator.userAgent, session_id: getBidIdParameter('auctionId', generalObject), tmax: timeout - } + }; const userIdsParam = getBidIdParameter('userId', generalObject); if (userIdsParam) { @@ -430,5 +430,5 @@ function generateGeneralParams(generalObject, bidderRequest) { generalParams.page_url = deepAccess(bidderRequest, 'refererInfo.page') || deepAccess(window, 'location.href'); } - return generalParams + return generalParams; } diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 192d5406b86..4817d62348a 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -939,7 +939,7 @@ function parseSizes(bid, mediaType) { } else if (typeof deepAccess(bid, 'mediaTypes.banner.sizes') !== 'undefined') { sizes = mapSizes(bid.mediaTypes.banner.sizes); } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0) { - sizes = mapSizes(bid.sizes) + sizes = mapSizes(bid.sizes); } else { logWarn('Rubicon: no sizes are setup or found'); } @@ -1045,7 +1045,7 @@ function applyFPD(bidRequest, mediaType, data) { let val = validate(obj, key, name); let loc = (MAP[key] && isParent) ? `${MAP[key]}` : (key === 'data') ? `${MAP[name]}iab` : `${MAP[name]}${key}`; data[loc] = (data[loc]) ? data[loc].concat(',', val) : val; - } + }; if (mediaType === BANNER) { ['site', 'user'].forEach(name => { diff --git a/modules/sirdataRtdProvider.js b/modules/sirdataRtdProvider.js index 532d938375e..369276e7638 100644 --- a/modules/sirdataRtdProvider.js +++ b/modules/sirdataRtdProvider.js @@ -200,7 +200,7 @@ export function addSegmentData(reqBids, data, moduleConfig, onDone) { if (typeof n.setTargeting !== 'undefined' && sirdataMergedList && sirdataMergedList.length > 0) { n.setTargeting('sd_rtd', sirdataMergedList); } - }) + }); } catch (e) { logError(e); } @@ -224,7 +224,7 @@ export function addSegmentData(reqBids, data, moduleConfig, onDone) { curationData = {'segments': [], 'categories': []}; sirdataMergedList = []; - let minScore = (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('contextualMinRelevancyScore') ? moduleConfig.params.bidders[bidderIndex].contextualMinRelevancyScore : globalMinScore) + let minScore = (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('contextualMinRelevancyScore') ? moduleConfig.params.bidders[bidderIndex].contextualMinRelevancyScore : globalMinScore); if (!biddersParamsExist || (indexFound && (!moduleConfig.params.bidders[bidderIndex].hasOwnProperty('adUnitCodes') || moduleConfig.params.bidders[bidderIndex].adUnitCodes.indexOf(adUnit.code) !== -1))) { switch (bid.bidder) { @@ -476,7 +476,7 @@ export function addSegmentData(reqBids, data, moduleConfig, onDone) { } } } catch (e) { - logError(e) + logError(e); } }) }); diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index f3188c1f110..e5f71265e34 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -221,7 +221,7 @@ export const spec = { name: provider, value: targetingstring, } - }) + }); } } @@ -230,7 +230,7 @@ export const spec = { requestPayload.user = { ext: userExt, data: targetingarr - } + }; } return { diff --git a/modules/sspBCBidAdapter.js b/modules/sspBCBidAdapter.js index 0a19410c78f..ffe2bef054c 100644 --- a/modules/sspBCBidAdapter.js +++ b/modules/sspBCBidAdapter.js @@ -338,7 +338,7 @@ const mapImpression = slot => { if (!adUnitsCalled[adUnitCode]) { // this is a new adunit - assign & save pbsize adSizesCalled[slotSize] = adSizesCalled[slotSize] ? adSizesCalled[slotSize] += 1 : 1; - adUnitsCalled[adUnitCode] = `${slotSize}_${adSizesCalled[slotSize]}` + adUnitsCalled[adUnitCode] = `${slotSize}_${adSizesCalled[slotSize]}`; } ext.data = Object.assign({ pbsize: adUnitsCalled[adUnitCode] }, ext.data); @@ -682,7 +682,7 @@ const spec = { site: site.id, slot: site.slot, cpm: bid.cpm.toPrecision(4), - } + }; const jsTracker = ''; // banner + break; + default: ''; break; + }; + + return ADM_PAYLOAD; +}; + +const generateResponseMock = (admPayloadType) => { + const bidResponse = { + id: 'fc0c35df-21fb-4f93-9ebd-88759dbe31f9', + impid: '274395c06a24e5', + adm: generateAdmPayload(admPayloadType), + price: 1, + w: 300, + h: 250, + crid: 'ssp-placement-name', + adomain: ['advertiser-domain.com'] + }; + + const serverResponse = { + body: { + id: 'fc0c35df-21fb-4f93-9ebd-88759dbe31f9', + seatbid: [{ bid: [ bidResponse ], seat: 13107 }] + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: admPayloadType}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + + return {serverResponse, data, bidderRequest}; +} + +// Unit tests +describe('adtrgtme Bid Adapter:', () => { + it('PLACEHOLDER TO PASS GULP', () => { + const obj = {}; + expect(obj).to.be.an('object'); + }); + + describe('Validate basic properties', () => { + it('should define the correct bidder code', () => { + expect(spec.code).to.equal('adtrgtme') + }); + }); + + describe('getUserSyncs()', () => { + const IMAGE_PIXEL_URL = 'http://image-pixel.com/foo/bar?1234&baz=true'; + const IFRAME_ONE_URL = 'http://image-iframe.com/foo/bar?1234&baz=true'; + const IFRAME_TWO_URL = 'http://image-iframe-two.com/foo/bar?1234&baz=true'; + + let serverResponses = []; + beforeEach(() => { + serverResponses[0] = { + body: { + ext: { + pixels: `` + } + } + } + }); + + after(() => { + serverResponses = undefined; + }); + + it('for only iframe enabled syncs', () => { + let syncOptions = { + iframeEnabled: true, + pixelEnabled: false + }; + let pixelsObjects = spec.getUserSyncs(syncOptions, serverResponses); + expect(pixelsObjects.length).to.equal(2); + expect(pixelsObjects).to.deep.equal( + [ + {type: 'iframe', 'url': IFRAME_ONE_URL}, + {type: 'iframe', 'url': IFRAME_TWO_URL} + ] + ) + }); + + it('for only pixel enabled syncs', () => { + let syncOptions = { + iframeEnabled: false, + pixelEnabled: true + }; + let pixelsObjects = spec.getUserSyncs(syncOptions, serverResponses); + expect(pixelsObjects.length).to.equal(1); + expect(pixelsObjects).to.deep.equal( + [ + {type: 'image', 'url': IMAGE_PIXEL_URL} + ] + ) + }); + + it('for both pixel and iframe enabled syncs', () => { + let syncOptions = { + iframeEnabled: true, + pixelEnabled: true + }; + let pixelsObjects = spec.getUserSyncs(syncOptions, serverResponses); + expect(pixelsObjects.length).to.equal(3); + expect(pixelsObjects).to.deep.equal( + [ + {type: 'iframe', 'url': IFRAME_ONE_URL}, + {type: 'image', 'url': IMAGE_PIXEL_URL}, + {type: 'iframe', 'url': IFRAME_TWO_URL} + ] + ) + }); + }); + + // Validate Bid Requests + describe('isBidRequestValid()', () => { + const INVALID_INPUT = [ + {}, + {params: {}}, + {params: {sid: '1234', zid: '4321'}}, + {params: {sid: '1220291391', zid: 4321}}, + {params: {zid: ''}}, + {params: {sid: '', zid: ''}}, + ]; + + INVALID_INPUT.forEach(input => { + it(`should determine that the bid is INVALID for the input ${JSON.stringify(input)}`, () => { + expect(spec.isBidRequestValid(input)).to.be.false; + }); + }); + + it('should determine that the bid is VALID if sid and zid are present on the params object', () => { + const validBid = { + params: { + sid: 1220291391, + zid: 1836455615 + } + }; + expect(spec.isBidRequestValid(validBid)).to.be.true; + }); + }); + + describe('Price Floor module support:', () => { + it('should get bidfloor from getFloor method', () => { + const { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); + bidRequest.params.bidOverride = {cur: 'EUR'}; + bidRequest.getFloor = (floorObj) => { + return { + floor: bidRequest.floors.values[floorObj.mediaType + '|300x250'], + currency: floorObj.currency, + mediaType: floorObj.mediaType + } + }; + bidRequest.floors = { + currency: 'EUR', + values: { + 'banner|300x250': 5.55 + } + }; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.cur).to.deep.equal(['EUR']); + expect(data.imp[0].bidfloor).is.a('number'); + expect(data.imp[0].bidfloor).to.equal(5.55); + }); + }); + + describe('Schain module support:', () => { + it('should send Global or Bidder specific schain', function () { + const { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const globalSchain = { + ver: '1.0', + complete: 1, + nodes: [{ + asi: 'some-platform.com', + sid: '111111', + rid: bidRequest.bidId, + hp: 1 + }] + }; + bidRequest.schain = globalSchain; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const schain = data.source.ext.schain; + expect(schain.nodes.length).to.equal(1); + expect(schain).to.equal(globalSchain); + }); + }); + + describe('First party data module - "Site" support (ortb2):', () => { + // Should not allow invalid "site" data types + const INVALID_ORTB2_TYPES = [ null, [], 123, 'unsupportedKeyName', true, false, undefined ]; + INVALID_ORTB2_TYPES.forEach(param => { + it(`should not allow invalid site types to be added to bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { site: param } + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site[param]).to.be.undefined; + }); + }); + + // Should add valid "site" params + const VALID_SITE_STRINGS = ['name', 'domain', 'page', 'ref', 'keywords']; + const VALID_SITE_ARRAYS = ['cat', 'sectioncat', 'pagecat']; + + VALID_SITE_STRINGS.forEach(param => { + it(`should allow supported site keys to be added bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + site: { + [param]: 'something' + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site[param]).to.exist; + expect(data.site[param]).to.be.a('string'); + expect(data.site[param]).to.be.equal(ortb2.site[param]); + }); + }); + + VALID_SITE_ARRAYS.forEach(param => { + it(`should determine that the ortb2.site Array key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + site: { + [param]: ['something'] + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site[param]).to.exist; + expect(data.site[param]).to.be.a('array'); + expect(data.site[param]).to.be.equal(ortb2.site[param]); + }); + }); + + // Should not allow invalid "site.content" data types + INVALID_ORTB2_TYPES.forEach(param => { + it(`should determine that the ortb2.site.content key is invalid and should not be added to bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + site: { + content: param + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site.content).to.be.undefined; + }); + }); + + // Should not allow invalid "site.content" keys + it(`should not allow invalid ortb2.site.content object keys to be added to bid-request: {custom object}`, () => { + const ortb2 = { + site: { + content: { + fake: 'news', + unreal: 'param', + counterfit: 'data' + } + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site.content).to.be.a('object'); + }); + + // Should append valid "site.content" keys + const VALID_CONTENT_STRINGS = ['id', 'title', 'language']; + VALID_CONTENT_STRINGS.forEach(param => { + it(`should determine that the ortb2.site String key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + site: { + content: { + [param]: 'something' + } + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site.content[param]).to.exist; + expect(data.site.content[param]).to.be.a('string'); + expect(data.site.content[param]).to.be.equal(ortb2.site.content[param]); + }); + }); + + const VALID_CONTENT_ARRAYS = ['cat']; + VALID_CONTENT_ARRAYS.forEach(param => { + it(`should determine that the ortb2.site Array key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + site: { + content: { + [param]: ['something', 'something-else'] + } + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site.content[param]).to.be.a('array'); + expect(data.site.content[param]).to.be.equal(ortb2.site.content[param]); + }); + }); + }); + + describe('First party data module - "User" support (ortb2):', () => { + // Global ortb2.user validations + // Should not allow invalid "user" data types + const INVALID_ORTB2_TYPES = [ null, [], 'unsupportedKeyName', true, false, undefined ]; + INVALID_ORTB2_TYPES.forEach(param => { + it(`should not allow invalid site types to be added to bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { user: param } + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.user[param]).to.be.undefined; + }); + }); + + // Should add valid "user" params + const VALID_USER_STRINGS = ['id', 'buyeruid', 'gender', 'keywords', 'customdata']; + VALID_USER_STRINGS.forEach(param => { + it(`should allow supported user string keys to be added bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + user: { + [param]: 'something' + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.user[param]).to.exist; + expect(data.user[param]).to.be.a('string'); + expect(data.user[param]).to.be.equal(ortb2.user[param]); + }); + }); + + const VALID_USER_OBJECTS = ['ext']; + VALID_USER_OBJECTS.forEach(param => { + it(`should allow supported user extObject keys to be added to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + user: { + [param]: {a: '123', b: '456'} + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.user[param]).to.exist; + expect(data.user[param]).to.be.a('object'); + expect(data.user[param]).to.be.deep.include({[param]: {a: '123', b: '456'}}); + config.setConfig({ortb2: {}}); + }); + }); + + // adUnit.ortb2Imp.ext.data + it(`should allow adUnit.ortb2Imp.ext.data object to be added to the bid-request`, () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({}) + validBidRequests[0].ortb2Imp = { + ext: { + data: { + pbadslot: 'homepage-top-rect', + adUnitSpecificAttribute: '123' + } + } + }; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].ext.data).to.deep.equal(validBidRequests[0].ortb2Imp.ext.data); + }); + // adUnit.ortb2Imp.instl + it(`should allow adUnit.ortb2Imp.instl numeric boolean "1" to be added to the bid-request`, () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({}) + validBidRequests[0].ortb2Imp = { + instl: 1 + }; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].instl).to.deep.equal(validBidRequests[0].ortb2Imp.instl); + }); + + it(`should prevent adUnit.ortb2Imp.instl boolean "true" to be added to the bid-request`, () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({}) + validBidRequests[0].ortb2Imp = { + instl: true + }; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].instl).to.not.exist; + }); + + it(`should prevent adUnit.ortb2Imp.instl boolean "false" to be added to the bid-request`, () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({}) + validBidRequests[0].ortb2Imp = { + instl: false + }; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].instl).to.not.exist; + }); + }); + + describe('GDPR & Consent:', () => { + it('should return request objects that do not send cookies if purpose 1 consent is not provided', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + bidderRequest.gdprConsent = { + consentString: 'BOtmiBKOtmiBKABABAENAFAAAAACeAAA', + apiVersion: 2, + vendorData: { + purpose: { + consents: { + '1': false + } + } + }, + gdprApplies: true + }; + const options = spec.buildRequests(validBidRequests, bidderRequest)[0].options; + expect(options.withCredentials).to.be.false; + }); + }); + + describe('Endpoint & Impression Request Mode:', () => { + it('should route request to config override endpoint', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const sid = validBidRequests[0].params.sid; + const testOverrideEndpoint = 'http://new_bidder_host.com/ssp?s='; + config.setConfig({ + adtrgtme: { + endpoint: testOverrideEndpoint + } + }); + const response = spec.buildRequests(validBidRequests, bidderRequest)[0]; + expect(response).to.deep.include( + { + method: 'POST', + url: testOverrideEndpoint + sid + }); + }); + + it('should route request to endpoint + sid', () => { + config.setConfig({ + adtrgtme: {} + }); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const sid = validBidRequests[0].params.sid; + const response = spec.buildRequests(validBidRequests, bidderRequest); + expect(response[0]).to.deep.include({ + method: 'POST', + url: 'https://z.cdn.adtarget.market/ssp?prebid&s=' + sid + }); + }); + + it('should return a single request object for single request mode', () => { + let { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const BID_ID_2 = '84ab50xxxxx'; + const BID_ZID_2 = 98876543210; + const AD_UNIT_CODE_2 = 'test-ad-unit-code-123'; + const { bidRequest: bidRequest2 } = generateBuildRequestMock({bidId: BID_ID_2, zid: BID_ZID_2, adUnitCode: AD_UNIT_CODE_2}); + validBidRequests = [bidRequest, bidRequest2]; + bidderRequest.bids = validBidRequests; + + config.setConfig({ + adtrgtme: { + singleRequestMode: true + } + }); + + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + + expect(data.imp).to.be.an('array').with.lengthOf(2); + + expect(data.imp[0]).to.deep.include({ + id: DEFAULT_BID_ID, + ext: { + dfp_ad_unit_code: DEFAULT_AD_UNIT_CODE + } + }); + + expect(data.imp[1]).to.deep.include({ + id: BID_ID_2, + tagid: BID_ZID_2, + ext: { + dfp_ad_unit_code: AD_UNIT_CODE_2 + } + }); + }); + }); + + describe('Validate request filtering:', () => { + it('should not return request when no bids are present', function () { + let request = spec.buildRequests([]); + expect(request).to.be.undefined; + }); + + it('buildRequests(): should return an array with the correct amount of request objects', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const response = spec.buildRequests(validBidRequests, bidderRequest).bidderRequest; + expect(response.bids).to.be.an('array').to.have.lengthOf(1); + }); + }); + + describe('Request Headers validation:', () => { + it('should return request objects with the relevant custom headers and content type declaration', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + bidderRequest.gdprConsent.gdprApplies = false; + const options = spec.buildRequests(validBidRequests, bidderRequest).options; + expect(options).to.deep.equal( + { + contentType: 'application/json', + customHeaders: { + 'x-openrtb-version': '2.5' + }, + withCredentials: true + }); + }); + }); + + describe('Request Payload oRTB bid validation:', () => { + it('should generate a valid openRTB bid-request object in the data field', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + expect(data.site).to.deep.include({ + id: bidderRequest.bids[0].params.sid, + page: bidderRequest.refererInfo.page + }); + + expect(data.device).to.deep.equal({ + dnt: 0, + ua: navigator.userAgent, + ip: undefined + }); + + expect(data.regs).to.deep.equal({ + ext: { + 'us_privacy': '', + gdpr: 1 + } + }); + + expect(data.source).to.deep.equal({ + ext: { + hb: 1, + adapterver: ADAPTER_VERSION, + prebidver: PREBID_VERSION, + integration: { + name: INTEGRATION_METHOD, + ver: PREBID_VERSION + } + }, + fd: 1 + }); + + expect(data.cur).to.deep.equal(['USD']); + }); + + it('should generate a valid openRTB imp.ext object in the bid-request', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const bid = validBidRequests[0]; + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + expect(data.imp[0].ext).to.deep.equal({ + dfp_ad_unit_code: DEFAULT_AD_UNIT_CODE + }); + }); + + it('should use siteId value as site.id in the outbound bid-request when using "pubId" integration mode', () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({pubIdMode: true}); + validBidRequests[0].params.sid = 9876543210; + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + expect(data.site.id).to.equal(9876543210); + }); + + it('should use placementId value as imp.tagid in the outbound bid-request when using "zid"', () => { + let { validBidRequests, bidderRequest } = generateBuildRequestMock({}), + TEST_ZID = 54321; + validBidRequests[0].params.zid = TEST_ZID; + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + expect(data.imp[0].tagid).to.deep.equal(TEST_ZID); + }); + }); + + describe('Request Payload oRTB bid.imp validation:', () => { + // Validate Banner imp imp when adtrgtme.mode=undefined + it('should generate a valid "Banner" imp object', () => { + config.setConfig({ + adtrgtme: {} + }); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].banner).to.deep.equal({ + mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], + format: [{w: 300, h: 250}] + }); + }); + + // Validate Banner imp + it('should generate a valid "Banner" imp object', () => { + config.setConfig({ + adtrgtme: { mode: 'banner' } + }); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].banner).to.deep.equal({ + mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], + format: [{w: 300, h: 250}] + }); + }); + }); + + describe('interpretResponse()', () => { + describe('for mediaTypes: "banner"', () => { + it('should insert banner payload into response[0].ad', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ad).to.equal(''); + expect(response[0].mediaType).to.equal('banner'); + }) + }); + + describe('Support Advertiser domains', () => { + it('should append bid-response adomain to meta.advertiserDomains', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].meta.advertiserDomains).to.be.a('array'); + expect(response[0].meta.advertiserDomains[0]).to.equal('advertiser-domain.com'); + }) + }); + + describe('bid response Ad ID / Creative ID', () => { + it('should use adId if it exists in the bid-response', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const adId = 'bid-response-adId'; + serverResponse.body.seatbid[0].bid[0].adId = adId; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].adId).to.equal(adId); + }); + + it('should use impid if adId does not exist in the bid-response', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const impid = '25b6c429c1f52f'; + serverResponse.body.seatbid[0].bid[0].impid = impid; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].adId).to.equal(impid); + }); + + it('should use crid if adId & impid do not exist in the bid-response', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const crid = 'passback-12579'; + serverResponse.body.seatbid[0].bid[0].impid = undefined; + serverResponse.body.seatbid[0].bid[0].crid = crid; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].adId).to.equal(crid); + }); + }); + + describe('Time To Live (ttl)', () => { + const UNSUPPORTED_TTL_FORMATS = ['string', [1, 2, 3], true, false, null, undefined]; + UNSUPPORTED_TTL_FORMATS.forEach(param => { + it('should not allow unsupported global adtrgtme.ttl formats and default to 300', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + config.setConfig({ + adtrgtme: { ttl: param } + }); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); + }); + + it('should not allow unsupported params.ttl formats and default to 300', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + bidderRequest.bids[0].params.ttl = param; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); + }); + }); + + const UNSUPPORTED_TTL_VALUES = [-1, 3601]; + UNSUPPORTED_TTL_VALUES.forEach(param => { + it('should not allow invalid global adtrgtme.ttl values 3600 < ttl < 0 and default to 300', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + config.setConfig({ + adtrgtme: { ttl: param } + }); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); + }); + + it('should not allow invalid params.ttl values 3600 < ttl < 0 and default to 300', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + bidderRequest.bids[0].params.ttl = param; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); + }); + }); + + it('should give presedence to Gloabl ttl over params.ttl ', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + config.setConfig({ + adtrgtme: { ttl: 500 } + }); + bidderRequest.bids[0].params.ttl = 400; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(500); + }); + }); + + describe('Aliasing support', () => { + it('should return undefined as the bidder code value', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].bidderCode).to.be.undefined; + }); + }); + }); +}); From 8a47629bbe1bd304fcb0739090fc77d95af0961b Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Thu, 11 Aug 2022 09:13:26 -0700 Subject: [PATCH 058/246] update pre version (#8823) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f3bbc12d2d5..19748040d79 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.10.0-pre", + "version": "7.11.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 941f0daffa39f71615433020239a05a3ad2c6302 Mon Sep 17 00:00:00 2001 From: Olivier Date: Thu, 11 Aug 2022 19:29:04 +0200 Subject: [PATCH 059/246] AdagioBidAdapter: use `refererInfo.topmostLocation` only (#8824) --- modules/adagioBidAdapter.js | 5 ++--- test/spec/modules/adagioBidAdapter_spec.js | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 5c07cbe2844..ac01b317ab3 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -271,9 +271,8 @@ function getDevice() { function getSite(bidderRequest) { const { refererInfo } = bidderRequest; return { - // TODO: do these fallbacks make sense? - domain: refererInfo.domain || parseDomain(refererInfo.topmostLocation) || '', - page: refererInfo.page || refererInfo.topmostLocation || '', + domain: parseDomain(refererInfo.topmostLocation) || '', + page: refererInfo.topmostLocation || '', referrer: refererInfo.ref || getWindowSelf().document.referrer || '', top: refererInfo.reachedTop }; diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 15f79b407d1..36bb5fb1e4d 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -1391,6 +1391,7 @@ describe('Adagio bid adapter', () => { refererInfo: { numIframes: 0, reachedTop: true, + topmostLocation: 'https://test.io/article/a.html', page: 'https://test.io/article/a.html', domain: 'test.io', ref: 'https://google.com' @@ -1417,6 +1418,7 @@ describe('Adagio bid adapter', () => { numIframes: 0, reachedTop: true, page: 'http://level.io/', + topmostLocation: 'http://level.io/', stack: [ 'http://level.io/', 'http://example.com/iframe1.html', From 0e3dd1c31a4e81e22ee6dd9db46f7f7776c81eb7 Mon Sep 17 00:00:00 2001 From: Gena Date: Fri, 12 Aug 2022 19:25:03 +0200 Subject: [PATCH 060/246] Add OCM alias (#8814) --- modules/adtelligentBidAdapter.js | 4 +++- test/spec/modules/adtelligentBidAdapter_spec.js | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index 6c6fca13d7b..03d4e634062 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -22,6 +22,7 @@ const HOST_GETTERS = { streamkey: () => 'ghb.hb.streamkey.net', janet: () => 'ghb.bidder.jmgads.com', pgam: () => 'ghb.pgamssp.com', + ocm: () => 'ghb.cenarius.orangeclickmedia.com', } const getUri = function (bidderCode) { let bidderWithoutSuffix = bidderCode.split('_')[0]; @@ -40,7 +41,8 @@ export const spec = { aliases: ['onefiftytwomedia', 'appaloosa', 'bidsxchange', 'streamkey', 'janet', { code: 'selectmedia', gvlid: 775 }, { code: 'navelix', gvlid: 380 }, - 'pgam' + 'pgam', + 'ocm', ], supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid: function (bid) { diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js index 2600b7c4595..a9b9724da3a 100644 --- a/test/spec/modules/adtelligentBidAdapter_spec.js +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -19,6 +19,7 @@ const aliasEP = { streamkey: 'https://ghb.hb.streamkey.net/v2/auction/', janet: 'https://ghb.bidder.jmgads.com/v2/auction/', pgam: 'https://ghb.pgamssp.com/v2/auction/', + ocm: 'https://ghb.cenarius.orangeclickmedia.com/v2/auction/', }; const DEFAULT_ADATPER_REQ = { bidderCode: 'adtelligent' }; From b3710cbfa19591e9c0ef31c43e257e152693c896 Mon Sep 17 00:00:00 2001 From: Brian Schmidt Date: Fri, 12 Aug 2022 14:42:55 -0700 Subject: [PATCH 061/246] make openxAnalyticsAdapter do nothing (#8828) --- modules/openxAnalyticsAdapter.js | 749 +----------------- modules/openxAnalyticsAdapter.md | 4 + .../modules/openxAnalyticsAdapter_spec.js | 644 +-------------- 3 files changed, 15 insertions(+), 1382 deletions(-) diff --git a/modules/openxAnalyticsAdapter.js b/modules/openxAnalyticsAdapter.js index 7cc71fe898e..3c5517223af 100644 --- a/modules/openxAnalyticsAdapter.js +++ b/modules/openxAnalyticsAdapter.js @@ -1,128 +1,19 @@ import { - _each, - _map, - deepAccess, - flatten, - getWindowLocation, - isEmpty, - logError, - logInfo, - logMessage, - logWarn, - parseQS, - parseSizesInput, - uniques + logWarn } from '../src/utils.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; -import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; -import {ajax} from '../src/ajax.js'; -import {find, includes} from '../src/polyfill.js'; -export const AUCTION_STATES = { - INIT: 'initialized', // auction has initialized - ENDED: 'ended', // all auction requests have been accounted for - COMPLETED: 'completed' // all slots have rendered -}; - -const ADAPTER_VERSION = '0.1'; -const SCHEMA_VERSION = '0.1'; - -const AUCTION_END_WAIT_TIME = 1000; -const URL_PARAM = ''; -const ANALYTICS_TYPE = 'endpoint'; -const ENDPOINT = 'https://prebid.openx.net/ox/analytics/'; - -// Event Types -const { - EVENTS: { AUCTION_INIT, BID_REQUESTED, BID_RESPONSE, BID_TIMEOUT, AUCTION_END, BID_WON } -} = CONSTANTS; -const SLOT_LOADED = 'slotOnload'; - -const UTM_TAGS = [ - 'utm_campaign', - 'utm_source', - 'utm_medium', - 'utm_term', - 'utm_content' -]; -const UTM_TO_CAMPAIGN_PROPERTIES = { - 'utm_campaign': 'name', - 'utm_source': 'source', - 'utm_medium': 'medium', - 'utm_term': 'term', - 'utm_content': 'content' -}; - -/** - * @typedef {Object} OxAnalyticsConfig - * @property {string} orgId - * @property {string} publisherPlatformId - * @property {number} publisherAccountId - * @property {string} configId - * @property {string} optimizerConfig - * @property {number} sampling - * @property {Object} campaign - * @property {number} payloadWaitTime - * @property {number} payloadWaitTimePadding - * @property {Array} adUnits - */ - -/** - * @type {OxAnalyticsConfig} - */ -const DEFAULT_ANALYTICS_CONFIG = { - orgId: void (0), - publisherPlatformId: void (0), - publisherAccountId: void (0), - sampling: 0.05, // default sampling rate of 5% - testCode: 'default', - campaign: {}, - adUnits: [], - payloadWaitTime: AUCTION_END_WAIT_TIME, - payloadWaitTimePadding: 2000 -}; - -// Initialization -/** - * @type {OxAnalyticsConfig} - */ -let analyticsConfig; -let auctionMap = {}; -let auctionOrder = 1; // tracks the number of auctions ran on the page - -let googletag = window.googletag || {}; -googletag.cmd = googletag.cmd || []; - -let openxAdapter = Object.assign(adapter({ urlParam: URL_PARAM, analyticsType: ANALYTICS_TYPE })); +let openxAdapter = Object.assign(adapter({ urlParam: '', analyticsType: 'endpoint' })); openxAdapter.originEnableAnalytics = openxAdapter.enableAnalytics; openxAdapter.enableAnalytics = function(adapterConfig = {options: {}}) { - if (isValidConfig(adapterConfig)) { - analyticsConfig = {...DEFAULT_ANALYTICS_CONFIG, ...adapterConfig.options}; - - // campaign properties defined by config will override utm query parameters - analyticsConfig.campaign = {...buildCampaignFromUtmCodes(), ...analyticsConfig.campaign}; - - logInfo('OpenX Analytics enabled with config', analyticsConfig); + logWarn('OpenX Analytics has been deprecated, this adapter will be removed in Prebid 8'); - // override track method with v2 handlers - openxAdapter.track = prebidAnalyticsEventHandler; + openxAdapter.track = prebidAnalyticsEventHandler; - googletag.cmd.push(function () { - let pubads = googletag.pubads(); - - if (pubads.addEventListener) { - pubads.addEventListener(SLOT_LOADED, args => { - openxAdapter.track({eventType: SLOT_LOADED, args}); - logInfo('OX: SlotOnLoad event triggered'); - }); - } - }); - - openxAdapter.originEnableAnalytics(adapterConfig); - } + openxAdapter.originEnableAnalytics(adapterConfig); }; adapterManager.registerAnalyticsAdapter({ @@ -132,635 +23,5 @@ adapterManager.registerAnalyticsAdapter({ export default openxAdapter; -/** - * Test Helper Functions - */ - -// reset the cache for unit tests -openxAdapter.reset = function() { - auctionMap = {}; - auctionOrder = 1; -}; - -/** - * Private Functions - */ - -function isValidConfig({options: analyticsOptions}) { - let hasOrgId = analyticsOptions && analyticsOptions.orgId !== void (0); - - const fieldValidations = [ - // tuple of property, type, required - ['orgId', 'string', hasOrgId], - ['publisherPlatformId', 'string', !hasOrgId], - ['publisherAccountId', 'number', !hasOrgId], - ['configId', 'string', false], - ['optimizerConfig', 'string', false], - ['sampling', 'number', false], - ['adIdKey', 'string', false], - ['payloadWaitTime', 'number', false], - ['payloadWaitTimePadding', 'number', false], - ]; - - let failedValidation = find(fieldValidations, ([property, type, required]) => { - // if required, the property has to exist - // if property exists, type check value - return (required && !analyticsOptions.hasOwnProperty(property)) || - /* eslint-disable valid-typeof */ - (analyticsOptions.hasOwnProperty(property) && typeof analyticsOptions[property] !== type); - }); - if (failedValidation) { - let [property, type, required] = failedValidation; - - if (required) { - logError(`OpenXAnalyticsAdapter: Expected '${property}' to exist and of type '${type}'`); - } else { - logError(`OpenXAnalyticsAdapter: Expected '${property}' to be type '${type}'`); - } - } - - return !failedValidation; -} - -function buildCampaignFromUtmCodes() { - const location = getWindowLocation(); - const queryParams = parseQS(location && location.search); - let campaign = {}; - - UTM_TAGS.forEach(function(utmKey) { - let utmValue = queryParams[utmKey]; - if (utmValue) { - let key = UTM_TO_CAMPAIGN_PROPERTIES[utmKey]; - campaign[key] = decodeURIComponent(utmValue); - } - }); - return campaign; -} - -function detectMob() { - if ( - navigator.userAgent.match(/Android/i) || - navigator.userAgent.match(/webOS/i) || - navigator.userAgent.match(/iPhone/i) || - navigator.userAgent.match(/iPad/i) || - navigator.userAgent.match(/iPod/i) || - navigator.userAgent.match(/BlackBerry/i) || - navigator.userAgent.match(/Windows Phone/i) - ) { - return true; - } else { - return false; - } -} - -function detectOS() { - if (navigator.userAgent.indexOf('Android') != -1) return 'Android'; - if (navigator.userAgent.indexOf('like Mac') != -1) return 'iOS'; - if (navigator.userAgent.indexOf('Win') != -1) return 'Windows'; - if (navigator.userAgent.indexOf('Mac') != -1) return 'Macintosh'; - if (navigator.userAgent.indexOf('Linux') != -1) return 'Linux'; - if (navigator.appVersion.indexOf('X11') != -1) return 'Unix'; - return 'Others'; -} - -function detectBrowser() { - var isChrome = - /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor); - var isCriOS = navigator.userAgent.match('CriOS'); - var isSafari = - /Safari/.test(navigator.userAgent) && - /Apple Computer/.test(navigator.vendor); - var isFirefox = /Firefox/.test(navigator.userAgent); - var isIE = - /Trident/.test(navigator.userAgent) || /MSIE/.test(navigator.userAgent); - var isEdge = /Edge/.test(navigator.userAgent); - if (isIE) return 'Internet Explorer'; - if (isEdge) return 'Microsoft Edge'; - if (isCriOS) return 'Chrome'; - if (isSafari) return 'Safari'; - if (isFirefox) return 'Firefox'; - if (isChrome) return 'Chrome'; - return 'Others'; -} - function prebidAnalyticsEventHandler({eventType, args}) { - logMessage(eventType, Object.assign({}, args)); - switch (eventType) { - case AUCTION_INIT: - onAuctionInit(args); - break; - case BID_REQUESTED: - onBidRequested(args); - break; - case BID_RESPONSE: - onBidResponse(args); - break; - case BID_TIMEOUT: - onBidTimeout(args); - break; - case AUCTION_END: - onAuctionEnd(args); - break; - case BID_WON: - onBidWon(args); - break; - case SLOT_LOADED: - onSlotLoadedV2(args); - break; - } -} - -/** - * @typedef {Object} PbAuction - * @property {string} auctionId - Auction ID of the request this bid responded to - * @property {number} timestamp //: 1586675964364 - * @property {number} auctionEnd - timestamp of when auction ended //: 1586675964364 - * @property {string} auctionStatus //: "inProgress" - * @property {Array} adUnits //: [{…}] - * @property {string} adUnitCodes //: ["video1"] - * @property {string} labels //: undefined - * @property {Array} bidderRequests //: (2) [{…}, {…}] - * @property {Array} noBids //: [] - * @property {Array} bidsReceived //: [] - * @property {Array} winningBids //: [] - * @property {number} timeout //: 3000 - * @property {Object} config //: {publisherPlatformId: "a3aece0c-9e80-4316-8deb-faf804779bd1", publisherAccountId: 537143056, sampling: 1}/* - */ - -function onAuctionInit({auctionId, timestamp: startTime, timeout, adUnitCodes}) { - auctionMap[auctionId] = { - id: auctionId, - startTime, - endTime: void (0), - timeout, - auctionOrder, - userIds: [], - adUnitCodesCount: adUnitCodes.length, - adunitCodesRenderedCount: 0, - state: AUCTION_STATES.INIT, - auctionSendDelayTimer: void (0), - }; - - // setup adunit properties in map - auctionMap[auctionId].adUnitCodeToAdUnitMap = adUnitCodes.reduce((obj, adunitCode) => { - obj[adunitCode] = { - code: adunitCode, - adPosition: void (0), - bidRequestsMap: {} - }; - return obj; - }, {}); - - auctionOrder++; -} - -/** - * @typedef {Object} PbBidRequest - * @property {string} auctionId - Auction ID of the request this bid responded to - * @property {number} auctionStart //: 1586675964364 - * @property {Object} refererInfo - * @property {PbBidderRequest} bids - * @property {number} start - Start timestamp of the bidder request - * - */ - -/** - * @typedef {Object} PbBidderRequest - * @property {string} adUnitCode - Name of div or google adunit path - * @property {string} bidder - Bame of bidder - * @property {string} bidId - Identifies the bid request - * @property {Object} mediaTypes - * @property {Object} params - * @property {string} src - * @property {Object} userId - Map of userId module to module object - */ - -/** - * Tracks the bid request - * @param {PbBidRequest} bidRequest - */ -function onBidRequested(bidRequest) { - const {auctionId, bids: bidderRequests, start, timeout} = bidRequest; - const auction = auctionMap[auctionId]; - const adUnitCodeToAdUnitMap = auction.adUnitCodeToAdUnitMap; - - bidderRequests.forEach(bidderRequest => { - const { adUnitCode, bidder, bidId: requestId, mediaTypes, params, src, userId } = bidderRequest; - - auction.userIds.push(userId); - adUnitCodeToAdUnitMap[adUnitCode].bidRequestsMap[requestId] = { - bidder, - params, - mediaTypes, - source: src, - startTime: start, - timedOut: false, - timeLimit: timeout, - bids: {} - }; - }); -} - -/** - * - * @param {BidResponse} bidResponse - */ -function onBidResponse(bidResponse) { - let { - auctionId, - adUnitCode, - requestId, - cpm, - creativeId, - requestTimestamp, - responseTimestamp, - ts, - mediaType, - dealId, - ttl, - netRevenue, - currency, - originalCpm, - originalCurrency, - width, - height, - timeToRespond: latency, - adId, - meta - } = bidResponse; - - auctionMap[auctionId].adUnitCodeToAdUnitMap[adUnitCode].bidRequestsMap[requestId].bids[adId] = { - cpm, - creativeId, - requestTimestamp, - responseTimestamp, - ts, - adId, - meta, - mediaType, - dealId, - ttl, - netRevenue, - currency, - originalCpm, - originalCurrency, - width, - height, - latency, - winner: false, - rendered: false, - renderTime: 0, - }; -} - -function onBidTimeout(args) { - _each(args, ({auctionId, adUnitCode, bidId: requestId}) => { - let timedOutRequest = deepAccess(auctionMap, - `${auctionId}.adUnitCodeToAdUnitMap.${adUnitCode}.bidRequestsMap.${requestId}`); - - if (timedOutRequest) { - timedOutRequest.timedOut = true; - } - }); -} -/** - * - * @param {PbAuction} endedAuction - */ -function onAuctionEnd(endedAuction) { - let auction = auctionMap[endedAuction.auctionId]; - - if (!auction) { - return; - } - - clearAuctionTimer(auction); - auction.endTime = endedAuction.auctionEnd; - auction.state = AUCTION_STATES.ENDED; - delayedSend(auction); -} - -/** - * - * @param {BidResponse} bidResponse - */ -function onBidWon(bidResponse) { - const { auctionId, adUnitCode, requestId, adId } = bidResponse; - let winningBid = deepAccess(auctionMap, - `${auctionId}.adUnitCodeToAdUnitMap.${adUnitCode}.bidRequestsMap.${requestId}.bids.${adId}`); - - if (winningBid) { - winningBid.winner = true; - const auction = auctionMap[auctionId]; - if (auction.sent) { - const endpoint = (analyticsConfig.endpoint || ENDPOINT) + 'event'; - const bidder = auction.adUnitCodeToAdUnitMap[adUnitCode].bidRequestsMap[requestId].bidder; - ajax(`${endpoint}?t=win&b=${adId}&a=${analyticsConfig.orgId}&bidder=${bidder}&ts=${auction.startTime}`, - () => { - logInfo(`Openx Analytics - Sending complete impression event for ${adId} at ${Date.now()}`) - }); - } else { - logInfo(`Openx Analytics - impression event for ${adId} will be sent with auction data`) - } - } -} - -/** - * - * @param {GoogleTagSlot} slot - * @param {string} serviceName - */ -function onSlotLoadedV2({ slot }) { - const renderTime = Date.now(); - const elementId = slot.getSlotElementId(); - const bidId = slot.getTargeting('hb_adid')[0]; - - let [auction, adUnit, bid] = getPathToBidResponseByBidId(bidId); - - if (!auction) { - // attempt to get auction by adUnitCode - auction = getAuctionByGoogleTagSLot(slot); - - if (!auction) { - return; // slot is not participating in an active prebid auction - } - } - - clearAuctionTimer(auction); - - // track that an adunit code has completed within an auction - auction.adunitCodesRenderedCount++; - - // mark adunit as rendered - if (bid) { - let {x, y} = getPageOffset(); - bid.rendered = true; - bid.renderTime = renderTime; - adUnit.adPosition = isAtf(elementId, x, y) ? 'ATF' : 'BTF'; - } - - if (auction.adunitCodesRenderedCount === auction.adUnitCodesCount) { - auction.state = AUCTION_STATES.COMPLETED; - } - - // prepare to send regardless if auction is complete or not as a failsafe in case not all events are tracked - // add additional padding when not all slots are rendered - delayedSend(auction); -} - -function isAtf(elementId, scrollLeft = 0, scrollTop = 0) { - let elem = document.querySelector('#' + elementId); - let isAtf = false; - if (elem) { - let bounding = elem.getBoundingClientRect(); - if (bounding) { - let windowWidth = (window.innerWidth || document.documentElement.clientWidth); - let windowHeight = (window.innerHeight || document.documentElement.clientHeight); - - // intersection coordinates - let left = Math.max(0, bounding.left + scrollLeft); - let right = Math.min(windowWidth, bounding.right + scrollLeft); - let top = Math.max(0, bounding.top + scrollTop); - let bottom = Math.min(windowHeight, bounding.bottom + scrollTop); - - let intersectionWidth = right - left; - let intersectionHeight = bottom - top; - - let intersectionArea = (intersectionHeight > 0 && intersectionWidth > 0) ? (intersectionHeight * intersectionWidth) : 0; - let adSlotArea = (bounding.right - bounding.left) * (bounding.bottom - bounding.top); - - if (adSlotArea > 0) { - // Atleast 50% of intersection in window - isAtf = intersectionArea * 2 >= adSlotArea; - } - } - } else { - logWarn('OX: DOM element not for id ' + elementId); - } - return isAtf; -} - -// backwards compatible pageOffset from https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollX -function getPageOffset() { - var x = (window.pageXOffset !== undefined) - ? window.pageXOffset - : (document.documentElement || document.body.parentNode || document.body).scrollLeft; - - var y = (window.pageYOffset !== undefined) - ? window.pageYOffset - : (document.documentElement || document.body.parentNode || document.body).scrollTop; - return {x, y}; -} - -function delayedSend(auction) { - if (auction.sent) { - return; - } - const delayTime = auction.adunitCodesRenderedCount === auction.adUnitCodesCount - ? analyticsConfig.payloadWaitTime - : analyticsConfig.payloadWaitTime + analyticsConfig.payloadWaitTimePadding; - - auction.auctionSendDelayTimer = setTimeout(() => { - auction.sent = true; // any BidWon emitted after this will be recorded separately - let payload = JSON.stringify([buildAuctionPayload(auction)]); - - ajax(analyticsConfig.endpoint || ENDPOINT, () => { - logInfo(`OpenX Analytics - Sending complete auction at ${Date.now()}`); - }, payload, { contentType: 'application/json' }); - }, delayTime); -} - -function clearAuctionTimer(auction) { - // reset the delay timer to send the auction data - if (auction.auctionSendDelayTimer) { - clearTimeout(auction.auctionSendDelayTimer); - auction.auctionSendDelayTimer = void (0); - } -} - -/** - * Returns the path to a bid (auction, adunit, bidRequest, and bid) based on a bidId - * @param {string} bidId - * @returns {Array<*>} - */ -function getPathToBidResponseByBidId(bidId) { - let auction; - let adUnit; - let bidResponse; - - if (!bidId) { - return []; - } - - _each(auctionMap, currentAuction => { - // skip completed auctions - if (currentAuction.state === AUCTION_STATES.COMPLETED) { - return; - } - - _each(currentAuction.adUnitCodeToAdUnitMap, (currentAdunit) => { - _each(currentAdunit.bidRequestsMap, currentBiddRequest => { - _each(currentBiddRequest.bids, (currentBidResponse, bidResponseId) => { - if (bidId === bidResponseId) { - auction = currentAuction; - adUnit = currentAdunit; - bidResponse = currentBidResponse; - } - }); - }); - }); - }); - return [auction, adUnit, bidResponse]; -} - -function getAuctionByGoogleTagSLot(slot) { - let slotAdunitCodes = [slot.getSlotElementId(), slot.getAdUnitPath()]; - let slotAuction; - - _each(auctionMap, auction => { - if (auction.state === AUCTION_STATES.COMPLETED) { - return; - } - - _each(auction.adUnitCodeToAdUnitMap, (bidderRequestIdMap, adUnitCode) => { - if (includes(slotAdunitCodes, adUnitCode)) { - slotAuction = auction; - } - }); - }); - - return slotAuction; -} - -function buildAuctionPayload(auction) { - let {startTime, endTime, state, timeout, auctionOrder, userIds, adUnitCodeToAdUnitMap, id} = auction; - const auctionId = id; - let {orgId, publisherPlatformId, publisherAccountId, campaign, testCode, configId, optimizerConfig} = analyticsConfig; - - return { - auctionId, - adapterVersion: ADAPTER_VERSION, - schemaVersion: SCHEMA_VERSION, - orgId, - publisherPlatformId, - publisherAccountId, - configId, - optimizerConfig, - campaign, - state, - startTime, - endTime, - timeLimit: timeout, - auctionOrder, - deviceType: detectMob() ? 'Mobile' : 'Desktop', - deviceOSType: detectOS(), - browser: detectBrowser(), - testCode: testCode, - // return an array of module name that have user data - userIdProviders: buildUserIdProviders(userIds), - adUnits: buildAdUnitsPayload(adUnitCodeToAdUnitMap), - }; - - function buildAdUnitsPayload(adUnitCodeToAdUnitMap) { - return _map(adUnitCodeToAdUnitMap, (adUnit) => { - let {code, adPosition} = adUnit; - - return { - code, - adPosition, - bidRequests: buildBidRequestPayload(adUnit.bidRequestsMap) - }; - - function buildBidRequestPayload(bidRequestsMap) { - return _map(bidRequestsMap, (bidRequest) => { - let {bidder, source, bids, mediaTypes, timeLimit, timedOut} = bidRequest; - return { - bidder, - source, - hasBidderResponded: Object.keys(bids).length > 0, - availableAdSizes: getMediaTypeSizes(mediaTypes), - availableMediaTypes: getMediaTypes(mediaTypes), - timeLimit, - timedOut, - bidResponses: _map(bidRequest.bids, (bidderBidResponse) => { - let { - adId, - cpm, - creativeId, - ts, - meta, - mediaType, - dealId, - ttl, - netRevenue, - currency, - width, - height, - latency, - winner, - rendered, - renderTime - } = bidderBidResponse; - - return { - bidId: adId, - microCpm: cpm * 1000000, - netRevenue, - currency, - mediaType, - height, - width, - size: `${width}x${height}`, - dealId, - latency, - ttl, - winner, - creativeId, - ts, - rendered, - renderTime, - meta - } - }) - } - }); - } - }); - } - - function buildUserIdProviders(userIds) { - return _map(userIds, (userId) => { - return _map(userId, (id, module) => { - return hasUserData(module, id) ? module : false - }).filter(module => module); - }).reduce(flatten, []).filter(uniques).sort(); - } - - function hasUserData(module, idOrIdObject) { - let normalizedId; - - switch (module) { - case 'digitrustid': - normalizedId = deepAccess(idOrIdObject, 'data.id'); - break; - case 'lipb': - normalizedId = idOrIdObject.lipbid; - break; - default: - normalizedId = idOrIdObject; - } - - return !isEmpty(normalizedId); - } - - function getMediaTypeSizes(mediaTypes) { - return _map(mediaTypes, (mediaTypeConfig, mediaType) => { - return parseSizesInput(mediaTypeConfig.sizes) - .map(size => `${mediaType}_${size}`); - }).reduce(flatten, []); - } - - function getMediaTypes(mediaTypes) { - return _map(mediaTypes, (mediaTypeConfig, mediaType) => mediaType); - } } diff --git a/modules/openxAnalyticsAdapter.md b/modules/openxAnalyticsAdapter.md index af40486f2a4..357e1520eb4 100644 --- a/modules/openxAnalyticsAdapter.md +++ b/modules/openxAnalyticsAdapter.md @@ -5,6 +5,10 @@ --- +# NOTE: OPENX NO LONGER OFFERS ANALYTICS +# THIS ADAPTER NO LONGER FUNCTIONS AND +# WILL BE REMOVED IN PREBID 8 + # About this Guide This implementation guide walks through the flow of onboarding an alpha Publisher to test OpenX’s new Analytics Adapter. diff --git a/test/spec/modules/openxAnalyticsAdapter_spec.js b/test/spec/modules/openxAnalyticsAdapter_spec.js index 47663a41f47..1b1adeb8807 100644 --- a/test/spec/modules/openxAnalyticsAdapter_spec.js +++ b/test/spec/modules/openxAnalyticsAdapter_spec.js @@ -1,653 +1,21 @@ import { expect } from 'chai'; -import openxAdapter, {AUCTION_STATES} from 'modules/openxAnalyticsAdapter.js'; -import * as events from 'src/events.js'; -import CONSTANTS from 'src/constants.json'; +import openxAdapter from 'modules/openxAnalyticsAdapter.js'; import * as utils from 'src/utils.js'; -import { server } from 'test/mocks/xhr.js'; -import {find} from 'src/polyfill.js'; - -const { - EVENTS: { AUCTION_INIT, BID_REQUESTED, BID_RESPONSE, BID_TIMEOUT, BID_WON, AUCTION_END } -} = CONSTANTS; -const SLOT_LOADED = 'slotOnload'; -const CURRENT_TIME = 1586000000000; describe('openx analytics adapter', function() { - describe('when validating the configuration', function () { + describe('deprecation message', function () { let spy; beforeEach(function () { - spy = sinon.spy(utils, 'logError'); + spy = sinon.spy(utils, 'logWarn'); }); afterEach(function() { - utils.logError.restore(); + utils.logWarn.restore(); }); - it('should require organization id when no configuration is passed', function() { + it('should warn on enable', function() { openxAdapter.enableAnalytics(); - expect(spy.firstCall.args[0]).to.match(/publisherPlatformId/); - expect(spy.firstCall.args[0]).to.match(/to exist/); - }); - - it('should require publisher id when no orgId is passed', function() { - openxAdapter.enableAnalytics({ - provider: 'openx', - options: { - publisherAccountId: 12345 - } - }); - expect(spy.firstCall.args[0]).to.match(/publisherPlatformId/); - expect(spy.firstCall.args[0]).to.match(/to exist/); - }); - - it('should validate types', function() { - openxAdapter.enableAnalytics({ - provider: 'openx', - options: { - orgId: 'test platformId', - sampling: 'invalid-float' - } - }); - - expect(spy.firstCall.args[0]).to.match(/sampling/); - expect(spy.firstCall.args[0]).to.match(/type 'number'/); - }); - }); - - describe('when tracking analytic events', function () { - const AD_UNIT_CODE = 'test-div-1'; - const SLOT_LOAD_WAIT_TIME = 10; - - const DEFAULT_V2_ANALYTICS_CONFIG = { - orgId: 'test-org-id', - publisherAccountId: 123, - publisherPlatformId: 'test-platform-id', - configId: 'my_config', - optimizerConfig: 'my my optimizer', - sample: 1.0, - payloadWaitTime: SLOT_LOAD_WAIT_TIME, - payloadWaitTimePadding: SLOT_LOAD_WAIT_TIME - }; - - const auctionInit = { - auctionId: 'test-auction-id', - timestamp: CURRENT_TIME, - timeout: 3000, - adUnitCodes: [AD_UNIT_CODE], - }; - - const bidRequestedOpenX = { - auctionId: 'test-auction-id', - auctionStart: CURRENT_TIME, - timeout: 2000, - bids: [ - { - adUnitCode: AD_UNIT_CODE, - bidId: 'test-openx-request-id', - bidder: 'openx', - params: { unit: 'test-openx-ad-unit-id' }, - userId: { - tdid: 'test-tradedesk-id', - empty_id: '', - null_id: null, - bla_id: '', - digitrustid: { data: { id: '1' } }, - lipbid: { lipb: '2' } - } - } - ], - start: CURRENT_TIME + 10 - }; - - const bidRequestedCloseX = { - auctionId: 'test-auction-id', - auctionStart: CURRENT_TIME, - timeout: 1000, - bids: [ - { - adUnitCode: AD_UNIT_CODE, - bidId: 'test-closex-request-id', - bidder: 'closex', - params: { unit: 'test-closex-ad-unit-id' }, - userId: { - bla_id: '2', - tdid: 'test-tradedesk-id' - } - } - ], - start: CURRENT_TIME + 20 - }; - - const bidResponseOpenX = { - adUnitCode: AD_UNIT_CODE, - cpm: 0.5, - netRevenue: true, - requestId: 'test-openx-request-id', - mediaType: 'banner', - width: 300, - height: 250, - adId: 'test-openx-ad-id', - auctionId: 'test-auction-id', - creativeId: 'openx-crid', - currency: 'USD', - timeToRespond: 100, - responseTimestamp: CURRENT_TIME + 30, - ts: 'test-openx-ts' - }; - - const bidResponseCloseX = { - adUnitCode: AD_UNIT_CODE, - cpm: 0.3, - netRevenue: true, - requestId: 'test-closex-request-id', - mediaType: 'video', - width: 300, - height: 250, - adId: 'test-closex-ad-id', - auctionId: 'test-auction-id', - creativeId: 'closex-crid', - currency: 'USD', - timeToRespond: 200, - dealId: 'test-closex-deal-id', - responseTimestamp: CURRENT_TIME + 40, - ts: 'test-closex-ts' - }; - - const bidTimeoutOpenX = { - 0: { - adUnitCode: AD_UNIT_CODE, - auctionId: 'test-auction-id', - bidId: 'test-openx-request-id' - }}; - - const bidTimeoutCloseX = { - 0: { - adUnitCode: AD_UNIT_CODE, - auctionId: 'test-auction-id', - bidId: 'test-closex-request-id' - } - }; - - const bidWonOpenX = { - requestId: 'test-openx-request-id', - adId: 'test-openx-ad-id', - adUnitCode: AD_UNIT_CODE, - auctionId: 'test-auction-id' - }; - - const auctionEnd = { - auctionId: 'test-auction-id', - timestamp: CURRENT_TIME, - auctionEnd: CURRENT_TIME + 100, - timeout: 3000, - adUnitCodes: [AD_UNIT_CODE], - }; - - const bidWonCloseX = { - requestId: 'test-closex-request-id', - adId: 'test-closex-ad-id', - adUnitCode: AD_UNIT_CODE, - auctionId: 'test-auction-id' - }; - - function simulateAuction(events) { - let highestBid; - - events.forEach(event => { - const [eventType, args] = event; - if (eventType === BID_RESPONSE) { - highestBid = highestBid || args; - if (highestBid.cpm < args.cpm) { - highestBid = args; - } - } - - if (eventType === SLOT_LOADED) { - const slotLoaded = { - slot: { - getAdUnitPath: () => { - return '/12345678/test_ad_unit'; - }, - getSlotElementId: () => { - return AD_UNIT_CODE; - }, - getTargeting: (key) => { - if (key === 'hb_adid') { - return highestBid ? [highestBid.adId] : []; - } else { - return []; - } - } - } - }; - openxAdapter.track({ eventType, args: slotLoaded }); - } else { - openxAdapter.track({ eventType, args }); - } - }); - } - - let clock; - - beforeEach(function() { - sinon.stub(events, 'getEvents').returns([]); - clock = sinon.useFakeTimers(CURRENT_TIME); - }); - - afterEach(function() { - events.getEvents.restore(); - clock.restore(); - }); - - describe('when there is an auction', function () { - let auction; - let auction2; - beforeEach(function () { - openxAdapter.enableAnalytics({options: DEFAULT_V2_ANALYTICS_CONFIG}); - - simulateAuction([ - [AUCTION_INIT, auctionInit], - [SLOT_LOADED] - ]); - - simulateAuction([ - [AUCTION_INIT, {...auctionInit, auctionId: 'second-auction-id'}], - [SLOT_LOADED] - ]); - - clock.tick(SLOT_LOAD_WAIT_TIME); - auction = JSON.parse(server.requests[0].requestBody)[0]; - auction2 = JSON.parse(server.requests[1].requestBody)[0]; - }); - - afterEach(function () { - openxAdapter.reset(); - openxAdapter.disableAnalytics(); - }); - - it('should track auction start time', function () { - expect(auction.startTime).to.equal(auctionInit.timestamp); - }); - - it('should track auction time limit', function () { - expect(auction.timeLimit).to.equal(auctionInit.timeout); - }); - - it('should track the \'default\' test code', function () { - expect(auction.testCode).to.equal('default'); - }); - - it('should track auction count', function () { - expect(auction.auctionOrder).to.equal(1); - expect(auction2.auctionOrder).to.equal(2); - }); - - it('should track the orgId', function () { - expect(auction.orgId).to.equal(DEFAULT_V2_ANALYTICS_CONFIG.orgId); - }); - - it('should track the orgId', function () { - expect(auction.publisherPlatformId).to.equal(DEFAULT_V2_ANALYTICS_CONFIG.publisherPlatformId); - }); - - it('should track the orgId', function () { - expect(auction.publisherAccountId).to.equal(DEFAULT_V2_ANALYTICS_CONFIG.publisherAccountId); - }); - - it('should track the optimizerConfig', function () { - expect(auction.optimizerConfig).to.equal(DEFAULT_V2_ANALYTICS_CONFIG.optimizerConfig); - }); - - it('should track the configId', function () { - expect(auction.configId).to.equal(DEFAULT_V2_ANALYTICS_CONFIG.configId); - }); - - it('should track the auction Id', function () { - expect(auction.auctionId).to.equal(auctionInit.auctionId); - }); - }); - - describe('when there is a custom test code', function () { - let auction; - beforeEach(function () { - openxAdapter.enableAnalytics({ - options: { - ...DEFAULT_V2_ANALYTICS_CONFIG, - testCode: 'test-code' - } - }); - - simulateAuction([ - [AUCTION_INIT, auctionInit], - [SLOT_LOADED], - ]); - clock.tick(SLOT_LOAD_WAIT_TIME); - auction = JSON.parse(server.requests[0].requestBody)[0]; - }); - - afterEach(function () { - openxAdapter.reset(); - openxAdapter.disableAnalytics(); - }); - - it('should track the custom test code', function () { - expect(auction.testCode).to.equal('test-code'); - }); - }); - - describe('when there is campaign (utm) data', function () { - let auction; - beforeEach(function () { - - }); - - afterEach(function () { - openxAdapter.reset(); - utils.getWindowLocation.restore(); - openxAdapter.disableAnalytics(); - }); - - it('should track values from query params when they exist', function () { - sinon.stub(utils, 'getWindowLocation').returns({search: '?' + - 'utm_campaign=test%20campaign-name&' + - 'utm_source=test-source&' + - 'utm_medium=test-medium&' - }); - - openxAdapter.enableAnalytics({options: DEFAULT_V2_ANALYTICS_CONFIG}); - - simulateAuction([ - [AUCTION_INIT, auctionInit], - [SLOT_LOADED], - ]); - clock.tick(SLOT_LOAD_WAIT_TIME); - auction = JSON.parse(server.requests[0].requestBody)[0]; - - // ensure that value are URI decoded - expect(auction.campaign.name).to.equal('test campaign-name'); - expect(auction.campaign.source).to.equal('test-source'); - expect(auction.campaign.medium).to.equal('test-medium'); - expect(auction.campaign.content).to.be.undefined; - expect(auction.campaign.term).to.be.undefined; - }); - - it('should override query params if configuration parameters exist', function () { - sinon.stub(utils, 'getWindowLocation').returns({search: '?' + - 'utm_campaign=test-campaign-name&' + - 'utm_source=test-source&' + - 'utm_medium=test-medium&' + - 'utm_content=test-content&' + - 'utm_term=test-term' - }); - - openxAdapter.enableAnalytics({ - options: { - ...DEFAULT_V2_ANALYTICS_CONFIG, - campaign: { - name: 'test-config-name', - source: 'test-config-source', - medium: 'test-config-medium' - } - } - }); - - simulateAuction([ - [AUCTION_INIT, auctionInit], - [SLOT_LOADED], - ]); - clock.tick(SLOT_LOAD_WAIT_TIME); - auction = JSON.parse(server.requests[0].requestBody)[0]; - - expect(auction.campaign.name).to.equal('test-config-name'); - expect(auction.campaign.source).to.equal('test-config-source'); - expect(auction.campaign.medium).to.equal('test-config-medium'); - expect(auction.campaign.content).to.equal('test-content'); - expect(auction.campaign.term).to.equal('test-term'); - }); - }); - - describe('when there are bid requests', function () { - let auction; - let openxBidder; - let closexBidder; - - beforeEach(function () { - openxAdapter.enableAnalytics({options: DEFAULT_V2_ANALYTICS_CONFIG}); - - simulateAuction([ - [AUCTION_INIT, auctionInit], - [BID_REQUESTED, bidRequestedCloseX], - [BID_REQUESTED, bidRequestedOpenX], - [SLOT_LOADED], - ]); - clock.tick(SLOT_LOAD_WAIT_TIME * 2); - auction = JSON.parse(server.requests[0].requestBody)[0]; - openxBidder = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'openx'); - closexBidder = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'closex'); - }); - - afterEach(function () { - openxAdapter.reset(); - openxAdapter.disableAnalytics(); - }); - - it('should track the bidder', function () { - expect(openxBidder.bidder).to.equal('openx'); - expect(closexBidder.bidder).to.equal('closex'); - }); - - it('should track the adunit code', function () { - expect(auction.adUnits[0].code).to.equal(AD_UNIT_CODE); - }); - - it('should track the user ids', function () { - expect(auction.userIdProviders).to.deep.equal(['bla_id', 'digitrustid', 'lipbid', 'tdid']); - }); - - it('should not have responded', function () { - expect(openxBidder.hasBidderResponded).to.equal(false); - expect(closexBidder.hasBidderResponded).to.equal(false); - }); - }); - - describe('when there are request timeouts', function () { - let auction; - let openxBidRequest; - let closexBidRequest; - - beforeEach(function () { - openxAdapter.enableAnalytics({options: DEFAULT_V2_ANALYTICS_CONFIG}); - - simulateAuction([ - [AUCTION_INIT, auctionInit], - [BID_REQUESTED, bidRequestedCloseX], - [BID_REQUESTED, bidRequestedOpenX], - [BID_TIMEOUT, bidTimeoutCloseX], - [BID_TIMEOUT, bidTimeoutOpenX], - [AUCTION_END, auctionEnd] - ]); - clock.tick(SLOT_LOAD_WAIT_TIME * 2); - auction = JSON.parse(server.requests[0].requestBody)[0]; - - openxBidRequest = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'openx'); - closexBidRequest = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'closex'); - }); - - afterEach(function () { - openxAdapter.reset(); - openxAdapter.disableAnalytics(); - }); - - it('should track the timeout', function () { - expect(openxBidRequest.timedOut).to.equal(true); - expect(closexBidRequest.timedOut).to.equal(true); - }); - - it('should track the timeout value ie timeLimit', function () { - expect(openxBidRequest.timeLimit).to.equal(2000); - expect(closexBidRequest.timeLimit).to.equal(1000); - }); - }); - - describe('when there are bid responses', function () { - let auction; - let openxBidResponse; - let closexBidResponse; - - beforeEach(function () { - openxAdapter.enableAnalytics({options: DEFAULT_V2_ANALYTICS_CONFIG}); - - simulateAuction([ - [AUCTION_INIT, auctionInit], - [BID_REQUESTED, bidRequestedCloseX], - [BID_REQUESTED, bidRequestedOpenX], - [BID_RESPONSE, bidResponseOpenX], - [BID_RESPONSE, bidResponseCloseX], - [AUCTION_END, auctionEnd] - ]); - - clock.tick(SLOT_LOAD_WAIT_TIME * 2); - auction = JSON.parse(server.requests[0].requestBody)[0]; - - openxBidResponse = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'openx').bidResponses[0]; - closexBidResponse = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'closex').bidResponses[0]; - }); - - afterEach(function () { - openxAdapter.reset(); - openxAdapter.disableAnalytics(); - }); - - it('should track the cpm in microCPM', function () { - expect(openxBidResponse.microCpm).to.equal(bidResponseOpenX.cpm * 1000000); - expect(closexBidResponse.microCpm).to.equal(bidResponseCloseX.cpm * 1000000); - }); - - it('should track if the bid is in net revenue', function () { - expect(openxBidResponse.netRevenue).to.equal(bidResponseOpenX.netRevenue); - expect(closexBidResponse.netRevenue).to.equal(bidResponseCloseX.netRevenue); - }); - - it('should track the mediaType', function () { - expect(openxBidResponse.mediaType).to.equal(bidResponseOpenX.mediaType); - expect(closexBidResponse.mediaType).to.equal(bidResponseCloseX.mediaType); - }); - - it('should track the currency', function () { - expect(openxBidResponse.currency).to.equal(bidResponseOpenX.currency); - expect(closexBidResponse.currency).to.equal(bidResponseCloseX.currency); - }); - - it('should track the ad width and height', function () { - expect(openxBidResponse.width).to.equal(bidResponseOpenX.width); - expect(openxBidResponse.height).to.equal(bidResponseOpenX.height); - - expect(closexBidResponse.width).to.equal(bidResponseCloseX.width); - expect(closexBidResponse.height).to.equal(bidResponseCloseX.height); - }); - - it('should track the bid dealId', function () { - expect(openxBidResponse.dealId).to.equal(bidResponseOpenX.dealId); // no deal id defined - expect(closexBidResponse.dealId).to.equal(bidResponseCloseX.dealId); // deal id defined - }); - - it('should track the bid\'s latency', function () { - expect(openxBidResponse.latency).to.equal(bidResponseOpenX.timeToRespond); - expect(closexBidResponse.latency).to.equal(bidResponseCloseX.timeToRespond); - }); - - it('should not have any bid winners', function () { - expect(openxBidResponse.winner).to.equal(false); - expect(closexBidResponse.winner).to.equal(false); - }); - - it('should track the bid currency', function () { - expect(openxBidResponse.currency).to.equal(bidResponseOpenX.currency); - expect(closexBidResponse.currency).to.equal(bidResponseCloseX.currency); - }); - - it('should track the auction end time', function () { - expect(auction.endTime).to.equal(auctionEnd.auctionEnd); - }); - - it('should track that the auction ended', function () { - expect(auction.state).to.equal(AUCTION_STATES.ENDED); - }); - }); - - describe('when there are bidder wins', function () { - let auction; - beforeEach(function () { - openxAdapter.enableAnalytics({options: DEFAULT_V2_ANALYTICS_CONFIG}); - - simulateAuction([ - [AUCTION_INIT, auctionInit], - [BID_REQUESTED, bidRequestedOpenX], - [BID_REQUESTED, bidRequestedCloseX], - [BID_RESPONSE, bidResponseOpenX], - [BID_RESPONSE, bidResponseCloseX], - [AUCTION_END, auctionEnd], - [BID_WON, bidWonOpenX] - ]); - - clock.tick(SLOT_LOAD_WAIT_TIME * 2); - auction = JSON.parse(server.requests[0].requestBody)[0]; - }); - - afterEach(function () { - openxAdapter.reset(); - openxAdapter.disableAnalytics(); - }); - - it('should track that bidder as the winner', function () { - let openxBidder = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'openx'); - expect(openxBidder.bidResponses[0]).to.contain({winner: true}); - }); - - it('should track that bidder as the losers', function () { - let closexBidder = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'closex'); - expect(closexBidder.bidResponses[0]).to.contain({winner: false}); - }); - }); - - describe('when a winning bid renders', function () { - let auction; - beforeEach(function () { - openxAdapter.enableAnalytics({options: DEFAULT_V2_ANALYTICS_CONFIG}); - - simulateAuction([ - [AUCTION_INIT, auctionInit], - [BID_REQUESTED, bidRequestedOpenX], - [BID_REQUESTED, bidRequestedCloseX], - [BID_RESPONSE, bidResponseOpenX], - [BID_RESPONSE, bidResponseCloseX], - [AUCTION_END, auctionEnd], - [BID_WON, bidWonOpenX], - [SLOT_LOADED] - ]); - - clock.tick(SLOT_LOAD_WAIT_TIME * 2); - auction = JSON.parse(server.requests[0].requestBody)[0]; - }); - - afterEach(function () { - openxAdapter.reset(); - openxAdapter.disableAnalytics(); - }); - - it('should track that winning bid rendered', function () { - let openxBidder = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'openx'); - expect(openxBidder.bidResponses[0]).to.contain({rendered: true}); - }); - - it('should track that winning bid render time', function () { - let openxBidder = find(auction.adUnits[0].bidRequests, bidderRequest => bidderRequest.bidder === 'openx'); - expect(openxBidder.bidResponses[0]).to.contain({renderTime: CURRENT_TIME}); - }); - - it('should track that the auction completed', function () { - expect(auction.state).to.equal(AUCTION_STATES.COMPLETED); - }); + expect(spy.firstCall.args[0]).to.match(/OpenX Analytics has been deprecated/); }); }); }); From d836669df6529bf12f804c2d80cb0806084439e0 Mon Sep 17 00:00:00 2001 From: AndriiTokarGL <111281550+AndriiTokarGL@users.noreply.github.com> Date: Mon, 15 Aug 2022 16:57:43 +0300 Subject: [PATCH 062/246] Aniview Bid Adapter: add a new alias (#8832) --- modules/aniviewBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/aniviewBidAdapter.js b/modules/aniviewBidAdapter.js index 00280379133..84552638421 100644 --- a/modules/aniviewBidAdapter.js +++ b/modules/aniviewBidAdapter.js @@ -306,7 +306,7 @@ function getUserSyncs(syncOptions, serverResponses) { export const spec = { code: BIDDER_CODE, gvlid: GVLID, - aliases: ['avantisvideo', 'selectmediavideo', 'vidcrunch', 'openwebvideo', 'didnavideo', 'ottadvisors'], + aliases: ['avantisvideo', 'selectmediavideo', 'vidcrunch', 'openwebvideo', 'didnavideo', 'ottadvisors', 'pgammedia'], supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid, buildRequests, From 9eda78a179ad4d5477fa9d5ebe8bd760fa925c95 Mon Sep 17 00:00:00 2001 From: pm-nitin-shirsat <107102698+pm-nitin-shirsat@users.noreply.github.com> Date: Mon, 15 Aug 2022 19:28:30 +0530 Subject: [PATCH 063/246] Pubmatic bid adapter: improved site object handling (#8820) * UOE-7836: Vanilla JS: Stop overwriting site.page site.ref and site.domain * UOE-7836: Vanilla JS: Stop overwriting site.page site.ref and site.domain in PrebidServerBidAdapter * Rename variable name * Revert changes from PrebidServerBidAdapter file * Test cases written for the ticket UOE-7666 - stop overriding page, domain, site --- modules/pubmaticBidAdapter.js | 6 ++- test/spec/modules/pubmaticBidAdapter_spec.js | 57 ++++++++++++++++++-- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 922fedd5092..b71fa5c8ed2 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -185,7 +185,7 @@ _each(NATIVE_ASSETS, anAsset => { NATIVE_ASSET_ID_TO_KEY_MAP[anAsset.ID] = anAss // loading NATIVE_ASSET_KEY_TO_ASSET_MAP _each(NATIVE_ASSETS, anAsset => { NATIVE_ASSET_KEY_TO_ASSET_MAP[anAsset.KEY] = anAsset }); -function _getDomainFromURL(url) { +export function _getDomainFromURL(url) { let anchor = document.createElement('a'); anchor.href = url; return anchor.hostname; @@ -1162,7 +1162,11 @@ export const spec = { // First Party Data const commonFpd = (bidderRequest && bidderRequest.ortb2) || {}; if (commonFpd.site) { + const { page, domain, ref } = payload.site; mergeDeep(payload, {site: commonFpd.site}); + payload.site.page = page; + payload.site.domain = domain; + payload.site.ref = ref; } if (commonFpd.user) { mergeDeep(payload, {user: commonFpd.user}); diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index f3a49a6a925..688bec5b5e9 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -1,5 +1,5 @@ import {expect} from 'chai'; -import {spec, checkVideoPlacement} from 'modules/pubmaticBidAdapter.js'; +import {spec, checkVideoPlacement, _getDomainFromURL} from 'modules/pubmaticBidAdapter.js'; import * as utils from 'src/utils.js'; import {config} from 'src/config.js'; import { createEidsArray } from 'modules/userId/eids.js'; @@ -1688,17 +1688,66 @@ describe('PubMatic adapter', function () { describe('FPD', function() { let newRequest; - it('ortb2.site should be merged in the request', function() { + describe('ortb2.site should not override page, domain & ref values', function() { + it('When above properties are present in ortb2.site', function() { + const ortb2 = { + site: { + domain: 'page.example.com', + page: 'https://page.example.com/here.html', + ref: 'https://page.example.com/here.html' + } + }; + const request = spec.buildRequests(bidRequests, {ortb2}); + let data = JSON.parse(request.data); + expect(data.site.domain).not.equal('page.example.com'); + expect(data.site.page).not.equal('https://page.example.com/here.html'); + expect(data.site.ref).not.equal('https://page.example.com/here.html'); + }); + + it('When above properties are absent in ortb2.site', function () { + const ortb2 = { + site: {} + }; + let request = spec.buildRequests(bidRequests, { + auctionId: 'new-auction-id', + ortb2 + }); + let data = JSON.parse(request.data); + let response = spec.interpretResponse(bidResponses, request); + expect(data.site.page).to.equal(bidRequests[0].params.kadpageurl); + expect(data.site.domain).to.equal(_getDomainFromURL(data.site.page)); + expect(response[0].referrer).to.equal(data.site.ref); + }); + + it('With some extra properties in ortb2.site', function() { + const ortb2 = { + site: { + domain: 'page.example.com', + page: 'https://page.example.com/here.html', + ref: 'https://page.example.com/here.html', + cat: ['IAB2'], + sectioncat: ['IAB2-2'] + } + }; + const request = spec.buildRequests(bidRequests, {ortb2}); + let data = JSON.parse(request.data); + expect(data.site.domain).not.equal('page.example.com'); + expect(data.site.page).not.equal('https://page.example.com/here.html'); + expect(data.site.ref).not.equal('https://page.example.com/here.html'); + expect(data.site.cat).to.deep.equal(['IAB2']); + expect(data.site.sectioncat).to.deep.equal(['IAB2-2']); + }); + }); + + it('ortb2.site should be merged except page, domain & ref in the request', function() { const ortb2 = { site: { - domain: 'page.example.com', cat: ['IAB2'], sectioncat: ['IAB2-2'] } }; const request = spec.buildRequests(bidRequests, {ortb2}); let data = JSON.parse(request.data); - expect(data.site.domain).to.equal('page.example.com'); expect(data.site.cat).to.deep.equal(['IAB2']); expect(data.site.sectioncat).to.deep.equal(['IAB2-2']); }); From 902cfc087b85ee6162a7f49544f57f07ab26f06e Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Mon, 15 Aug 2022 07:00:39 -0700 Subject: [PATCH 064/246] Build system: fix "test-coverage" (#8819) --- babelConfig.js | 14 +- karma.conf.maker.js | 13 +- package-lock.json | 367 ++++++++++++++++++++++++++++++-------------- package.json | 4 +- 4 files changed, 265 insertions(+), 133 deletions(-) diff --git a/babelConfig.js b/babelConfig.js index 2592d8dc776..a88491c0cae 100644 --- a/babelConfig.js +++ b/babelConfig.js @@ -22,9 +22,15 @@ module.exports = function (options = {}) { } ] ], - 'plugins': [ - [path.resolve(__dirname, './plugins/pbjsGlobals.js'), options], - [useLocal('@babel/plugin-transform-runtime')], - ], + 'plugins': (() => { + const plugins = [ + [path.resolve(__dirname, './plugins/pbjsGlobals.js'), options], + [useLocal('@babel/plugin-transform-runtime')], + ]; + if (options.codeCoverage) { + plugins.push([useLocal('babel-plugin-istanbul')]) + } + return plugins; + })(), } } diff --git a/karma.conf.maker.js b/karma.conf.maker.js index 82ede90b141..81930f42544 100644 --- a/karma.conf.maker.js +++ b/karma.conf.maker.js @@ -22,20 +22,9 @@ function newWebpackConfig(codeCoverage, disableFeatures) { .flatMap((r) => r.use) .filter((use) => use.loader === 'babel-loader') .forEach((use) => { - use.options = babelConfig({test: true, disableFeatures}); + use.options = babelConfig({test: true, codeCoverage, disableFeatures}); }); - if (codeCoverage) { - webpackConfig.module.rules.push({ - enforce: 'post', - exclude: /(node_modules)|(test)|(integrationExamples)|(build)|polyfill.js|(src\/adapters\/analytics\/ga.js)/, - use: { - loader: '@jsdevtools/coverage-istanbul-loader', - options: { esModules: true } - }, - test: /\.js$/ - }) - } return webpackConfig; } diff --git a/package-lock.json b/package-lock.json index 0e221a988fd..749fafaebc4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "prebid.js", - "version": "7.8.0-pre", + "version": "7.10.0-pre", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.16.7", @@ -26,7 +26,6 @@ }, "devDependencies": { "@babel/eslint-parser": "^7.16.5", - "@jsdevtools/coverage-istanbul-loader": "^3.0.3", "@wdio/browserstack-service": "^7.16.0", "@wdio/cli": "^7.5.2", "@wdio/concise-reporter": "^7.5.2", @@ -37,6 +36,7 @@ "ajv": "6.12.3", "assert": "^2.0.0", "babel-loader": "^8.0.5", + "babel-plugin-istanbul": "^6.1.1", "babel-register": "^6.26.0", "body-parser": "^1.19.0", "chai": "^4.2.0", @@ -1837,6 +1837,92 @@ "node": ">=6.9.0" } }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -1978,19 +2064,6 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@jsdevtools/coverage-istanbul-loader": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz", - "integrity": "sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA==", - "dev": true, - "dependencies": { - "convert-source-map": "^1.7.0", - "istanbul-lib-instrument": "^4.0.3", - "loader-utils": "^2.0.0", - "merge-source-map": "^1.1.0", - "schema-utils": "^2.7.0" - } - }, "node_modules/@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", @@ -4459,6 +4532,38 @@ "object.assign": "^4.1.0" } }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", @@ -10016,6 +10121,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/get-pkg-repo": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz", @@ -14128,21 +14242,6 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", @@ -15405,20 +15504,6 @@ "node": ">=6.11.5" } }, - "node_modules/loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, "node_modules/locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", @@ -16546,24 +16631,6 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, - "node_modules/merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "dependencies": { - "source-map": "^0.6.1" - } - }, - "node_modules/merge-source-map/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -21427,6 +21494,20 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/text-extensions": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", @@ -24734,6 +24815,70 @@ "integrity": "sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==", "dev": true }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } + } + }, "@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -24844,19 +24989,6 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "@jsdevtools/coverage-istanbul-loader": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz", - "integrity": "sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA==", - "dev": true, - "requires": { - "convert-source-map": "^1.7.0", - "istanbul-lib-instrument": "^4.0.3", - "loader-utils": "^2.0.0", - "merge-source-map": "^1.1.0", - "schema-utils": "^2.7.0" - } - }, "@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", @@ -26886,6 +27018,34 @@ "object.assign": "^4.1.0" } }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "dependencies": { + "istanbul-lib-instrument": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + } + } + }, "babel-plugin-polyfill-corejs2": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", @@ -31285,6 +31445,12 @@ "has-symbols": "^1.0.1" } }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, "get-pkg-repo": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz", @@ -34570,18 +34736,6 @@ "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", "dev": true }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - } - }, "istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", @@ -35534,17 +35688,6 @@ "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", "dev": true }, - "loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, "locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", @@ -36476,23 +36619,6 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, - "merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "requires": { - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -40338,6 +40464,17 @@ } } }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, "text-extensions": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", diff --git a/package.json b/package.json index 19748040d79..58898eb0110 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,6 @@ }, "devDependencies": { "@babel/eslint-parser": "^7.16.5", - "@jsdevtools/coverage-istanbul-loader": "^3.0.3", "@wdio/browserstack-service": "^7.16.0", "@wdio/cli": "^7.5.2", "@wdio/concise-reporter": "^7.5.2", @@ -46,6 +45,7 @@ "ajv": "6.12.3", "assert": "^2.0.0", "babel-loader": "^8.0.5", + "babel-plugin-istanbul": "^6.1.1", "babel-register": "^6.26.0", "body-parser": "^1.19.0", "chai": "^4.2.0", @@ -114,9 +114,9 @@ }, "dependencies": { "@babel/core": "^7.16.7", + "@babel/plugin-transform-runtime": "^7.18.9", "@babel/preset-env": "^7.16.8", "@babel/runtime": "^7.18.9", - "@babel/plugin-transform-runtime": "^7.18.9", "core-js": "^3.13.0", "core-js-pure": "^3.13.0", "criteo-direct-rsa-validate": "^1.1.0", From 335a90782bc5f709a039b5b6a85811098e0b544f Mon Sep 17 00:00:00 2001 From: CPMStar Date: Mon, 15 Aug 2022 09:20:48 -0700 Subject: [PATCH 065/246] CPMStar Bid Adapter : change request method to post & added data to endpoint (#8810) * added cpmstarBidAdapter with meta.advertiserDomains support * fix linting * Updated modules/cpmstarBidAdapter: Changed request method to post and included the mediaType.video and adUnitCode data to the request body. Co-authored-by: Chris Huie Co-authored-by: = <=> --- modules/cpmstarBidAdapter.js | 32 ++++++++++++++------- test/spec/modules/cpmstarBidAdapter_spec.js | 2 +- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/modules/cpmstarBidAdapter.js b/modules/cpmstarBidAdapter.js index 6e32c5c4713..e8e1a846f35 100755 --- a/modules/cpmstarBidAdapter.js +++ b/modules/cpmstarBidAdapter.js @@ -1,4 +1,5 @@ -import { deepAccess, getBidIdParameter, logWarn, logError } from '../src/utils.js'; + +import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO, BANNER } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; @@ -25,11 +26,11 @@ export const spec = { getMediaType: function (bidRequest) { if (bidRequest == null) return BANNER; - return !deepAccess(bidRequest, 'mediaTypes.video') ? BANNER : VIDEO; + return !utils.deepAccess(bidRequest, 'mediaTypes.video') ? BANNER : VIDEO; }, getPlayerSize: function (bidRequest) { - var playerSize = deepAccess(bidRequest, 'mediaTypes.video.playerSize'); + var playerSize = utils.deepAccess(bidRequest, 'mediaTypes.video.playerSize'); if (playerSize == null) return [640, 440]; if (playerSize[0] != null) playerSize = playerSize[0]; if (playerSize == null || playerSize[0] == null || playerSize[1] == null) return [640, 440]; @@ -46,15 +47,14 @@ export const spec = { for (var i = 0; i < validBidRequests.length; i++) { var bidRequest = validBidRequests[i]; - // TODO: is 'page' the right value here? - var referer = encodeURIComponent(bidderRequest.refererInfo.page); - var e = getBidIdParameter('endpoint', bidRequest.params); + var referer = encodeURIComponent(bidderRequest.refererInfo.referer); + var e = utils.getBidIdParameter('endpoint', bidRequest.params); var ENDPOINT = e == 'dev' ? ENDPOINT_DEV : e == 'staging' ? ENDPOINT_STAGING : ENDPOINT_PRODUCTION; var mediaType = spec.getMediaType(bidRequest); var playerSize = spec.getPlayerSize(bidRequest); var videoArgs = '&fv=0' + (playerSize ? ('&w=' + playerSize[0] + '&h=' + playerSize[1]) : ''); var url = ENDPOINT + '?media=' + mediaType + (mediaType == VIDEO ? videoArgs : '') + - '&json=c_b&mv=1&poolid=' + getBidIdParameter('placementId', bidRequest.params) + + '&json=c_b&mv=1&poolid=' + utils.getBidIdParameter('placementId', bidRequest.params) + '&reachedTop=' + encodeURIComponent(bidderRequest.refererInfo.reachedTop) + '&requestid=' + bidRequest.bidId + '&referer=' + encodeURIComponent(referer); @@ -93,10 +93,20 @@ export const spec = { url += '&tfcd=' + (config.getConfig('coppa') ? 1 : 0); } + let body = {}; + let adUnitCode = bidRequest.adUnitCode; + if (adUnitCode) { + body.adUnitCode = adUnitCode; + } + if (mediaType == VIDEO) { + body.video = utils.deepAccess(bidRequest, 'mediaTypes.video'); + } + requests.push({ - method: 'GET', + method: 'POST', url: url, bidRequest: bidRequest, + data: body }); } @@ -117,13 +127,13 @@ export const spec = { var raw = serverResponse.body[i]; var rawBid = raw.creatives[0]; if (!rawBid) { - logWarn('cpmstarBidAdapter: server response failed check'); + utils.logWarn('cpmstarBidAdapter: server response failed check'); return; } var cpm = (parseFloat(rawBid.cpm) || 0); if (!cpm) { - logWarn('cpmstarBidAdapter: server response failed check. Missing cpm') + utils.logWarn('cpmstarBidAdapter: server response failed check. Missing cpm') return; } @@ -156,7 +166,7 @@ export const spec = { bidResponse.mediaType = VIDEO; bidResponse.vastXml = rawBid.creativemacros.HTML5VID_VASTSTRING; } else { - return logError('bad response', rawBid); + return utils.logError('bad response', rawBid); } bidResponses.push(bidResponse); diff --git a/test/spec/modules/cpmstarBidAdapter_spec.js b/test/spec/modules/cpmstarBidAdapter_spec.js index dd076d060b9..285fca9690a 100755 --- a/test/spec/modules/cpmstarBidAdapter_spec.js +++ b/test/spec/modules/cpmstarBidAdapter_spec.js @@ -14,7 +14,7 @@ const valid_bid_requests = [{ const bidderRequest = { refererInfo: { - page: 'referer', + referer: 'referer', reachedTop: false, } }; From 6b0021c02c80b32c0e9b688064f8ff1790270057 Mon Sep 17 00:00:00 2001 From: Jeremy Sadwith Date: Mon, 15 Aug 2022 12:35:10 -0400 Subject: [PATCH 066/246] Kargo Bid Adapter: Pull Page URL from refererInfo (#8825) * Kargo Bid Adapter: Use currency from Bid Response * Kargo Bid Adapter: Fix failed test * Kargo Bid Adapter: adding media type to bid response, supporting vastXml response (#8426) * kargo adapter - adding mediaType to bid response, conditionally set vastXml field * kargo adapter - updating tests * Kargo Bid Adapter: onTimeout Support (#6) * Adding additional param * Adding response time function * Remove debug * Updating response time log to be set by bid response * Adding screen width/height to request * Test fix * Test fix * Removing interpretResponse signaling * Simplifying send data function * Kargo Analytics Adapter: Update with bid response time support * Renaming event route for auction data * Reset bid response data sent bool * Using array to store logged auctions * Update to use timeToResponse * Test fix * Kargo: Pull page URL from refererInfo * Test fix: Page URL * Removing extra bracket Co-authored-by: Wei Wong Co-authored-by: Andy Rusiecki --- modules/kargoBidAdapter.js | 9 ++++----- test/spec/modules/kargoBidAdapter_spec.js | 13 +++++++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index 72342903d87..deeb1cb8630 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -60,7 +60,7 @@ export const spec = { height: window.screen.height }, prebidRawBidRequests: validBidRequests - }, spec._getAllMetadata(tdid, bidderRequest.uspConsent, bidderRequest.gdprConsent)); + }, spec._getAllMetadata(bidderRequest, tdid)); const encodedParams = encodeURIComponent(JSON.stringify(transformedParams)); return Object.assign({}, bidderRequest, { method: 'GET', @@ -215,11 +215,10 @@ export const spec = { return crb.clientId; }, - _getAllMetadata(tdid, usp, gdpr) { + _getAllMetadata(bidderRequest, tdid) { return { - userIDs: spec._getUserIds(tdid, usp, gdpr), - // TODO: this should probably look at refererInfo - pageURL: window.location.href, + userIDs: spec._getUserIds(tdid, bidderRequest.uspConsent, bidderRequest.gdprConsent), + pageURL: bidderRequest.refererInfo && bidderRequest.refererInfo.page, rawCRB: storage.getCookie('krg_crb'), rawCRBLocalStorage: spec._getLocalStorageSafely('krg_crb') }; diff --git a/test/spec/modules/kargoBidAdapter_spec.js b/test/spec/modules/kargoBidAdapter_spec.js index e900b91ed11..169ee520f33 100644 --- a/test/spec/modules/kargoBidAdapter_spec.js +++ b/test/spec/modules/kargoBidAdapter_spec.js @@ -276,7 +276,7 @@ describe('kargo adapter tests', function () { optOut: false, usp: '1---' }, - pageURL: window.location.href, + pageURL: 'https://www.prebid.org', prebidRawBidRequests: [ { bidId: 1, @@ -327,10 +327,19 @@ describe('kargo adapter tests', function () { if (excludeTdid) { delete clonedBids[0].userId.tdid; } - var payload = { timeout: 200, uspConsent: '1---', foo: 'bar' }; + var payload = { + timeout: 200, + uspConsent: '1---', + foo: 'bar', + refererInfo: { + page: 'https://www.prebid.org', + }, + }; + if (gdpr) { payload['gdprConsent'] = gdpr } + var request = spec.buildRequests(clonedBids, payload); expected.sessionId = getSessionId(); sessionIds.push(expected.sessionId); From 6e36e363aa843603a8b7cb29da3242d84e5f4b9d Mon Sep 17 00:00:00 2001 From: caseywhitmire <60086994+caseywhitmire@users.noreply.github.com> Date: Mon, 15 Aug 2022 12:33:13 -0700 Subject: [PATCH 067/246] Various Files: Fix LGTM trailing semi-colon (#8830) * Update showheroes-bsBidAdapter.js * Update adyoulikeBidAdapter.js * Update sonobiBidAdapter.js * Update mediakeysBidAdapter.js * Update revcontentBidAdapter.js * Update vidazooBidAdapter.js * Update tpmnBidAdapter.js * Update yandexBidAdapter.js * Update yieldoneBidAdapter.js * Update karma.conf.maker.js * Update trustpidSystem.js * Update iqmBidAdapter.js * Update yieldmoBidAdapter.js * Update tappxBidAdapter.js * Update e_volutionBidAdapter.js * Update adrelevantisBidAdapter.js * Update goldbachBidAdapter.js * Update Renderer.js * Update priceFloors.js * Update bizzclickBidAdapter.js * Update nobidBidAdapter.js * Update openxBidAdapter.js * Update yahoosspBidAdapter.js * Update rtbhouseBidAdapter.js * Update admixerBidAdapter.js * Update gamoshiBidAdapter.js * Update pubxaiAnalyticsAdapter.js * Update impactifyBidAdapter.js * Update pubwiseAnalyticsAdapter.js * Update sizeMappingV2.js * Update pubwiseBidAdapter.js * Update theAdxBidAdapter.js * Update staqAnalyticsAdapter.js * Update adtelligentBidAdapter.js * Update novatiqIdSystem.js * Update cwireBidAdapter.js * Update datablocksBidAdapter.js * Update gothamadsBidAdapter.js * Update krushmediaBidAdapter.js * Update vidoomyBidAdapter.js * Update adtrueBidAdapter.js * Update userSync.js * Update targetVideoBidAdapter.js * Update open8BidAdapter.js * Update gumgumBidAdapter.js * Update mgidBidAdapter.js * Update inskinBidAdapter.js * Update adxpremiumAnalyticsAdapter.js * Update emx_digitalBidAdapter.js * Update betweenBidAdapter.js * Update pixfutureBidAdapter.js * Update winrBidAdapter.js * Update luponmediaBidAdapter.js * Update interactiveOffersBidAdapter.js * Update intentIqIdSystem.js * Update lkqdBidAdapter.js * Update loganBidAdapter.js * Update dfpAdServerVideo.js * Update targeting.js * Update livewrappedAnalyticsAdapter.js * Update adnowBidAdapter.js * Update cleanmedianetBidAdapter.js * Update sonobiAnalyticsAdapter.js * Update apstreamBidAdapter.js * Update quantcastIdSystem.js * Update prebidmanagerAnalyticsAdapter.js * Update brightMountainMediaBidAdapter.js * Update dgkeywordRtdProvider.js * Update h12mediaBidAdapter.js * Update permutiveRtdProvider_example.html * Update dspxBidAdapter.js * Update radsBidAdapter.js * Update id5AnalyticsAdapter.js * Update richaudienceBidAdapter.js * Update cpmstarBidAdapter.js * Update vdoaiBidAdapter.js * Update smaatoBidAdapter.js * Update adtargetBidAdapter.js * Update openwebBidAdapter.js * Update malltvBidAdapter.js * Update gjirafaBidAdapter.js * Update integr8BidAdapter.js * Update fabrickIdSystem.js * Update sizeMapping.js * Update adpod.js * Update adriverBidAdapter.js * Update trionBidAdapter.js * Update ebdrBidAdapter.js * Update getintentBidAdapter.js * Update pubxBidAdapter.js * Update schain.js * Update underdogmediaBidAdapter.js * Update bidfactory.js * eslint: update semi-colon rule Missing semi-colons will now cause an error. * Update .eslintrc.js --- .../gpt/permutiveRtdProvider_example.html | 2 +- karma.conf.maker.js | 2 +- modules/admixerBidAdapter.js | 2 +- modules/adnowBidAdapter.js | 2 +- modules/adpod.js | 2 +- modules/adrelevantisBidAdapter.js | 4 ++-- modules/adriverBidAdapter.js | 2 +- modules/adtargetBidAdapter.js | 2 +- modules/adtelligentBidAdapter.js | 2 +- modules/adtrueBidAdapter.js | 2 +- modules/adxpremiumAnalyticsAdapter.js | 2 +- modules/adyoulikeBidAdapter.js | 2 +- modules/apstreamBidAdapter.js | 4 ++-- modules/betweenBidAdapter.js | 2 +- modules/bizzclickBidAdapter.js | 2 +- modules/brightMountainMediaBidAdapter.js | 2 +- modules/cleanmedianetBidAdapter.js | 2 +- modules/cpmstarBidAdapter.js | 6 +++--- modules/cwireBidAdapter.js | 2 +- modules/datablocksBidAdapter.js | 2 +- modules/dfpAdServerVideo.js | 4 ++-- modules/dgkeywordRtdProvider.js | 2 +- modules/dspxBidAdapter.js | 2 +- modules/e_volutionBidAdapter.js | 6 +++--- modules/ebdrBidAdapter.js | 2 +- modules/emx_digitalBidAdapter.js | 2 +- modules/fabrickIdSystem.js | 4 ++-- modules/gamoshiBidAdapter.js | 2 +- modules/getintentBidAdapter.js | 2 +- modules/gjirafaBidAdapter.js | 4 ++-- modules/goldbachBidAdapter.js | 16 ++++++++-------- modules/gothamadsBidAdapter.js | 4 ++-- modules/gumgumBidAdapter.js | 6 +++--- modules/h12mediaBidAdapter.js | 2 +- modules/id5AnalyticsAdapter.js | 2 +- modules/impactifyBidAdapter.js | 2 +- modules/inskinBidAdapter.js | 2 +- modules/integr8BidAdapter.js | 4 ++-- modules/intentIqIdSystem.js | 4 ++-- modules/interactiveOffersBidAdapter.js | 2 +- modules/iqmBidAdapter.js | 2 +- modules/krushmediaBidAdapter.js | 4 ++-- modules/livewrappedAnalyticsAdapter.js | 6 +++--- modules/lkqdBidAdapter.js | 6 +++--- modules/loganBidAdapter.js | 4 ++-- modules/luponmediaBidAdapter.js | 6 +++--- modules/malltvBidAdapter.js | 4 ++-- modules/mediakeysBidAdapter.js | 4 ++-- modules/mgidBidAdapter.js | 4 ++-- modules/nobidBidAdapter.js | 4 ++-- modules/novatiqIdSystem.js | 2 +- modules/open8BidAdapter.js | 2 +- modules/openwebBidAdapter.js | 2 +- modules/openxBidAdapter.js | 6 +++--- modules/pixfutureBidAdapter.js | 2 +- modules/prebidmanagerAnalyticsAdapter.js | 2 +- modules/priceFloors.js | 2 +- modules/pubwiseAnalyticsAdapter.js | 2 +- modules/pubwiseBidAdapter.js | 6 +++--- modules/pubxBidAdapter.js | 2 +- modules/pubxaiAnalyticsAdapter.js | 4 ++-- modules/quantcastIdSystem.js | 2 +- modules/radsBidAdapter.js | 2 +- modules/revcontentBidAdapter.js | 2 +- modules/richaudienceBidAdapter.js | 2 +- modules/rtbhouseBidAdapter.js | 2 +- modules/schain.js | 2 +- modules/showheroes-bsBidAdapter.js | 2 +- modules/sizeMappingV2.js | 2 +- modules/smaatoBidAdapter.js | 4 ++-- modules/sonobiAnalyticsAdapter.js | 2 +- modules/sonobiBidAdapter.js | 6 +++--- modules/staqAnalyticsAdapter.js | 2 +- modules/tappxBidAdapter.js | 4 ++-- modules/targetVideoBidAdapter.js | 2 +- modules/theAdxBidAdapter.js | 4 ++-- modules/tpmnBidAdapter.js | 2 +- modules/trionBidAdapter.js | 4 ++-- modules/trustpidSystem.js | 2 +- modules/underdogmediaBidAdapter.js | 2 +- modules/vdoaiBidAdapter.js | 2 +- modules/vidazooBidAdapter.js | 2 +- modules/vidoomyBidAdapter.js | 2 +- modules/winrBidAdapter.js | 2 +- modules/yahoosspBidAdapter.js | 4 ++-- modules/yandexBidAdapter.js | 2 +- modules/yieldmoBidAdapter.js | 6 +++--- modules/yieldoneBidAdapter.js | 4 ++-- src/Renderer.js | 2 +- src/bidfactory.js | 2 +- src/sizeMapping.js | 2 +- src/targeting.js | 2 +- src/userSync.js | 2 +- 93 files changed, 141 insertions(+), 141 deletions(-) diff --git a/integrationExamples/gpt/permutiveRtdProvider_example.html b/integrationExamples/gpt/permutiveRtdProvider_example.html index dbb4d2af0d6..118cc678726 100644 --- a/integrationExamples/gpt/permutiveRtdProvider_example.html +++ b/integrationExamples/gpt/permutiveRtdProvider_example.html @@ -23,7 +23,7 @@ } } - setLocalStorageData() + setLocalStorageData(); var div_1_sizes = [ [300, 250], diff --git a/karma.conf.maker.js b/karma.conf.maker.js index 81930f42544..fd8f2e6fdd8 100644 --- a/karma.conf.maker.js +++ b/karma.conf.maker.js @@ -176,7 +176,7 @@ module.exports = function(codeCoverage, browserstack, watchMode, file, disableFe concurrency: 6, plugins: plugins - } + }; // To ensure that, we are able to run single spec file // here we are adding preprocessors, when file is passed diff --git a/modules/admixerBidAdapter.js b/modules/admixerBidAdapter.js index b9cde172ebe..8fcee60f9f9 100644 --- a/modules/admixerBidAdapter.js +++ b/modules/admixerBidAdapter.js @@ -52,7 +52,7 @@ export const spec = { consentString: bidderRequest.gdprConsent.consentString, // will check if the gdprApplies field was populated with a boolean value (ie from page config). If it's undefined, then default to true gdprApplies: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : true - } + }; } if (bidderRequest.uspConsent) { payload.uspConsent = bidderRequest.uspConsent; diff --git a/modules/adnowBidAdapter.js b/modules/adnowBidAdapter.js index 484b6202479..f83dbf68a1f 100644 --- a/modules/adnowBidAdapter.js +++ b/modules/adnowBidAdapter.js @@ -67,7 +67,7 @@ export const spec = { if (mediaType === BANNER) { data.sizes = parseSizesInput( req.mediaTypes && req.mediaTypes.banner && req.mediaTypes.banner.sizes - ).join('|') + ).join('|'); } else { data.width = data.height = 200; diff --git a/modules/adpod.js b/modules/adpod.js index b7c459fd66f..f1ab4bd2ef1 100644 --- a/modules/adpod.js +++ b/modules/adpod.js @@ -243,7 +243,7 @@ export function callPrebidCacheHook(fn, auctionInstance, bidResponse, afterBidAd let brandCategoryExclusion = config.getConfig('adpod.brandCategoryExclusion'); let adServerCatId = deepAccess(bidResponse, 'meta.adServerCatId'); if (!adServerCatId && brandCategoryExclusion) { - logWarn('Detected a bid without meta.adServerCatId while setConfig({adpod.brandCategoryExclusion}) was enabled. This bid has been rejected:', bidResponse) + logWarn('Detected a bid without meta.adServerCatId while setConfig({adpod.brandCategoryExclusion}) was enabled. This bid has been rejected:', bidResponse); afterBidAdded(); } else { if (config.getConfig('adpod.deferCaching') === false) { diff --git a/modules/adrelevantisBidAdapter.js b/modules/adrelevantisBidAdapter.js index 193c92cbb19..40cfe18f025 100644 --- a/modules/adrelevantisBidAdapter.js +++ b/modules/adrelevantisBidAdapter.js @@ -209,7 +209,7 @@ export const spec = { return params; } -} +}; function isPopulatedArray(arr) { return !!(isArray(arr) && arr.length > 0); @@ -444,7 +444,7 @@ function bidToTag(bid) { tag.cpm = bid.params.cpm; } tag.allow_smaller_sizes = bid.params.allowSmallerSizes || false; - tag.use_pmt_rule = bid.params.usePaymentRule || false + tag.use_pmt_rule = bid.params.usePaymentRule || false; tag.prebid = true; tag.disable_psa = true; if (bid.params.position) { diff --git a/modules/adriverBidAdapter.js b/modules/adriverBidAdapter.js index 5ab417520e9..e95f83d2c7b 100644 --- a/modules/adriverBidAdapter.js +++ b/modules/adriverBidAdapter.js @@ -32,7 +32,7 @@ export const spec = { let timeout = null; if (bidderRequest) { - timeout = bidderRequest.timeout + timeout = bidderRequest.timeout; } const payload = { diff --git a/modules/adtargetBidAdapter.js b/modules/adtargetBidAdapter.js index c4f2bc65655..89ba4878acf 100644 --- a/modules/adtargetBidAdapter.js +++ b/modules/adtargetBidAdapter.js @@ -137,7 +137,7 @@ function bidToTag(bidRequests, adapterRequest) { tag.UserIds = deepAccess(bidRequests[0], 'userId'); } - const bids = [] + const bids = []; for (let i = 0, length = bidRequests.length; i < length; i++) { const bid = prepareBidRequests(bidRequests[i]); diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index 03d4e634062..2ee5b0f72a3 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -190,7 +190,7 @@ function bidToTag(bidRequests, adapterRequest) { } // end publisher env - const bids = [] + const bids = []; for (let i = 0, length = bidRequests.length; i < length; i++) { const bid = prepareBidRequests(bidRequests[i]); diff --git a/modules/adtrueBidAdapter.js b/modules/adtrueBidAdapter.js index b0aafc3ae4e..2ec5cd59e1d 100644 --- a/modules/adtrueBidAdapter.js +++ b/modules/adtrueBidAdapter.js @@ -45,7 +45,7 @@ const VIDEO_CUSTOM_PARAMS = { 'placement': DATA_TYPES.NUMBER, 'minbitrate': DATA_TYPES.NUMBER, 'maxbitrate': DATA_TYPES.NUMBER -} +}; const NATIVE_ASSETS = { 'TITLE': {ID: 1, KEY: 'title', TYPE: 0}, diff --git a/modules/adxpremiumAnalyticsAdapter.js b/modules/adxpremiumAnalyticsAdapter.js index 456180a964b..9161c6338f4 100644 --- a/modules/adxpremiumAnalyticsAdapter.js +++ b/modules/adxpremiumAnalyticsAdapter.js @@ -263,7 +263,7 @@ adxpremiumAnalyticsAdapter.enableAnalytics = function (config) { } adxpremiumAnalyticsAdapter.originEnableAnalytics(config); // call the base class function -} +}; adapterManager.registerAnalyticsAdapter({ adapter: adxpremiumAnalyticsAdapter, diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 0f373749a7f..b0b132abd1c 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -444,7 +444,7 @@ function getNativeAssets(response, nativeConfig) { /* Create bid from response */ function createBid(response, bidRequests) { if (!response || (!response.Ad && !response.Native && !response.Vast)) { - return + return; } const request = bidRequests && bidRequests[response.BidID]; diff --git a/modules/apstreamBidAdapter.js b/modules/apstreamBidAdapter.js index 5ae0d5c56fd..30abb17bfde 100644 --- a/modules/apstreamBidAdapter.js +++ b/modules/apstreamBidAdapter.js @@ -267,7 +267,7 @@ var dsuModule = (function() { return { readOrCreateDsu: readOrCreateDsu - } + }; })(); function serializeSizes(sizes) { @@ -343,7 +343,7 @@ function getBids(bids) { const bidId = bid.bidId; let mediaType = ''; - const mediaTypes = Object.keys(bid.mediaTypes) + const mediaTypes = Object.keys(bid.mediaTypes); switch (mediaTypes[0]) { case 'video': mediaType = 'v'; diff --git a/modules/betweenBidAdapter.js b/modules/betweenBidAdapter.js index 9e57d0f5cd3..2ca829b796b 100644 --- a/modules/betweenBidAdapter.js +++ b/modules/betweenBidAdapter.js @@ -90,7 +90,7 @@ export const spec = { } } - requests.push({data: params}) + requests.push({data: params}); }) return { method: 'POST', diff --git a/modules/bizzclickBidAdapter.js b/modules/bizzclickBidAdapter.js index 7b32ffd84b0..4ef2b6dd9f8 100644 --- a/modules/bizzclickBidAdapter.js +++ b/modules/bizzclickBidAdapter.js @@ -256,7 +256,7 @@ const addNativeParameters = bidRequest => { wmin = sizes[0]; hmin = sizes[1]; } - asset[props.name] = {} + asset[props.name] = {}; if (bidParams.len) asset[props.name]['len'] = bidParams.len; if (props.type) asset[props.name]['type'] = props.type; if (wmin) asset[props.name]['wmin'] = wmin; diff --git a/modules/brightMountainMediaBidAdapter.js b/modules/brightMountainMediaBidAdapter.js index bfe1e8ecb29..6db06744c24 100644 --- a/modules/brightMountainMediaBidAdapter.js +++ b/modules/brightMountainMediaBidAdapter.js @@ -99,7 +99,7 @@ export const spec = { let response; try { - response = serverResponse.body + response = serverResponse.body; bid = response.seatbid[0].bid[0]; } catch (e) { response = null; diff --git a/modules/cleanmedianetBidAdapter.js b/modules/cleanmedianetBidAdapter.js index f7d74c0df64..ba72eec3d95 100644 --- a/modules/cleanmedianetBidAdapter.js +++ b/modules/cleanmedianetBidAdapter.js @@ -104,7 +104,7 @@ export const spec = { ext: { consent: bidderRequest.gdprConsent.consentString } - } + }; } const imp = { diff --git a/modules/cpmstarBidAdapter.js b/modules/cpmstarBidAdapter.js index e8e1a846f35..64fe85fdbbc 100755 --- a/modules/cpmstarBidAdapter.js +++ b/modules/cpmstarBidAdapter.js @@ -73,7 +73,7 @@ export const spec = { fixedEncodeURIComponent(node.name || '') + ',' + fixedEncodeURIComponent(node.domain || ''); } - url += '&schain=' + schainString + url += '&schain=' + schainString; } if (bidderRequest.gdprConsent) { @@ -133,7 +133,7 @@ export const spec = { var cpm = (parseFloat(rawBid.cpm) || 0); if (!cpm) { - utils.logWarn('cpmstarBidAdapter: server response failed check. Missing cpm') + utils.logWarn('cpmstarBidAdapter: server response failed check. Missing cpm'); return; } @@ -152,7 +152,7 @@ export const spec = { }; if (rawBid.hasOwnProperty('dealId')) { - bidResponse.dealId = rawBid.dealId + bidResponse.dealId = rawBid.dealId; } if (mediaType == BANNER && rawBid.code) { diff --git a/modules/cwireBidAdapter.js b/modules/cwireBidAdapter.js index 47f3020fec3..a11899609bc 100644 --- a/modules/cwireBidAdapter.js +++ b/modules/cwireBidAdapter.js @@ -303,5 +303,5 @@ export const spec = { return bidResponses; }, -} +}; registerBidder(spec); diff --git a/modules/datablocksBidAdapter.js b/modules/datablocksBidAdapter.js index f0e0c32990a..4e21e08ba57 100644 --- a/modules/datablocksBidAdapter.js +++ b/modules/datablocksBidAdapter.js @@ -361,7 +361,7 @@ export const spec = { stack: bidderRequest.refererInfo.stack, timeout: config.getConfig('bidderTimeout') }, - } + }; // ADD REF URL IF FOUND if (self === top && document.referrer) { diff --git a/modules/dfpAdServerVideo.js b/modules/dfpAdServerVideo.js index 37f038d2a67..072715b4bd6 100644 --- a/modules/dfpAdServerVideo.js +++ b/modules/dfpAdServerVideo.js @@ -91,7 +91,7 @@ export function buildDfpVideoUrl(options) { }; const urlSearchComponent = urlComponents.search; - const urlSzParam = urlSearchComponent && urlSearchComponent.sz + const urlSzParam = urlSearchComponent && urlSearchComponent.sz; if (urlSzParam) { derivedParams.sz = urlSzParam + '|' + derivedParams.sz; } @@ -186,7 +186,7 @@ export function buildAdpodVideoUrl({code, params, callback} = {}) { let initialValue = { [adpodUtils.TARGETING_KEY_PB_CAT_DUR]: undefined, [adpodUtils.TARGETING_KEY_CACHE_ID]: undefined - } + }; let customParams = {}; if (targeting[code]) { customParams = targeting[code].reduce((acc, curValue) => { diff --git a/modules/dgkeywordRtdProvider.js b/modules/dgkeywordRtdProvider.js index 171766ac4ce..cea33014144 100644 --- a/modules/dgkeywordRtdProvider.js +++ b/modules/dgkeywordRtdProvider.js @@ -48,7 +48,7 @@ export function getDgKeywordsAndSet(reqBidsConfigObj, callback, moduleConfig, us keywords['opectx'] = res['t']; } if (Object.keys(keywords).length > 0) { - const targetBidKeys = {} + const targetBidKeys = {}; for (let bid of setKeywordTargetBidders) { // set keywords to params bid.params.keywords = keywords; diff --git a/modules/dspxBidAdapter.js b/modules/dspxBidAdapter.js index 6af0236d3bb..8850eb282b5 100644 --- a/modules/dspxBidAdapter.js +++ b/modules/dspxBidAdapter.js @@ -99,7 +99,7 @@ export const spec = { method: 'GET', url: endpoint, data: objectToQueryString(payload), - } + }; }); }, interpretResponse: function(serverResponse, bidRequest) { diff --git a/modules/e_volutionBidAdapter.js b/modules/e_volutionBidAdapter.js index 8e829a39a57..5f1b46ff9eb 100644 --- a/modules/e_volutionBidAdapter.js +++ b/modules/e_volutionBidAdapter.js @@ -72,7 +72,7 @@ export const spec = { let location; // TODO: this odd try-catch block was copied in several adapters; it doesn't seem to be correct for cross-origin try { - location = new URL(bidderRequest.refererInfo.page) + location = new URL(bidderRequest.refererInfo.page); winTop = window.top; } catch (e) { location = winTop.location; @@ -93,7 +93,7 @@ export const spec = { request.ccpa = bidderRequest.uspConsent; } if (bidderRequest.gdprConsent) { - request.gdpr = bidderRequest.gdprConsent + request.gdpr = bidderRequest.gdprConsent; } } const len = validBidRequests.length; @@ -106,7 +106,7 @@ export const spec = { bidId: bid.bidId, bidfloor: getBidFloor(bid), eids: [] - } + }; if (bid.userId) { getUserId(placement.eids, bid.userId.id5id, 'id5-sync.com'); diff --git a/modules/ebdrBidAdapter.js b/modules/ebdrBidAdapter.js index 62a3b171b74..a7b1991df9b 100644 --- a/modules/ebdrBidAdapter.js +++ b/modules/ebdrBidAdapter.js @@ -31,7 +31,7 @@ export const spec = { h: whArr[1] }, bidfloor: bidFloor - }) + }); ebdrReq[bid.bidId] = {mediaTypes: _mediaTypes, w: whArr[0], h: whArr[1] diff --git a/modules/emx_digitalBidAdapter.js b/modules/emx_digitalBidAdapter.js index d904ebda9c8..eb69e76a837 100644 --- a/modules/emx_digitalBidAdapter.js +++ b/modules/emx_digitalBidAdapter.js @@ -287,7 +287,7 @@ export const spec = { emxData = emxAdapter.getGdpr(bidderRequest, Object.assign({}, emxData)); emxData = emxAdapter.getSupplyChain(bidderRequest, Object.assign({}, emxData)); if (bidderRequest && bidderRequest.uspConsent) { - emxData.us_privacy = bidderRequest.uspConsent + emxData.us_privacy = bidderRequest.uspConsent; } // adding eid support diff --git a/modules/fabrickIdSystem.js b/modules/fabrickIdSystem.js index 24eac8517b0..dfc2985b0db 100644 --- a/modules/fabrickIdSystem.js +++ b/modules/fabrickIdSystem.js @@ -70,7 +70,7 @@ export const fabrickIdSubmodule = { } } // pull off the trailing & - url = url.slice(0, -1) + url = url.slice(0, -1); const referer = _getRefererInfo(configParams); const refs = new Map(); _setReferrer(refs, referer.topmostLocation); @@ -174,7 +174,7 @@ export function appendUrl(url, paramName, s, configParams) { s = s.substring(0, thisMaxRefLen - 2); } } - return `${url}${s}` + return `${url}${s}`; } else { return url; } diff --git a/modules/gamoshiBidAdapter.js b/modules/gamoshiBidAdapter.js index 26afe6e6b87..ae320db1251 100644 --- a/modules/gamoshiBidAdapter.js +++ b/modules/gamoshiBidAdapter.js @@ -259,7 +259,7 @@ export const spec = { let gdpr = gdprApplies ? 1 : 0; if (gdprApplies && gdprConsent.consentString) { - consentString = encodeURIComponent(gdprConsent.consentString) + consentString = encodeURIComponent(gdprConsent.consentString); } if (uspConsent) { diff --git a/modules/getintentBidAdapter.js b/modules/getintentBidAdapter.js index 98659cc25e2..25322d81f9b 100644 --- a/modules/getintentBidAdapter.js +++ b/modules/getintentBidAdapter.js @@ -97,7 +97,7 @@ export const spec = { return bids; } -} +}; function buildUrl(bid) { return 'https://' + BID_HOST + (bid.is_video ? BID_VIDEO_PATH : BID_BANNER_PATH); diff --git a/modules/gjirafaBidAdapter.js b/modules/gjirafaBidAdapter.js index af70c0c67f0..91ed5c9b3fb 100644 --- a/modules/gjirafaBidAdapter.js +++ b/modules/gjirafaBidAdapter.js @@ -74,7 +74,7 @@ export const spec = { placements: placements, contents: contents, data: data - } + }; return [{ method: 'POST', @@ -113,7 +113,7 @@ export const spec = { } return bidResponses; } -} +}; /** * Generate size param for bid request using sizes array diff --git a/modules/goldbachBidAdapter.js b/modules/goldbachBidAdapter.js index a6e5c59ced2..21c56643353 100644 --- a/modules/goldbachBidAdapter.js +++ b/modules/goldbachBidAdapter.js @@ -207,7 +207,7 @@ export const spec = { payload['iab_support'] = { omidpn: 'Appnexus', omidpv: '$prebid.version$' - } + }; } if (member > 0) { @@ -215,7 +215,7 @@ export const spec = { } if (appDeviceObjBid) { - payload.device = appDeviceObj + payload.device = appDeviceObj; } if (appIdObjBid) { payload.app = appIdObj; @@ -246,7 +246,7 @@ export const spec = { } if (bidderRequest && bidderRequest.uspConsent) { - payload.us_privacy = bidderRequest.uspConsent + payload.us_privacy = bidderRequest.uspConsent; } if (bidderRequest && bidderRequest.refererInfo) { @@ -256,7 +256,7 @@ export const spec = { rd_top: bidderRequest.refererInfo.reachedTop, rd_ifs: bidderRequest.refererInfo.numIframes, rd_stk: bidderRequest.refererInfo.stack.map((url) => encodeURIComponent(url)).join(',') - } + }; payload.referrer_detection = refererinfo; } @@ -441,7 +441,7 @@ export const spec = { reloadViewabilityScriptWithCorrectParameters(bid); } } -} +}; function isPopulatedArray(arr) { return !!(isArray(arr) && arr.length > 0); @@ -459,7 +459,7 @@ function reloadViewabilityScriptWithCorrectParameters(bid) { if (viewJsPayload) { let prebidParams = 'pbjs_adid=' + bid.adId + ';pbjs_auc=' + bid.adUnitCode; - let jsTrackerSrc = getViewabilityScriptUrlFromPayload(viewJsPayload) + let jsTrackerSrc = getViewabilityScriptUrlFromPayload(viewJsPayload); let newJsTrackerSrc = jsTrackerSrc.replace('dom_id=%native_dom_id%', prebidParams); @@ -553,7 +553,7 @@ function formatRequest(payload, bidderRequest) { if (getParameterByName('apn_test').toUpperCase() === 'TRUE' || config.getConfig('apn_test') === true) { options.customHeaders = { 'X-Is-Test': 1 - } + }; } if (payload.tags.length > MAX_IMPS_PER_REQUEST) { @@ -766,7 +766,7 @@ function bidToTag(bid) { tag.code = bid.params.invCode; } tag.allow_smaller_sizes = bid.params.allowSmallerSizes || false; - tag.use_pmt_rule = bid.params.usePaymentRule || false + tag.use_pmt_rule = bid.params.usePaymentRule || false; tag.prebid = true; tag.disable_psa = true; let bidFloor = getBidFloor(bid); diff --git a/modules/gothamadsBidAdapter.js b/modules/gothamadsBidAdapter.js index 37433e547a9..6b549f347bb 100644 --- a/modules/gothamadsBidAdapter.js +++ b/modules/gothamadsBidAdapter.js @@ -139,7 +139,7 @@ export const spec = { * @return {Bid[]} An array of bids which were nested inside the server. */ interpretResponse: (serverResponse) => { - if (!serverResponse || !serverResponse.body) return [] + if (!serverResponse || !serverResponse.body) return []; let GothamAdsResponse = serverResponse.body; let bids = []; @@ -273,7 +273,7 @@ const addNativeParameters = bidRequest => { hmin = sizes[1]; } - asset[props.name] = {} + asset[props.name] = {}; if (bidParams.len) asset[props.name]['len'] = bidParams.len; if (props.type) asset[props.name]['type'] = props.type; diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index b77f82030e1..fe15602c0c0 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -321,7 +321,7 @@ function buildRequests(validBidRequests, bidderRequest) { data.to = to; // ADTS-169 add adUnitCode to requests - if (adUnitCode) data.aun = adUnitCode + if (adUnitCode) data.aun = adUnitCode; // ADTS-134 Retrieve ID envelopes for (const eid in eids) data[eid] = eids[eid]; @@ -374,7 +374,7 @@ function buildRequests(validBidRequests, bidderRequest) { data.pi = 8; } } else { // legacy params - data = { ...data, ...handleLegacyParams(params, sizes) } + data = { ...data, ...handleLegacyParams(params, sizes) }; } if (gdprConsent) { @@ -400,7 +400,7 @@ function buildRequests(validBidRequests, bidderRequest) { url: BID_ENDPOINT, method: 'GET', data: Object.assign(data, _getBrowserParams(topWindowUrl), _getDigiTrustQueryParams(userId)) - }) + }); }); return bids; } diff --git a/modules/h12mediaBidAdapter.js b/modules/h12mediaBidAdapter.js index 29d8bfa5e0f..c58f15c9a81 100644 --- a/modules/h12mediaBidAdapter.js +++ b/modules/h12mediaBidAdapter.js @@ -198,7 +198,7 @@ function getIsHidden(elem) { } catch (o) { return false; } - } while ((m < 250) && (lastElem != null) && (elemHidden === false)) + } while ((m < 250) && (lastElem != null) && (elemHidden === false)); return elemHidden; } diff --git a/modules/id5AnalyticsAdapter.js b/modules/id5AnalyticsAdapter.js index 5ae04eb57ce..d35cdf29fca 100644 --- a/modules/id5AnalyticsAdapter.js +++ b/modules/id5AnalyticsAdapter.js @@ -141,7 +141,7 @@ const ENABLE_FUNCTION = (config) => { logInfo('id5Analytics: Tracking events', _this.eventsToTrack); if (sampling > 0 && _this.random() < (1 / sampling)) { // Init the module only if we got lucky - logInfo('id5Analytics: Selected by sampling. Starting up!') + logInfo('id5Analytics: Selected by sampling. Starting up!'); // Clean start _this.eventBuffer = {}; diff --git a/modules/impactifyBidAdapter.js b/modules/impactifyBidAdapter.js index bb3ad63085c..0717cf43741 100644 --- a/modules/impactifyBidAdapter.js +++ b/modules/impactifyBidAdapter.js @@ -25,7 +25,7 @@ const getDeviceType = () => { return 4; } return 2; -} +}; const createOpenRtbRequest = (validBidRequests, bidderRequest) => { // Create request and set imp bids inside diff --git a/modules/inskinBidAdapter.js b/modules/inskinBidAdapter.js index b76a759a047..f3464765bde 100644 --- a/modules/inskinBidAdapter.js +++ b/modules/inskinBidAdapter.js @@ -184,7 +184,7 @@ export const spec = { bid.currency = 'USD'; bid.creativeId = decision.adId; bid.ttl = 360; - bid.meta = { advertiserDomains: decision.adomain ? decision.adomain : [] } + bid.meta = { advertiserDomains: decision.adomain ? decision.adomain : [] }; bid.netRevenue = true; bidResponses.push(bid); diff --git a/modules/integr8BidAdapter.js b/modules/integr8BidAdapter.js index 3ba68ffb6d6..a85e9b0a55c 100644 --- a/modules/integr8BidAdapter.js +++ b/modules/integr8BidAdapter.js @@ -78,7 +78,7 @@ export const spec = { placements: placements, contents: contents, data: data - } + }; return [{ method: 'POST', @@ -117,7 +117,7 @@ export const spec = { } return bidResponses; } -} +}; /** * Generate size param for bid request using sizes array diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index 1347fa04bd5..563435dee65 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -118,7 +118,7 @@ export const intentIqIdSubmodule = { logError('User ID - intentIqId submodule requires a valid partner to be defined'); return; } - if (!FIRST_PARTY_DATA_KEY.includes(configParams.partner)) { FIRST_PARTY_DATA_KEY += '_' + configParams.partner } + if (!FIRST_PARTY_DATA_KEY.includes(configParams.partner)) { FIRST_PARTY_DATA_KEY += '_' + configParams.partner; } let rrttStrtTime = 0; // Read Intent IQ 1st party id or generate it if none exists @@ -169,7 +169,7 @@ export const intentIqIdSubmodule = { shouldUpdateLs = true; } if (shouldUpdateLs === true) { - partnerData.date = Date.now() + partnerData.date = Date.now(); storeData(FIRST_PARTY_KEY, JSON.stringify(firstPartyData)); storeData(FIRST_PARTY_DATA_KEY, JSON.stringify(partnerData)); } diff --git a/modules/interactiveOffersBidAdapter.js b/modules/interactiveOffersBidAdapter.js index 25dcd4f3cd1..1fad231dff2 100644 --- a/modules/interactiveOffersBidAdapter.js +++ b/modules/interactiveOffersBidAdapter.js @@ -169,7 +169,7 @@ function parseResponseOpenRTBToPrebidjs(openRTBResponse) { mediaType: 'banner', primaryCatId: bid.cat[0] || '', secondaryCatIds: bid.cat - } + }; prebidResponse.push(prebid); }); } diff --git a/modules/iqmBidAdapter.js b/modules/iqmBidAdapter.js index 68b027c1bec..1c36bafd3ce 100644 --- a/modules/iqmBidAdapter.js +++ b/modules/iqmBidAdapter.js @@ -245,7 +245,7 @@ function getSite(bidderRequest) { function _buildVideoORTB(bidRequest) { const videoAdUnit = deepAccess(bidRequest, 'mediaTypes.video'); const videoBidderParams = deepAccess(bidRequest, 'params.video', {}); - const video = {} + const video = {}; const videoParams = { ...videoAdUnit, diff --git a/modules/krushmediaBidAdapter.js b/modules/krushmediaBidAdapter.js index 6c7b75bbce2..876f0ebabc6 100644 --- a/modules/krushmediaBidAdapter.js +++ b/modules/krushmediaBidAdapter.js @@ -57,7 +57,7 @@ export const spec = { let location; // TODO: this odd try-catch block was copied in several adapters; it doesn't seem to be correct for cross-origin try { - location = new URL(bidderRequest.refererInfo.page) + location = new URL(bidderRequest.refererInfo.page); winTop = window.top; } catch (e) { location = winTop.location; @@ -80,7 +80,7 @@ export const spec = { request.ccpa = bidderRequest.uspConsent; } if (bidderRequest.gdprConsent) { - request.gdpr = bidderRequest.gdprConsent + request.gdpr = bidderRequest.gdprConsent; } } diff --git a/modules/livewrappedAnalyticsAdapter.js b/modules/livewrappedAnalyticsAdapter.js index 2721031948d..fe69220e123 100644 --- a/modules/livewrappedAnalyticsAdapter.js +++ b/modules/livewrappedAnalyticsAdapter.js @@ -67,10 +67,10 @@ let livewrappedAnalyticsAdapter = Object.assign(adapter({EMPTYURL, ANALYTICSTYPE auc: bidRequest.auc, buc: bidRequest.buc, lw: bidRequest.lw - } + }; logInfo(bidRequest); - }) + }); logInfo(livewrappedAnalyticsAdapter.requestEvents); break; case CONSTANTS.EVENTS.BID_RESPONSE: @@ -183,7 +183,7 @@ livewrappedAnalyticsAdapter.sendEvents = function() { } ajax(initOptions.endpoint || URL, undefined, JSON.stringify(events), {method: 'POST'}); -} +}; function getAdblockerRecovered() { try { diff --git a/modules/lkqdBidAdapter.js b/modules/lkqdBidAdapter.js index 6d2766ff2f1..1dbe89f5a49 100644 --- a/modules/lkqdBidAdapter.js +++ b/modules/lkqdBidAdapter.js @@ -38,7 +38,7 @@ export const spec = { const UA = navigator.userAgent; const USP = BIDDER_REQUEST.uspConsent || null; // TODO: does the fallback make sense here? - const REFERER = BIDDER_REQUEST?.refererInfo?.domain || window.location.host + const REFERER = BIDDER_REQUEST?.refererInfo?.domain || window.location.host; const BIDDER_GDPR = BIDDER_REQUEST.gdprConsent && BIDDER_REQUEST.gdprConsent.gdprApplies ? 1 : null; const BIDDER_GDPRS = BIDDER_REQUEST.gdprConsent && BIDDER_REQUEST.gdprConsent.consentString ? BIDDER_REQUEST.gdprConsent.consentString : null; @@ -74,7 +74,7 @@ export const spec = { us_privacy: USP } } - } + }; if (isSet(DNT)) { requestData.device.dnt = DNT; @@ -94,7 +94,7 @@ export const spec = { id: bid.params.aid, name: bid.params.appname, bundle: bid.params.bundleid - } + }; if (bid.params.contentId) { requestData.app.content = { diff --git a/modules/loganBidAdapter.js b/modules/loganBidAdapter.js index ec5006b7c8c..7aa82e3046c 100644 --- a/modules/loganBidAdapter.js +++ b/modules/loganBidAdapter.js @@ -72,7 +72,7 @@ export const spec = { request.ccpa = bidderRequest.uspConsent; } if (bidderRequest.gdprConsent) { - request.gdpr = bidderRequest.gdprConsent + request.gdpr = bidderRequest.gdprConsent; } } @@ -85,7 +85,7 @@ export const spec = { schain: bid.schain || {}, bidfloor: getBidFloor(bid) }; - const mediaType = bid.mediaTypes + const mediaType = bid.mediaTypes; if (mediaType && mediaType[BANNER] && mediaType[BANNER].sizes) { placement.sizes = mediaType[BANNER].sizes; diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index 31e2120d364..835c04ba074 100755 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -176,7 +176,7 @@ export const spec = { responses.forEach(csResp => { if (csResp.body && csResp.body.ext && csResp.body.ext.usersyncs) { try { - let response = csResp.body.ext.usersyncs + let response = csResp.body.ext.usersyncs; let bidders = response.bidder_status; for (let synci in bidders) { let thisSync = bidders[synci]; @@ -314,7 +314,7 @@ function newOrtbBidRequest(bidRequest, bidderRequest, currentImps) { }, user: { } - } + }; let bidFloor; if (isFn(bidRequest.getFloor) && !config.getConfig('disableFloors')) { @@ -566,7 +566,7 @@ function parseSizes(bid, mediaType) { } else if (typeof deepAccess(bid, 'mediaTypes.banner.sizes') !== 'undefined') { sizes = mapSizes(bid.mediaTypes.banner.sizes); } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0) { - sizes = mapSizes(bid.sizes) + sizes = mapSizes(bid.sizes); } else { logWarn('LuponMedia: no sizes are setup or found'); } diff --git a/modules/malltvBidAdapter.js b/modules/malltvBidAdapter.js index 6466aa4feed..cee8e888986 100644 --- a/modules/malltvBidAdapter.js +++ b/modules/malltvBidAdapter.js @@ -81,7 +81,7 @@ export const spec = { data: data, gdpr_applies: gdrpApplies, gdpr_consent: gdprConsent, - } + }; return [{ method: 'POST', @@ -120,7 +120,7 @@ export const spec = { } return bidResponses; } -} +}; /** * Generate size param for bid request using sizes array diff --git a/modules/mediakeysBidAdapter.js b/modules/mediakeysBidAdapter.js index cc8ffc6d29e..8c2b2c32e1c 100644 --- a/modules/mediakeysBidAdapter.js +++ b/modules/mediakeysBidAdapter.js @@ -420,7 +420,7 @@ function createVideoImp(bid) { } }); - return video + return video; } /** @@ -703,7 +703,7 @@ export const spec = { agencyName: deepAccess(bid, 'ext.agency_name', null), primaryCatId: getPrimaryCatFromResponse(bid.cat), mediaType - } + }; const newBid = { requestId: bid.impid, diff --git a/modules/mgidBidAdapter.js b/modules/mgidBidAdapter.js index 56cf1e9645e..2653f157196 100644 --- a/modules/mgidBidAdapter.js +++ b/modules/mgidBidAdapter.js @@ -88,7 +88,7 @@ export const spec = { let v = nativeParams[k]; const supportProp = spec.NATIVE_ASSET_KEY_TO_ASSET_MAP.hasOwnProperty(k); if (supportProp) { - assetsCount++ + assetsCount++; } if (!isPlainObject(v) || (!supportProp && deepAccess(v, 'required'))) { nativeOk = false; @@ -152,7 +152,7 @@ export const spec = { impObj.bidfloor = floorData.floor; } if (floorData.cur) { - impObj.bidfloorcur = floorData.cur + impObj.bidfloorcur = floorData.cur; } for (let mediaTypes in bid.mediaTypes) { switch (mediaTypes) { diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index 877afb08293..20878b545e4 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -147,7 +147,7 @@ function nobidBuildRequests(bids, bidderRequest) { if (eids && eids.length > 0) state['eids'] = eids; if (bidderRequest && bidderRequest.ortb2) state['ortb2'] = bidderRequest.ortb2; return state; - } + }; function newAdunit(adunitObject, adunits) { var getAdUnit = function(divid, adunits) { for (var i = 0; i < adunits.length; i++) { @@ -438,7 +438,7 @@ export const spec = { type: 'image', url: element }); - }) + }); } return syncs; } else { diff --git a/modules/novatiqIdSystem.js b/modules/novatiqIdSystem.js index 661a14ca0ef..7bd1ee8acd9 100644 --- a/modules/novatiqIdSystem.js +++ b/modules/novatiqIdSystem.js @@ -245,7 +245,7 @@ export const novatiqIdSubmodule = { } else { srcId = configParams.sourceid; } - return srcId + return srcId; } }; submodule('userId', novatiqIdSubmodule); diff --git a/modules/open8BidAdapter.js b/modules/open8BidAdapter.js index 7fa97235525..5fa1dd0a143 100644 --- a/modules/open8BidAdapter.js +++ b/modules/open8BidAdapter.js @@ -68,7 +68,7 @@ export const spec = { meta: { advertiserDomains: ad.adomain || [] } - } + }; if (ad.adType === AD_TYPE.VIDEO) { const videoAd = bidderResponse.ad.video; diff --git a/modules/openwebBidAdapter.js b/modules/openwebBidAdapter.js index f07b37e16e1..296bfc682f1 100644 --- a/modules/openwebBidAdapter.js +++ b/modules/openwebBidAdapter.js @@ -149,7 +149,7 @@ function bidToTag(bidRequests, adapterRequest) { tag.UserEids = deepAccess(bidRequests[0], 'userIdAsEids'); } // end publisher env - const bids = [] + const bids = []; for (let i = 0, length = bidRequests.length; i < length; i++) { const bid = prepareBidRequests(bidRequests[i]); diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index 251e6fb50e3..14525cd0cfc 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -353,7 +353,7 @@ function appendUserIdsToQueryParams(queryParams, userIds) { case 'lipb': queryParams[key] = userIdObjectOrValue.lipbid; if (Array.isArray(userIdObjectOrValue.segments) && userIdObjectOrValue.segments.length > 0) { - const liveIntentSegments = 'liveintent:' + userIdObjectOrValue.segments.join('|') + const liveIntentSegments = 'liveintent:' + userIdObjectOrValue.segments.join('|'); queryParams.sm = `${queryParams.sm ? queryParams.sm + ',' : ''}${liveIntentSegments}`; } break; @@ -502,7 +502,7 @@ function generateVideoParameters(bid, bidderRequest) { video: openRtbParams } ] - } + }; queryParams['openrtb'] = JSON.stringify(openRtbReq); @@ -525,7 +525,7 @@ function generateVideoParameters(bid, bidderRequest) { let gpid = deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); if (gpid) { - queryParams.aucs = encodeURIComponent(gpid) + queryParams.aucs = encodeURIComponent(gpid); } // each video bid makes a separate request diff --git a/modules/pixfutureBidAdapter.js b/modules/pixfutureBidAdapter.js index bffd5badded..4c4935e93a4 100644 --- a/modules/pixfutureBidAdapter.js +++ b/modules/pixfutureBidAdapter.js @@ -211,7 +211,7 @@ function bidToTag(bid) { tag.code = bid.params.invCode; } tag.allow_smaller_sizes = bid.params.allowSmallerSizes || false; - tag.use_pmt_rule = bid.params.usePaymentRule || false + tag.use_pmt_rule = bid.params.usePaymentRule || false; tag.prebid = true; tag.disable_psa = true; let bidFloor = getBidFloor(bid); diff --git a/modules/prebidmanagerAnalyticsAdapter.js b/modules/prebidmanagerAnalyticsAdapter.js index 708d2b79243..1180a74db30 100644 --- a/modules/prebidmanagerAnalyticsAdapter.js +++ b/modules/prebidmanagerAnalyticsAdapter.js @@ -9,7 +9,7 @@ import CONSTANTS from '../src/constants.json'; * prebidmanagerAnalyticsAdapter.js - analytics adapter for prebidmanager */ export const storage = getStorageManager({gvlid: undefined, moduleName: 'prebidmanager'}); -const DEFAULT_EVENT_URL = 'https://endpoint.prebidmanager.com/endpoint' +const DEFAULT_EVENT_URL = 'https://endpoint.prebidmanager.com/endpoint'; const analyticsType = 'endpoint'; const analyticsName = 'Prebid Manager Analytics: '; diff --git a/modules/priceFloors.js b/modules/priceFloors.js index 900276e6b4e..c972e024780 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -700,7 +700,7 @@ export function addBidResponseHook(fn, adUnitCode, bid) { return fn.call(this, adUnitCode, bid); } - const matchingBidRequest = auctionManager.index.getBidRequest(bid) + const matchingBidRequest = auctionManager.index.getBidRequest(bid); // get the matching rule let floorInfo = getFirstMatchingFloor(floorData.data, matchingBidRequest, {...bid, size: [bid.width, bid.height]}); diff --git a/modules/pubwiseAnalyticsAdapter.js b/modules/pubwiseAnalyticsAdapter.js index 7976bcef58d..19a74c7c245 100644 --- a/modules/pubwiseAnalyticsAdapter.js +++ b/modules/pubwiseAnalyticsAdapter.js @@ -295,7 +295,7 @@ pubwiseAnalytics.handleEvent = function(eventType, data) { if (eventType === CONSTANTS.EVENTS.AUCTION_END || eventType === CONSTANTS.EVENTS.BID_WON) { flushEvents(); } -} +}; pubwiseAnalytics.storeSessionID = function (userSessID) { storage.setDataInLocalStorage(localStorageSessName(), userSessID); diff --git a/modules/pubwiseBidAdapter.js b/modules/pubwiseBidAdapter.js index 1c015a24536..7721fe10459 100644 --- a/modules/pubwiseBidAdapter.js +++ b/modules/pubwiseBidAdapter.js @@ -161,7 +161,7 @@ export const spec = { } if (bid.params.isTest) { - payload.test = Number(bid.params.isTest) // should be 1 or 0 + payload.test = Number(bid.params.isTest); // should be 1 or 0 } payload.site.publisher.id = bid.params.siteId.trim(); payload.user.gender = (conf.gender ? conf.gender.trim() : UNDEFINED); @@ -207,7 +207,7 @@ export const spec = { deepSetValue(payload, 'regs.coppa', 1); } - var options = {contentType: 'text/plain'} + var options = {contentType: 'text/plain'}; _logInfo('buildRequests payload', payload); _logInfo('buildRequests bidderRequest', bidderRequest); @@ -463,7 +463,7 @@ function _createImpressionObject(bid, conf) { } } } else { - _logWarn('MediaTypes are Required for all Adunit Configs', bid) + _logWarn('MediaTypes are Required for all Adunit Configs', bid); } _addFloorFromFloorModule(impObj, bid); diff --git a/modules/pubxBidAdapter.js b/modules/pubxBidAdapter.js index 18d2bb11404..17c4ba3848e 100644 --- a/modules/pubxBidAdapter.js +++ b/modules/pubxBidAdapter.js @@ -76,7 +76,7 @@ export const spec = { } else { kwString = kwContents; } - kwEnc = encodeURIComponent(kwString) + kwEnc = encodeURIComponent(kwString); } else { } if (titleContent) { if (titleContent.length > 30) { diff --git a/modules/pubxaiAnalyticsAdapter.js b/modules/pubxaiAnalyticsAdapter.js index bd92162b8cb..f7e73dd5fdd 100644 --- a/modules/pubxaiAnalyticsAdapter.js +++ b/modules/pubxaiAnalyticsAdapter.js @@ -140,7 +140,7 @@ export function getOS() { // add sampling rate pubxaiAnalyticsAdapter.shouldFireEventRequest = function (samplingRate = 1) { return (Math.floor((Math.random() * samplingRate + 1)) === parseInt(samplingRate)); -} +}; function send(data, status) { if (pubxaiAnalyticsAdapter.shouldFireEventRequest(initOptions.samplingRate)) { @@ -158,7 +158,7 @@ function send(data, status) { data.initOptions.auctionId = data.auctionInit.auctionId; delete data.auctionInit; - data.pmcDetail = {} + data.pmcDetail = {}; Object.assign(data.pmcDetail, { bidDensity: storage ? storage.getItem('pbx:dpbid') : null, maxBid: storage ? storage.getItem('pbx:mxbid') : null, diff --git a/modules/quantcastIdSystem.js b/modules/quantcastIdSystem.js index b803096cd31..97cd01f98da 100644 --- a/modules/quantcastIdSystem.js +++ b/modules/quantcastIdSystem.js @@ -211,7 +211,7 @@ export const quantcastIdSubmodule = { }); } - return { id: fpa ? { quantcastId: fpa } : undefined } + return { id: fpa ? { quantcastId: fpa } : undefined }; } }; diff --git a/modules/radsBidAdapter.js b/modules/radsBidAdapter.js index 938fd566159..fcb8c3a8c2a 100644 --- a/modules/radsBidAdapter.js +++ b/modules/radsBidAdapter.js @@ -65,7 +65,7 @@ export const spec = { method: 'GET', url: endpoint, data: objectToQueryString(payload), - } + }; }); }, interpretResponse: function(serverResponse, bidRequest) { diff --git a/modules/revcontentBidAdapter.js b/modules/revcontentBidAdapter.js index c7c82cd323e..5e33a9a0fd7 100644 --- a/modules/revcontentBidAdapter.js +++ b/modules/revcontentBidAdapter.js @@ -232,7 +232,7 @@ function buildImp(bid, id) { w: sizes[0][0], h: sizes[0][1], format: sizes.map(wh => parseGPTSingleSizeArrayToRtbSize(wh)), - } + }; } else if (nativeReq) { const assets = _map(bid.nativeParams, (bidParams, key) => { const props = NATIVE_PARAMS[key]; diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js index 564b67641a2..1faed74d84e 100755 --- a/modules/richaudienceBidAdapter.js +++ b/modules/richaudienceBidAdapter.js @@ -132,7 +132,7 @@ export const spec = { bidResponses.push(bidResponse); } - return bidResponses + return bidResponses; }, /*** * User Syncs diff --git a/modules/rtbhouseBidAdapter.js b/modules/rtbhouseBidAdapter.js index 1ae73e192b8..961f0fcacae 100644 --- a/modules/rtbhouseBidAdapter.js +++ b/modules/rtbhouseBidAdapter.js @@ -68,7 +68,7 @@ export const spec = { if (schain) { request.ext = { schain: schain, - } + }; } } diff --git a/modules/schain.js b/modules/schain.js index d409e74df48..ef1c6da78ce 100644 --- a/modules/schain.js +++ b/modules/schain.js @@ -19,7 +19,7 @@ _each(MODE, mode => MODES.push(mode)); // validate the supply chain object export function isSchainObjectValid(schainObject, returnOnError) { - let failPrefix = 'Detected something wrong within an schain config:' + let failPrefix = 'Detected something wrong within an schain config:'; let failMsg = ''; function appendFailMsg(msg) { diff --git a/modules/showheroes-bsBidAdapter.js b/modules/showheroes-bsBidAdapter.js index 98451ebbb2f..f9ce3a450cf 100644 --- a/modules/showheroes-bsBidAdapter.js +++ b/modules/showheroes-bsBidAdapter.js @@ -314,7 +314,7 @@ function outstreamRender(bid) { logError('[ShowHeroes][renderer] Error: spot not found'); } } catch (err) { - logError('[ShowHeroes][renderer] Error:' + err.message) + logError('[ShowHeroes][renderer] Error:' + err.message); } } } diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 4a1518ab72e..05475b3e143 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -98,7 +98,7 @@ export function checkAdUnitSetupHook(adUnits) { */ if (!isArrayOfNums(config.minViewPort, 2)) { logError(`Ad unit ${adUnitCode}: Invalid declaration of 'minViewPort' in 'mediaTypes.${mediaType}.sizeConfig[${index}]'. ${conditionalLogMessages[mediaType]}`); - isValid = false + isValid = false; return; } /* diff --git a/modules/smaatoBidAdapter.js b/modules/smaatoBidAdapter.js index a24f296102f..c783f35d915 100644 --- a/modules/smaatoBidAdapter.js +++ b/modules/smaatoBidAdapter.js @@ -63,7 +63,7 @@ const buildOpenRtbBidRequest = (bidRequest, bidderRequest) => { if (deepAccess(bidRequest, 'params.app')) { const geo = deepAccess(bidRequest, 'params.app.geo'); deepSetValue(requestTemplate, 'device.geo', geo); - const ifa = deepAccess(bidRequest, 'params.app.ifa') + const ifa = deepAccess(bidRequest, 'params.app.ifa'); deepSetValue(requestTemplate, 'device.ifa', ifa); } @@ -210,7 +210,7 @@ export const spec = { } }; - const videoContext = deepAccess(JSON.parse(bidRequest.data).imp[0], 'video.ext.context') + const videoContext = deepAccess(JSON.parse(bidRequest.data).imp[0], 'video.ext.context'); if (videoContext === ADPOD) { resultingBid.vastXml = bid.adm; resultingBid.mediaType = VIDEO; diff --git a/modules/sonobiAnalyticsAdapter.js b/modules/sonobiAnalyticsAdapter.js index 75d481aba9e..0057944b201 100644 --- a/modules/sonobiAnalyticsAdapter.js +++ b/modules/sonobiAnalyticsAdapter.js @@ -255,7 +255,7 @@ sonobiAdapter.sendData = function (auction, data) { contentType: 'text/plain' } ); -} +}; function _logInfo(message, meta) { logInfo(buildLogMessage(message), meta); diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index 5309dbdad5a..c6100ac8b40 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -129,7 +129,7 @@ export const spec = { } if (validBidRequests[0].schain) { - payload.schain = JSON.stringify(validBidRequests[0].schain) + payload.schain = JSON.stringify(validBidRequests[0].schain); } if (deepAccess(validBidRequests[0], 'userId') && Object.keys(validBidRequests[0].userId).length > 0) { const userIds = deepClone(validBidRequests[0].userId); @@ -217,7 +217,7 @@ export const spec = { ] = bid.sbi_size.split('x'); let aDomains = []; if (bid.sbi_adomain) { - aDomains = [bid.sbi_adomain] + aDomains = [bid.sbi_adomain]; } const bids = { requestId: bidId, @@ -255,7 +255,7 @@ export const spec = { )); let videoSize = deepAccess(bidRequest, 'params.sizes'); if (Array.isArray(videoSize) && Array.isArray(videoSize[0])) { // handle case of multiple sizes - videoSize = videoSize[0] // Only take the first size for outstream + videoSize = videoSize[0]; // Only take the first size for outstream } if (videoSize) { bids.width = videoSize[0]; diff --git a/modules/staqAnalyticsAdapter.js b/modules/staqAnalyticsAdapter.js index 69d23a3a604..13996adfb7e 100644 --- a/modules/staqAnalyticsAdapter.js +++ b/modules/staqAnalyticsAdapter.js @@ -21,7 +21,7 @@ const STAQ_EVENTS = { BID_WON: 'bidWon', AUCTION_END: 'auctionEnd', TIMEOUT: 'adapterTimedOut' -} +}; function buildRequestTemplate(connId) { // TODO: what should these pick from refererInfo? diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index b8f7a5559f8..3cd77e7b853 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -295,9 +295,9 @@ function buildOneRequest(validBidRequests, bidderRequest) { if ( ((bannerMediaType.sizes[0].indexOf(480) >= 0) && (bannerMediaType.sizes[0].indexOf(320) >= 0)) || ((bannerMediaType.sizes[0].indexOf(768) >= 0) && (bannerMediaType.sizes[0].indexOf(1024) >= 0))) { - banner.pos = 7 + banner.pos = 7; } else { - banner.pos = 4 + banner.pos = 4; } banner.api = api; diff --git a/modules/targetVideoBidAdapter.js b/modules/targetVideoBidAdapter.js index f8cb58218cd..bc775e78647 100644 --- a/modules/targetVideoBidAdapter.js +++ b/modules/targetVideoBidAdapter.js @@ -132,7 +132,7 @@ function createVideoTag(bid) { tag.ad_types = [VIDEO]; tag.uuid = bid.bidId; tag.allow_smaller_sizes = false; - tag.use_pmt_rule = false + tag.use_pmt_rule = false; tag.prebid = true; tag.disable_psa = true; tag.hb_source = 1; diff --git a/modules/theAdxBidAdapter.js b/modules/theAdxBidAdapter.js index 03c6ea62cb7..3f7823d2fd1 100644 --- a/modules/theAdxBidAdapter.js +++ b/modules/theAdxBidAdapter.js @@ -206,7 +206,7 @@ export const spec = { let bidWidth = nullify(bid.w); let bidHeight = nullify(bid.h); - let creative = null + let creative = null; let videoXml = null; let mediaType = null; let native = null; @@ -389,7 +389,7 @@ let extractValidSize = (bidRequest, bidderRequest) => { requestedSizes = mediaTypes.video.sizes; } } else if (!isEmpty(bidRequest.sizes)) { - requestedSizes = bidRequest.sizes + requestedSizes = bidRequest.sizes; } // Ensure the size array is normalized diff --git a/modules/tpmnBidAdapter.js b/modules/tpmnBidAdapter.js index 8687ab06f4c..4dcdf1ef529 100644 --- a/modules/tpmnBidAdapter.js +++ b/modules/tpmnBidAdapter.js @@ -87,7 +87,7 @@ export const spec = { syncArr.push({ type: 'iframe', url: IFRAMESYNC + policyParam - }) + }); } else { syncArr.push({ type: 'image', diff --git a/modules/trionBidAdapter.js b/modules/trionBidAdapter.js index 5750406116b..b6375243a5a 100644 --- a/modules/trionBidAdapter.js +++ b/modules/trionBidAdapter.js @@ -53,7 +53,7 @@ export const spec = { bid.currency = result.currency; bid.netRevenue = result.netRevenue; if (result.adomain) { - bid.meta = {advertiserDomains: result.adomain} + bid.meta = {advertiserDomains: result.adomain}; } bidResponses.push(bid); } @@ -125,7 +125,7 @@ function buildTrionUrlParams(bid, bidderRequest) { intT = getStorageData(BASE_KEY + 'int_t'); } if (intT) { - setStorageData(BASE_KEY + 'int_t', intT) + setStorageData(BASE_KEY + 'int_t', intT); } setStorageData(BASE_KEY + 'lps', pubId + ':' + sectionId); var trionUrl = ''; diff --git a/modules/trustpidSystem.js b/modules/trustpidSystem.js index fc2bab19913..9d457d645b2 100644 --- a/modules/trustpidSystem.js +++ b/modules/trustpidSystem.js @@ -88,7 +88,7 @@ function getTrustpidFromStorage() { return { trustpid: null, acr: null - } + }; } let fcIdConnectObject; diff --git a/modules/underdogmediaBidAdapter.js b/modules/underdogmediaBidAdapter.js index 2ca4de7a555..53fe83b1dd0 100644 --- a/modules/underdogmediaBidAdapter.js +++ b/modules/underdogmediaBidAdapter.js @@ -98,7 +98,7 @@ export const spec = { } var sizeNotFound = true; - const bidParamSizes = bidParam.mediaTypes && bidParam.mediaTypes.banner && bidParam.mediaTypes.banner.sizes ? bidParam.mediaTypes.banner.sizes : bidParam.sizes + const bidParamSizes = bidParam.mediaTypes && bidParam.mediaTypes.banner && bidParam.mediaTypes.banner.sizes ? bidParam.mediaTypes.banner.sizes : bidParam.sizes; parseSizesInput(bidParamSizes).forEach(size => { if (size === mid.width + 'x' + mid.height) { sizeNotFound = false; diff --git a/modules/vdoaiBidAdapter.js b/modules/vdoaiBidAdapter.js index e149a56f988..a57eae5f328 100644 --- a/modules/vdoaiBidAdapter.js +++ b/modules/vdoaiBidAdapter.js @@ -102,7 +102,7 @@ export const spec = { if (response.adDomain) { bidResponse.meta = { advertiserDomains: response.adDomain - } + }; } bidResponses.push(bidResponse); } diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index 1f84c9f4b16..39d37d55a91 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -97,7 +97,7 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) { } } if (bidderRequest.uspConsent) { - data.usPrivacy = bidderRequest.uspConsent + data.usPrivacy = bidderRequest.uspConsent; } const dto = { diff --git a/modules/vidoomyBidAdapter.js b/modules/vidoomyBidAdapter.js index ebecb5a46ae..146ea6dd4bd 100644 --- a/modules/vidoomyBidAdapter.js +++ b/modules/vidoomyBidAdapter.js @@ -110,7 +110,7 @@ const buildRequests = (validBidRequests, bidderRequest) => { method: 'GET', url: ENDPOINT, data: queryParams - } + }; }); return serverRequests; }; diff --git a/modules/winrBidAdapter.js b/modules/winrBidAdapter.js index 3b86e5b59dc..cd807917944 100644 --- a/modules/winrBidAdapter.js +++ b/modules/winrBidAdapter.js @@ -208,7 +208,7 @@ export const spec = { } if (appDeviceObjBid) { - payload.device = appDeviceObj + payload.device = appDeviceObj; } if (appIdObjBid) { payload.app = appIdObj; diff --git a/modules/yahoosspBidAdapter.js b/modules/yahoosspBidAdapter.js index d1b7b08c202..38bb141070c 100644 --- a/modules/yahoosspBidAdapter.js +++ b/modules/yahoosspBidAdapter.js @@ -408,7 +408,7 @@ function appendFirstPartyData(outBoundBidRequest, bid) { newDataObject = validateAppendObject('object', allowedContentDataObjectKeys, dataObject, newDataObject); outBoundBidRequest.site.content.data = []; outBoundBidRequest.site.content.data.push(newDataObject); - }) + }); }; }; @@ -428,7 +428,7 @@ function appendFirstPartyData(outBoundBidRequest, bid) { } }; outBoundBidRequest.app.content.data.push(newDataObject); - }) + }); }; }; diff --git a/modules/yandexBidAdapter.js b/modules/yandexBidAdapter.js index ae2fb16c8ac..7da2872a3a6 100644 --- a/modules/yandexBidAdapter.js +++ b/modules/yandexBidAdapter.js @@ -65,7 +65,7 @@ export const spec = { withCredentials, }, bidRequest, - } + }; }); }, diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index a0d634e34b4..3149dcb7f5c 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -531,13 +531,13 @@ function validateVideoParams(bid) { error += ' when ' + conditionStr; } throw new Error(error); - } + }; const paramInvalid = (paramStr, value, expectedStr) => { expectedStr = expectedStr ? ', expected: ' + expectedStr : ''; value = JSON.stringify(value); throw new Error(`"${paramStr}"=${value} is invalid${expectedStr}`); - } + }; const isDefined = val => typeof val !== 'undefined'; const validate = (fieldPath, validateCb, errorCb, errorCbParam) => { @@ -563,7 +563,7 @@ function validateVideoParams(bid) { } return value; } - } + }; try { validate('video.context', val => !isEmpty(val), paramRequired); diff --git a/modules/yieldoneBidAdapter.js b/modules/yieldoneBidAdapter.js index 98a95b6758e..c07911c9e1f 100644 --- a/modules/yieldoneBidAdapter.js +++ b/modules/yieldoneBidAdapter.js @@ -90,7 +90,7 @@ export const spec = { method: 'GET', url: ENDPOINT_URL, data: payload, - } + }; }); }, interpretResponse: function(serverResponse, bidRequest) { @@ -212,7 +212,7 @@ function getMediaType(bidRequest, enabledOldFormat = true) { } if (hasBannerType && hasVideoType) { - const playerParams = deepAccess(bidRequest, 'params.playerParams') + const playerParams = deepAccess(bidRequest, 'params.playerParams'); if (playerParams) { return VIDEO; } else { diff --git a/src/Renderer.js b/src/Renderer.js index 3375edcd6c3..97e37084e89 100644 --- a/src/Renderer.js +++ b/src/Renderer.js @@ -60,7 +60,7 @@ export function Renderer(options) { this.cmd.unshift(runRender) // should render run first ? loadExternalScript(url, moduleCode, this.callback, this.documentContext); } - }.bind(this) // bind the function to this object to avoid 'this' errors + }.bind(this); // bind the function to this object to avoid 'this' errors } Renderer.install = function({ url, config, id, callback, loaded, adUnitCode, renderNow }) { diff --git a/src/bidfactory.js b/src/bidfactory.js index 95d69cf0adb..4c2e4cf3ffb 100644 --- a/src/bidfactory.js +++ b/src/bidfactory.js @@ -59,7 +59,7 @@ function Bid(statusCode, {src = 'client', bidder = '', bidId, transactionId, auc transactionId: this.transactionId, auctionId: this.auctionId } - } + }; } // Bid factory function. diff --git a/src/sizeMapping.js b/src/sizeMapping.js index 4333608ca95..cfd2861785c 100644 --- a/src/sizeMapping.js +++ b/src/sizeMapping.js @@ -111,7 +111,7 @@ export function resolveStatus({labels = [], labelAll = false, activeLabels = []} results.filterResults = { before: oldSizes, after: mediaTypes.banner.sizes - } + }; } return results; diff --git a/src/targeting.js b/src/targeting.js index cb10b3f0121..d2b3e9f8470 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -63,7 +63,7 @@ export const getHighestCpmBidsFromBidPool = hook('sync', function(bidsReceived, } return bidsReceived; -}) +}); /** * A descending sort function that will sort the list of objects based on the following two dimensions: diff --git a/src/userSync.js b/src/userSync.js index 674114e11f6..dec8f650930 100644 --- a/src/userSync.js +++ b/src/userSync.js @@ -311,7 +311,7 @@ export function newUserSync(userSyncDependencies) { } } return true; - } + }; return publicApi; } From 2bc9f9d4eb7671596b4d04df77d796d64470d056 Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Tue, 16 Aug 2022 15:45:11 +0600 Subject: [PATCH 068/246] Zeta ssp adapter: added multiimp request support (#8813) * multiimp ability * add some test * fix tests * fix tests 2 Co-authored-by: Surovenko Alexey --- modules/zeta_global_sspBidAdapter.js | 45 +++++++------- .../modules/zeta_global_sspBidAdapter_spec.js | 59 +++++++++++++++++++ 2 files changed, 83 insertions(+), 21 deletions(-) diff --git a/modules/zeta_global_sspBidAdapter.js b/modules/zeta_global_sspBidAdapter.js index d80265b28bb..4689683fbc7 100644 --- a/modules/zeta_global_sspBidAdapter.js +++ b/modules/zeta_global_sspBidAdapter.js @@ -69,31 +69,34 @@ export const spec = { */ buildRequests: function (validBidRequests, bidderRequest) { const secure = 1; // treat all requests as secure - const request = validBidRequests[0]; - const params = request.params; - const impData = { - id: request.bidId, - secure: secure - }; - if (request.mediaTypes) { - for (const mediaType in request.mediaTypes) { - switch (mediaType) { - case BANNER: - impData.banner = buildBanner(request); - break; - case VIDEO: - impData.video = buildVideo(request); - break; + const params = validBidRequests[0].params; + const imps = validBidRequests.map(request => { + const impData = { + id: request.bidId, + secure: secure + }; + if (request.mediaTypes) { + for (const mediaType in request.mediaTypes) { + switch (mediaType) { + case BANNER: + impData.banner = buildBanner(request); + break; + case VIDEO: + impData.video = buildVideo(request); + break; + } } } - } - if (!impData.banner && !impData.video) { - impData.banner = buildBanner(request); - } + if (!impData.banner && !impData.video) { + impData.banner = buildBanner(request); + } + return impData; + }); + let payload = { id: bidderRequest.auctionId, cur: [DEFAULT_CUR], - imp: [impData], + imp: imps, site: params.site ? params.site : {}, device: {...(bidderRequest.ortb2?.device || {}), ...params.device}, user: params.user ? params.user : {}, @@ -126,7 +129,7 @@ export const spec = { deepSetValue(payload, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - provideEids(request, payload); + provideEids(validBidRequests[0], payload); const url = params.shortname ? ENDPOINT_URL.concat('?shortname=', params.shortname) : ENDPOINT_URL; return { method: 'POST', diff --git a/test/spec/modules/zeta_global_sspBidAdapter_spec.js b/test/spec/modules/zeta_global_sspBidAdapter_spec.js index a70c1c79828..3bd17697f2d 100644 --- a/test/spec/modules/zeta_global_sspBidAdapter_spec.js +++ b/test/spec/modules/zeta_global_sspBidAdapter_spec.js @@ -44,6 +44,48 @@ describe('Zeta Ssp Bid Adapter', function () { test: 1 }; + const multiImpRequest = [ + { + bidId: 12345, + auctionId: 67890, + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + refererInfo: { + page: 'http://www.zetaglobal.com/page?param=value', + domain: 'www.zetaglobal.com', + }, + gdprConsent: { + gdprApplies: 1, + consentString: 'consentString' + }, + uspConsent: 'someCCPAString', + params: params, + userIdAsEids: eids + }, { + bidId: 54321, + auctionId: 67890, + mediaTypes: { + banner: { + sizes: [[600, 400]], + } + }, + refererInfo: { + page: 'http://www.zetaglobal.com/page?param=value', + domain: 'www.zetaglobal.com', + }, + gdprConsent: { + gdprApplies: 1, + consentString: 'consentString' + }, + uspConsent: 'someCCPAString', + params: params, + userIdAsEids: eids + } + ]; + const bannerRequest = [{ bidId: 12345, auctionId: 67890, @@ -285,4 +327,21 @@ describe('Zeta Ssp Bid Adapter', function () { expect(payload.ext.tags.someTag).to.eql(444); expect(payload.ext.tags.shortname).to.be.undefined; }); + + it('Test multi imp', function () { + const request = spec.buildRequests(multiImpRequest, multiImpRequest[0]); + const payload = JSON.parse(request.data); + expect(request.url).to.eql('https://ssp.disqus.com/bid/prebid?shortname=test_shortname'); + + expect(payload.imp.length).to.eql(2); + + expect(payload.imp[0].id).to.eql(12345); + expect(payload.imp[1].id).to.eql(54321); + + expect(payload.imp[0].banner.w).to.eql(300); + expect(payload.imp[0].banner.h).to.eql(250); + + expect(payload.imp[1].banner.w).to.eql(600); + expect(payload.imp[1].banner.h).to.eql(400); + }); }); From 9f01c38ba5e7074b3358a10003d0bb7363ce35ed Mon Sep 17 00:00:00 2001 From: vishal-dw <109065778+vishal-dw@users.noreply.github.com> Date: Tue, 16 Aug 2022 19:24:07 +0530 Subject: [PATCH 069/246] Datawrkz Bid Adapter: initial adapter release (#8754) * New Bid Adapter: datawrkz * New Bid Adapter: datawrkz. Test case formatting * New Bid Adatpter: datawrkz - updated import statements --- modules/datawrkzBidAdapter.js | 633 ++++++++++++++++++ modules/datawrkzBidAdapter.md | 160 +++++ test/spec/modules/datawrkzBidAdapter_spec.js | 656 +++++++++++++++++++ 3 files changed, 1449 insertions(+) create mode 100644 modules/datawrkzBidAdapter.js create mode 100644 modules/datawrkzBidAdapter.md create mode 100644 test/spec/modules/datawrkzBidAdapter_spec.js diff --git a/modules/datawrkzBidAdapter.js b/modules/datawrkzBidAdapter.js new file mode 100644 index 00000000000..a4ee6bcaace --- /dev/null +++ b/modules/datawrkzBidAdapter.js @@ -0,0 +1,633 @@ +import { deepAccess, getBidIdParameter, isArray, getUniqueIdentifierStr, contains } from '../src/utils.js'; +import { config } from '../src/config.js'; +import { Renderer } from '../src/Renderer.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { createBid } from '../src/bidfactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import CONSTANTS from '../src/constants.json'; +import { OUTSTREAM, INSTREAM } from '../src/video.js'; + +const BIDDER_CODE = 'datawrkz'; +const ALIASES = []; +const ENDPOINT_URL = 'https://at.datawrkz.com/exchange/openrtb23/'; +const RENDERER_URL = 'https://js.datawrkz.com/prebid/osRenderer.min.js'; +const OUTSTREAM_TYPES = ['inline', 'slider_top_left', 'slider_top_right', 'slider_bottom_left', 'slider_bottom_right', 'interstitial_close', 'listicle'] +const OUTSTREAM_MIMES = ['video/mp4'] +const SUPPORTED_AD_TYPES = [BANNER, NATIVE, VIDEO]; + +export const spec = { + code: BIDDER_CODE, + aliases: ALIASES, + supportedMediaTypes: SUPPORTED_AD_TYPES, + /** + * Determines whether or not the given bid request is valid. + * + * @param {object} bid The bid to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function(bid) { + return !!(bid.params && bid.params.site_id && (deepAccess(bid, 'mediaTypes.video.context') != 'adpod')); + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be sent to the Server. + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests, bidderRequest) { + let requests = []; + + if (validBidRequests.length > 0) { + validBidRequests.forEach(bidRequest => { + if (!bidRequest.mediaTypes) return; + if (bidRequest.mediaTypes.banner && ((bidRequest.mediaTypes.banner.sizes && bidRequest.mediaTypes.banner.sizes.length != 0) || + (bidRequest.sizes))) { + requests.push(buildBannerRequest(bidRequest, bidderRequest)); + } else if (bidRequest.mediaTypes.native) { + requests.push(buildNativeRequest(bidRequest, bidderRequest)); + } else if (bidRequest.mediaTypes.video) { + requests.push(buildVideoRequest(bidRequest, bidderRequest)); + } + }); + } + return requests; + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse, request) { + var bidResponses = []; + let bidRequest = request.bidRequest + let bidResponse = serverResponse.body; + + // valid object? + if ((!bidResponse || !bidResponse.id) || (!bidResponse.seatbid || bidResponse.seatbid.length === 0 || + !bidResponse.seatbid[0].bid || bidResponse.seatbid[0].bid.length === 0)) { + return []; + } + + if (getMediaTypeOfResponse(bidRequest) == BANNER) { + bidResponses = buildBannerResponse(bidRequest, bidResponse); + } else if (getMediaTypeOfResponse(bidRequest) == NATIVE) { + bidResponses = buildNativeResponse(bidRequest, bidResponse); + } else if (getMediaTypeOfResponse(bidRequest) == VIDEO) { + bidResponses = buildVideoResponse(bidRequest, bidResponse); + } + return bidResponses; + }, +} + +/* Generate bid request for banner adunit */ +function buildBannerRequest(bidRequest, bidderRequest) { + let bidFloor = Number(getBidIdParameter('bidfloor', bidRequest.params)); + + let adW = 0; + let adH = 0; + + let bannerSizes = deepAccess(bidRequest, 'mediaTypes.banner.sizes'); + let bidSizes = isArray(bannerSizes) ? bannerSizes : bidRequest.sizes; + if (isArray(bidSizes)) { + if (bidSizes.length === 2 && typeof bidSizes[0] === 'number' && typeof bidSizes[1] === 'number') { + adW = parseInt(bidSizes[0]); + adH = parseInt(bidSizes[1]); + } else { + adW = parseInt(bidSizes[0][0]); + adH = parseInt(bidSizes[0][1]); + } + } + + var deals = []; + if (bidRequest.params.deals && bidRequest.params.deals.length > 0) { + deals = bidRequest.params.deals; + } + + const imp = [{ + id: bidRequest.bidId, + banner: { + w: adW, + h: adH + }, + bidfloor: bidFloor, + pmp: { + deals: deals + } + }]; + + bidRequest.requestedMediaType = BANNER; + const scriptUrl = generateScriptUrl(bidRequest); + const payloadString = generatePayload(imp, bidderRequest); + + return { + method: 'POST', + url: scriptUrl, + data: payloadString, + bidRequest + }; +} + +/* Generate bid request for native adunit */ +function buildNativeRequest(bidRequest, bidderRequest) { + let counter = 0; + let assets = []; + + let bidFloor = Number(getBidIdParameter('bidfloor', bidRequest.params)); + + let title = deepAccess(bidRequest, 'mediaTypes.native.title'); + if (title && title.len) { + assets.push(generateNativeTitleObj(title, ++counter)); + } + let image = deepAccess(bidRequest, 'mediaTypes.native.image'); + if (image) { + assets.push(generateNativeImgObj(image, 'image', ++counter)); + } + let icon = deepAccess(bidRequest, 'mediaTypes.native.icon'); + if (icon) { + assets.push(generateNativeImgObj(icon, 'icon', ++counter)); + } + let sponsoredBy = deepAccess(bidRequest, 'mediaTypes.native.sponsoredBy'); + if (sponsoredBy) { + assets.push(generateNativeDataObj(sponsoredBy, 'sponsored', ++counter)); + } + let cta = deepAccess(bidRequest, 'mediaTypes.native.cta'); + if (cta) { + assets.push(generateNativeDataObj(cta, 'cta', ++counter)); + } + let body = deepAccess(bidRequest, 'mediaTypes.native.body'); + if (body) { + assets.push(generateNativeDataObj(body, 'desc', ++counter)); + } + + let request = JSON.stringify({assets: assets}) + const native = { + request: request + } + + var deals = []; + if (bidRequest.params.deals && bidRequest.params.deals.length > 0) { + deals = bidRequest.params.deals; + } + + const imp = [{ + id: bidRequest.bidId, + native: native, + bidfloor: bidFloor, + pmp: { + deals: deals + } + }]; + + bidRequest.requestedMediaType = NATIVE; + bidRequest.assets = assets; + const scriptUrl = generateScriptUrl(bidRequest); + const payloadString = generatePayload(imp, bidderRequest); + + return { + method: 'POST', + url: scriptUrl, + data: payloadString, + bidRequest + }; +} + +/* Generate bid request for video adunit */ +function buildVideoRequest(bidRequest, bidderRequest) { + let bidFloor = Number(getBidIdParameter('bidfloor', bidRequest.params)); + + let sizeObj = getVideoAdUnitSize(bidRequest); + + const video = { + w: sizeObj.adW, + h: sizeObj.adH, + api: deepAccess(bidRequest, 'mediaTypes.video.api'), + mimes: deepAccess(bidRequest, 'mediaTypes.video.mimes'), + protocols: deepAccess(bidRequest, 'mediaTypes.video.protocols'), + playbackmethod: deepAccess(bidRequest, 'mediaTypes.video.playbackmethod'), + minduration: deepAccess(bidRequest, 'mediaTypes.video.minduration'), + maxduration: deepAccess(bidRequest, 'mediaTypes.video.maxduration'), + startdelay: deepAccess(bidRequest, 'mediaTypes.video.startdelay'), + minbitrate: deepAccess(bidRequest, 'mediaTypes.video.minbitrate'), + maxbitrate: deepAccess(bidRequest, 'mediaTypes.video.maxbitrate'), + delivery: deepAccess(bidRequest, 'mediaTypes.video.delivery'), + linearity: deepAccess(bidRequest, 'mediaTypes.video.linearity'), + placement: deepAccess(bidRequest, 'mediaTypes.video.placement'), + skip: deepAccess(bidRequest, 'mediaTypes.video.skip'), + skipafter: deepAccess(bidRequest, 'mediaTypes.video.skipafter') + }; + + let context = deepAccess(bidRequest, 'mediaTypes.video.context'); + if (context == 'outstream' && !bidRequest.renderer) video.mimes = OUTSTREAM_MIMES + + var imp = []; + var deals = []; + if (bidRequest.params.deals && bidRequest.params.deals.length > 0) { + deals = bidRequest.params.deals; + } + + if (context != 'adpod') { + imp.push({ + id: bidRequest.bidId, + video: video, + bidfloor: bidFloor, + pmp: { + deals: deals + } + }); + } + bidRequest.requestedMediaType = VIDEO; + const scriptUrl = generateScriptUrl(bidRequest); + const payloadString = generatePayload(imp, bidderRequest); + + return { + method: 'POST', + url: scriptUrl, + data: payloadString, + bidRequest + }; +} + +/* Convert video player size to bid request compatible format */ +function getVideoAdUnitSize(bidRequest) { + var adH = 0; + var adW = 0; + let playerSize = deepAccess(bidRequest, 'mediaTypes.video.playerSize'); + if (isArray(playerSize)) { + if (playerSize.length === 2 && typeof playerSize[0] === 'number' && typeof playerSize[1] === 'number') { + adW = parseInt(playerSize[0]); + adH = parseInt(playerSize[1]); + } else { + adW = parseInt(playerSize[0][0]); + adH = parseInt(playerSize[0][1]); + } + } + return {adH: adH, adW: adW} +} + +/* Get mediatype of the adunit from request */ +function getMediaTypeOfResponse(bidRequest) { + if (bidRequest.requestedMediaType == BANNER) return BANNER; + else if (bidRequest.requestedMediaType == NATIVE) return NATIVE; + else if (bidRequest.requestedMediaType == VIDEO) return VIDEO; + else return ''; +} + +/* Generate endpoint url */ +function generateScriptUrl(bidRequest) { + let queryParams = 'hb=1'; + let siteId = getBidIdParameter('site_id', bidRequest.params); + return ENDPOINT_URL + siteId + '?' + queryParams; +} + +/* Generate request payload for the adunit */ +function generatePayload(imp, bidderRequest) { + let domain = window.location.host; + let page = window.location.host + window.location.pathname + location.search + location.hash; + + const site = { + domain: domain, + page: page, + publisher: {} + }; + + let regs = {ext: {}} + + if (bidderRequest.uspConsent) { + regs.ext.us_privacy = bidderRequest.uspConsent; + } + if (bidderRequest.gdprConsent && typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') { + regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? '1' : '0'; + } + + if (config.getConfig('coppa') === true) { + regs.coppa = '1'; + } + + const device = { + ua: window.navigator.userAgent + }; + + const payload = { + id: getUniqueIdentifierStr(), + imp: imp, + site: site, + device: device, + regs: regs + }; + + return JSON.stringify(payload); +} + +/* Generate image asset object */ +function generateNativeImgObj(obj, type, id) { + let adW = 0; + let adH = 0; + let bidSizes = obj.sizes; + + var typeId; + if (type == 'icon') typeId = 1; + else if (type == 'image') typeId = 3; + + if (isArray(bidSizes)) { + if (bidSizes.length === 2 && typeof bidSizes[0] === 'number' && typeof bidSizes[1] === 'number') { + adW = parseInt(bidSizes[0]); + adH = parseInt(bidSizes[1]); + } else { + adW = parseInt(bidSizes[0][0]); + adH = parseInt(bidSizes[0][1]); + } + } + + let required = obj.required ? 1 : 0; + let image = { + type: parseInt(typeId), + w: adW, + h: adH + } + return { + id: id, + required: required, + img: image + }; +} + +/* Generate title asset object */ +function generateNativeTitleObj(obj, id) { + let required = obj.required ? 1 : 0; + let title = { + len: obj.len + }; + return { + id: id, + required: required, + title: title + }; +} + +/* Generate data asset object */ +function generateNativeDataObj(obj, type, id) { + var typeId; + switch (type) { + case 'sponsored': typeId = 1; + break; + case 'desc' : typeId = 2; + break; + case 'cta' : typeId = 12; + break; + } + + let required = obj.required ? 1 : 0; + let data = { + type: typeId + }; + if (typeId == 2 && obj.len) { + data.len = parseInt(obj.len); + } + return { + id: id, + required: required, + data: data + }; +} + +/* Convert banner bid response to compatible format */ +function buildBannerResponse(bidRequest, bidResponse) { + const bidResponses = []; + bidResponse.seatbid[0].bid.forEach(function (bidderBid) { + let responseCPM; + let placementCode = ''; + + if (bidRequest) { + let bidResponse = createBid(1); + placementCode = bidRequest.placementCode; + bidRequest.status = CONSTANTS.STATUS.GOOD; + responseCPM = parseFloat(bidderBid.price); + if (responseCPM === 0 || isNaN(responseCPM)) { + let bid = createBid(2); + bid.requestId = bidRequest.bidId; + bid.bidderCode = bidRequest.bidder; + bidResponses.push(bid); + return; + } + let bidSizes = (deepAccess(bidRequest, 'mediaTypes.banner.sizes')) ? deepAccess(bidRequest, 'mediaTypes.banner.sizes') : bidRequest.sizes; + bidResponse.requestId = bidRequest.bidId + bidResponse.transactionId = bidRequest.transactionId + bidResponse.placementCode = placementCode; + bidResponse.cpm = responseCPM; + bidResponse.size = bidSizes; + bidResponse.width = parseInt(bidderBid.w); + bidResponse.height = parseInt(bidderBid.h); + let responseAd = bidderBid.adm; + let responseNurl = ''; + bidResponse.ad = decodeURIComponent(responseAd + responseNurl); + bidResponse.creativeId = bidderBid.id; + bidResponse.bidderCode = bidRequest.bidder; + bidResponse.ttl = 300; + bidResponse.netRevenue = true; + bidResponse.currency = 'USD'; + bidResponse.mediaType = BANNER; + bidResponses.push(bidResponse); + } + }); + return bidResponses; +} + +/* Convert native bid response to compatible format */ +function buildNativeResponse(bidRequest, response) { + const bidResponses = []; + response.seatbid[0].bid.forEach(function (bidderBid) { + let responseCPM; + let placementCode = ''; + + if (bidRequest) { + let bidResponse = createBid(1); + placementCode = bidRequest.placementCode; + bidRequest.status = CONSTANTS.STATUS.GOOD; + responseCPM = parseFloat(bidderBid.price); + if (responseCPM === 0 || isNaN(responseCPM)) { + let bid = createBid(2); + bid.requestId = bidRequest.bidId; + bid.bidderCode = bidRequest.bidder; + bidResponses.push(bid); + return; + } + bidResponse.requestId = bidRequest.bidId + bidResponse.transactionId = bidRequest.transactionId + bidResponse.placementCode = placementCode; + bidResponse.cpm = responseCPM; + + let nativeResponse = JSON.parse(bidderBid.adm).native; + + const native = { + clickUrl: nativeResponse.link.url, + impressionTrackers: nativeResponse.imptrackers + }; + + nativeResponse.assets.forEach(function(asset) { + let keyVal = getNativeAssestObj(asset, bidRequest.assets); + native[keyVal.key] = keyVal.value; + }); + + bidResponse.creativeId = bidderBid.id; + bidResponse.bidderCode = bidRequest.bidder; + bidResponse.ttl = 300; + if (bidRequest.sizes) { bidResponse.size = bidRequest.sizes; } + bidResponse.netRevenue = true; + bidResponse.currency = 'USD'; + bidResponse.native = native; + bidResponse.mediaType = NATIVE; + bidResponses.push(bidResponse); + } + }); + return bidResponses; +} + +/* Convert video bid response to compatible format */ +function buildVideoResponse(bidRequest, response) { + const bidResponses = []; + response.seatbid[0].bid.forEach(function (bidderBid) { + let responseCPM; + let placementCode = ''; + + if (bidRequest) { + let bidResponse = createBid(1); + placementCode = bidRequest.placementCode; + bidRequest.status = CONSTANTS.STATUS.GOOD; + responseCPM = parseFloat(bidderBid.price); + if (responseCPM === 0 || isNaN(responseCPM)) { + let bid = createBid(2); + bid.requestId = bidRequest.bidId; + bid.bidderCode = bidRequest.bidder; + bidResponses.push(bid); + return; + } + let context = bidRequest.mediaTypes.video.context; + + bidResponse.requestId = bidRequest.bidId + bidResponse.transactionId = bidRequest.transactionId + bidResponse.placementCode = placementCode; + bidResponse.cpm = responseCPM; + + let vastXml = decodeURIComponent(bidderBid.adm); + + bidResponse.creativeId = bidderBid.id; + bidResponse.bidderCode = bidRequest.bidder; + bidResponse.ttl = 300; + bidResponse.netRevenue = true; + bidResponse.currency = 'USD'; + var ext = bidderBid.ext; + var vastUrl = ''; + if (ext) { + vastUrl = ext.vast_url; + } + var adUnitCode = bidRequest.adUnitCode; + var sizeObj = getVideoAdUnitSize(bidRequest); + + bidResponse.height = sizeObj.adH; + bidResponse.width = sizeObj.adW; + + switch (context) { + case OUTSTREAM: + var outstreamType = contains(OUTSTREAM_TYPES, bidRequest.params.outstreamType) ? bidRequest.params.outstreamType : '' + bidResponse.outstreamType = outstreamType; + bidResponse.ad = vastXml; + if (!bidRequest.renderer) { + const renderer = Renderer.install({ + id: bidderBid.id, + url: RENDERER_URL, + config: bidRequest.params.outstreamConfig || {}, + loaded: false, + adUnitCode + }); + renderer.setRender(outstreamRender) + bidResponse.renderer = renderer; + } else { bidResponse.adResponse = vastXml; } + break; + case INSTREAM: + bidResponse.vastUrl = vastUrl; + bidResponse.adserverTargeting = setTargeting(vastUrl); + break; + } + bidResponse.mediaType = VIDEO; + bidResponses.push(bidResponse); + } + }); + return bidResponses; +} + +/* Generate renderer for outstream ad unit */ +function outstreamRender(bid) { + bid.renderer.push(() => { + window.osRenderer({ + adResponse: bid.ad, + height: bid.height, + width: bid.width, + targetId: bid.adUnitCode, // target div id to render video + outstreamType: bid.outstreamType, + options: bid.renderer.getConfig(), + }); + }); +} + +/* Set targeting params used for instream video that is required to generate cache url */ +function setTargeting(query) { + var targeting = {}; + var hash; + var hashes = query.slice(query.indexOf('?') + 1).split('&'); + for (var i = 0; i < hashes.length; i++) { + hash = hashes[i].split('='); + targeting['hb_' + hash[0]] = hash[1]; + } + return targeting; +} + +/* Get image type with respect to the id */ +function getAssetImageType(id, assets) { + for (var i = 0; i < assets.length; i++) { + if (assets[i].id == id) { + if (assets[i].img.type == 1) { return 'icon'; } else if (assets[i].img.type == 3) { return 'image'; } + } + } + return ''; +} + +/* Get type of data asset with respect to the id */ +function getAssetDataType(id, assets) { + for (var i = 0; i < assets.length; i++) { + if (assets[i].id == id) { + if (assets[i].data.type == 1) { return 'sponsored'; } else if (assets[i].data.type == 2) { return 'desc'; } else if (assets[i].data.type == 12) { return 'cta'; } + } + } + return ''; +} + +/* Convert response assests to compatible format */ +function getNativeAssestObj(obj, assets) { + if (obj.title) { + return { + key: 'title', + value: obj.title.text + } + } + if (obj.data) { + return { + key: getAssetDataType(obj.id, assets), + value: obj.data.value + } + } + if (obj.img) { + return { + key: getAssetImageType(obj.id, assets), + value: { + url: obj.img.url, + height: obj.img.h, + width: obj.img.w + } + } + } +} + +registerBidder(spec); diff --git a/modules/datawrkzBidAdapter.md b/modules/datawrkzBidAdapter.md new file mode 100644 index 00000000000..85ea43e6dd3 --- /dev/null +++ b/modules/datawrkzBidAdapter.md @@ -0,0 +1,160 @@ +# Overview + +``` +Module Name: Datawrkz Bid Adapter +Module Type: Bidder Adapter +Maintainer: pubops@datawrkz.com +``` + +# Description + +Module that connects to Datawrkz's demand sources. +Datawrkz bid adapter supports Banner, Video (instream and outstream) and Native ad units. + +# Bid Parameters + +| Name | Scope | Type | Description | Example +| ---- | ----- | ---- | ----------- | ------- +| `site_id` | required | String | Site id | "test_site_id" +| `deals` | optional | Array | Array of deal objects | `[{id: "deal_1"},{id: "deal_2"}]` +| `bidfloor` | optional | Float | Minimum bid for this impression expressed in CPM | `0.5` +| `outstreamType` | optional | String | Type of outstream video to the played. Available options: inline, slider_top_left, slider_top_right, slider_bottom_left, slider_bottom_right, interstitial_close, and listicle | "inline" +| `outstreamConfig` | optional | Object | Configuration settings for outstream ad unit | `{ad_unit_audio: 1, show_player_close_button_after: 5, hide_player_control: 0}` + +# Deal Object +| Name | Scope | Type | Description | Example +| ---- | ----- | ---- | ----------- | ------- +| `id` | required | String | Deal id | "test_deal_id" + +# outstreamConfig +| Name | Scope | Type | Description | Example +| ---- | ----- | ---- | ----------- | ------- +| `ad_unit_audio` | optional | Integer | Set default audio option for the player. 0 to play audio on hover and 2 to mute | `0` or `2` +| `show_player_close_button_after` | optional | Integer | Show player close button after specified seconds | `5` +| `hide_player_control` | optional | Integer | Show/hide player controls. 0 to show player controls and 1 to hide | `1` + +# Test Parameters +``` +var adUnits = [ + // Banner adUnit + { + code: 'banner-div', + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]] + } + }, + bids: [{ + bidder: 'datawrkz', + params: { + site_id: 'site_id', + bidfloor: 0.5 + } + }] + }, + // Native adUnit + { + code: 'native-div', + sizes: [[1, 1]], + mediaTypes: { + native: { + title: { + required: true, + len: 80 + }, + image: { + required: true, + sizes: [300, 250] + }, + icon: { + required: true, + sizes: [50, 50] + }, + body: { + required: true, + len: 800 + }, + sponsoredBy: { + required: true + }, + cta: { + required: true + } + } + }, + bids: [{ + bidder: 'datawrkz', + params: { + site_id: 'site_id', + bidfloor: 0.5 + } + }] + }, + // Video instream adUnit + { + code: 'video-instream', + sizes: [[640, 480]], + mediaTypes: { + video: { + playerSize: [[640, 480]], + context: 'instream', + api: [1, 2], + mimes: ["video/x-ms-wmv", "video/mp4"], + protocols: [1, 2, 3], + playbackmethod: [1, 2], + minduration: 20, + maxduration: 30, + startdelay: 5, + minbitrate: 300, + maxbitrate: 1500, + delivery: [2], + linearity: 1 + }, + }, + bids: [{ + bidder: 'datawrkz', + params: { + site_id: 'site_id', + bidfloor: 0.5 + } + }] + }, + // Video outstream adUnit + { + code: 'video-outstream', + sizes: [[300, 250]], + mediaTypes: { + video: { + playerSize: [[300, 250]], + context: 'outstream', + api: [1, 2], + mimes: ["video/mp4"], + protocols: [1, 2, 3], + playbackmethod: [1, 2], + minduration: 20, + maxduration: 30, + startdelay: 5, + minbitrate: 300, + maxbitrate: 1500, + delivery: [2], + linearity: 1 + } + }, + bids: [ + { + bidder: 'datawrkz', + params: { + site_id: 'site_id', + bidfloor: 0.5, + outstreamType: 'slider_top_left', //Supported types : inline, slider_top_left, slider_top_right, slider_bottom_left, slider_bottom_right, interstitial_close, listicle + outstreamConfig: { + ad_unit_audio: 1, // 0: audio on hover, 2: always muted + show_player_close_button_after: 5, // show close button after 5 seconds + hide_player_control: 0 // 0 to show/ 1 to hide + } + } + } + ] + } +]; +``` diff --git a/test/spec/modules/datawrkzBidAdapter_spec.js b/test/spec/modules/datawrkzBidAdapter_spec.js new file mode 100644 index 00000000000..3ddc2bad1cf --- /dev/null +++ b/test/spec/modules/datawrkzBidAdapter_spec.js @@ -0,0 +1,656 @@ +import { expect } from 'chai'; +import { config } from 'src/config.js'; +import { spec } from 'modules/datawrkzBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; + +const BIDDER_CODE = 'datawrkz'; +const ENDPOINT_URL = 'https://at.datawrkz.com/exchange/openrtb23/'; +const SITE_ID = 'site_id'; +const FINAL_URL = ENDPOINT_URL + SITE_ID + '?hb=1'; + +describe('datawrkzAdapterTests', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': BIDDER_CODE, + 'params': { + 'site_id': SITE_ID, + 'bidfloor': '1.0' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when params not found', function () { + let bid = Object.assign({}, bid); + delete bid.params; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when required site_id param not found', function () { + let bid = Object.assign({}, bid); + bid.params = {'bidfloor': '1.0'} + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when adunit is adpod video', function () { + let bid = Object.assign({}, bid); + bid.params = {'bidfloor': '1.0', 'site_id': SITE_ID}; + bid.mediaTypes = { + 'video': { + 'context': 'adpod' + } + } + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const consentString = '1YA-'; + const bidderRequest = { + 'bidderCode': 'datawrkz', + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'uspConsent': consentString, + 'gdprConsent': {'gdprApplies': true}, + }; + const bannerBidRequests = [{ + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, + 'mediaTypes': {'banner': {'sizes': [[300, 250], [300, 600]]}}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + }]; + const bannerBidRequestsSingleArraySlotAndDeals = [{ + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00, 'deals': [{id: 'deal_1'}, {id: 'deal_2'}]}, + 'mediaTypes': {'banner': {}}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'sizes': [300, 250], + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + }]; + const nativeBidRequests = [{ + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, + 'mediaTypes': {'native': { + 'title': {'required': true, 'len': 80}, + 'image': {'required': true, 'sizes': [[300, 250]]}, + 'icon': {'required': true, 'sizes': [[50, 50]]}, + 'sponsoredBy': {'required': true}, + 'cta': {'required': true}, + 'body': {'required': true, 'len': 100} + }}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + }]; + const nativeBidRequestsSingleArraySlotAndDeals = [{ + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00, 'deals': [{id: 'deal_1'}, {id: 'deal_2'}]}, + 'mediaTypes': {'native': { + 'title': {'len': 80}, + 'image': {'sizes': [300, 250]}, + 'icon': {'sizes': [50, 50]}, + 'sponsoredBy': {}, + 'cta': {}, + 'body': {'len': 100} + }}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + }]; + const instreamVideoBidRequests = [{ + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, + 'mediaTypes': {'video': {'context': 'instream', 'playerSize': [[640, 480]]}}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + }]; + const instreamVideoBidRequestsSingleArraySlotAndDeals = [{ + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00, 'deals': [{id: 'deal_1'}, {id: 'deal_2'}]}, + 'mediaTypes': {'video': {'context': 'instream', 'playerSize': [640, 480]}}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + }]; + const outstreamVideoBidRequests = [{ + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, + 'mediaTypes': {'video': {'context': 'outstream', 'playerSize': [[640, 480]], 'mimes': ['video/mp4', 'video/x-flv']}}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + }]; + const outstreamVideoBidRequestsSingleArraySlotAndDeals = [{ + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00, 'deals': [{id: 'deal_1'}, {id: 'deal_2'}]}, + 'mediaTypes': {'video': {'context': 'outstream', 'playerSize': [640, 480], 'mimes': ['video/mp4', 'video/x-flv']}}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + }]; + const bidRequestsWithNoMediaType = [{ + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + }]; + + it('empty bid requests', function () { + const requests = spec.buildRequests([], bidderRequest); + assert.lengthOf(requests, 0); + }); + + it('mediaTypes missing in bid request', function () { + const requests = spec.buildRequests(bidRequestsWithNoMediaType, bidderRequest); + assert.lengthOf(requests, 0); + }); + + it('invalid media type in bid request', function () { + bidRequestsWithNoMediaType[0].mediaTypes = {'test': {}}; + const requests = spec.buildRequests(bidRequestsWithNoMediaType, bidderRequest); + assert.lengthOf(requests, 0); + }); + + it('size missing in bid request for banner', function () { + delete bidRequestsWithNoMediaType[0].mediaTypes.test; + bidRequestsWithNoMediaType[0].mediaTypes.banner = {}; + const requests = spec.buildRequests(bidRequestsWithNoMediaType, bidderRequest); + assert.lengthOf(requests, 0); + }); + + it('size array empty in bid request for banner', function () { + bidRequestsWithNoMediaType[0].mediaTypes.banner.sizes = []; + const requests = spec.buildRequests(bidRequestsWithNoMediaType, bidderRequest); + assert.lengthOf(requests, 0); + }); + + it('banner bidRequest with slot size as 2 dimensional array', function () { + sinon.stub(config, 'getConfig').withArgs('coppa').returns(true); + const requests = spec.buildRequests(bannerBidRequests, bidderRequest); + config.getConfig.restore(); + const payload = JSON.parse(requests[0].data); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(FINAL_URL); + expect(payload.imp).to.exist; + expect(payload).to.nested.include({'imp[0].banner.w': 300}); + expect(payload).to.nested.include({'imp[0].banner.h': 250}); + expect(payload).to.nested.include({'regs.ext.us_privacy': consentString}); + expect(payload).to.nested.include({'regs.ext.gdpr': '1'}); + expect(payload).to.nested.include({'regs.coppa': '1'}); + expect(requests[0].bidRequest).to.exist; + expect(requests[0].bidRequest.requestedMediaType).to.equal('banner'); + }); + + it('banner bidRequest with deals and slot size as 1 dimensional array', function () { + const requests = spec.buildRequests(bannerBidRequestsSingleArraySlotAndDeals, bidderRequest); + const payload = JSON.parse(requests[0].data); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(FINAL_URL); + expect(payload.imp).to.exist; + expect(payload).to.nested.include({'imp[0].banner.w': 300}); + expect(payload).to.nested.include({'imp[0].banner.h': 250}); + expect(payload).to.nested.include({'imp[0].pmp.deals[0].id': 'deal_1'}); + expect(payload).to.nested.include({'imp[0].pmp.deals[1].id': 'deal_2'}); + expect(requests[0].bidRequest).to.exist; + expect(requests[0].bidRequest.requestedMediaType).to.equal('banner'); + }); + + it('native bidRequest fields with slot size as 2 dimensional array', function () { + sinon.stub(config, 'getConfig').withArgs('coppa').returns(true); + const requests = spec.buildRequests(nativeBidRequests, bidderRequest); + config.getConfig.restore(); + const payload = JSON.parse(requests[0].data); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(FINAL_URL); + expect(payload.imp[0].native.request).to.exist; + expect(payload).to.nested.include({'regs.ext.us_privacy': consentString}); + expect(payload).to.nested.include({'regs.ext.gdpr': '1'}); + expect(payload).to.nested.include({'regs.coppa': '1'}); + expect(requests[0].bidRequest).to.exist; + expect(requests[0].bidRequest.requestedMediaType).to.equal('native'); + }); + + it('native bidRequest fields with deals and slot size as 1 dimensional array', function () { + const requests = spec.buildRequests(nativeBidRequestsSingleArraySlotAndDeals, bidderRequest); + const payload = JSON.parse(requests[0].data); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(FINAL_URL); + expect(payload.imp).to.exist; + expect(payload.imp[0].native.request).to.exist; + expect(payload).to.nested.include({'imp[0].pmp.deals[0].id': 'deal_1'}); + expect(payload).to.nested.include({'imp[0].pmp.deals[1].id': 'deal_2'}); + expect(requests[0].bidRequest).to.exist; + expect(requests[0].bidRequest.requestedMediaType).to.equal('native'); + }); + + it('instream video bidRequest fields with slot size as 2 dimensional array', function () { + sinon.stub(config, 'getConfig').withArgs('coppa').returns(true); + const requests = spec.buildRequests(instreamVideoBidRequests, bidderRequest); + config.getConfig.restore(); + const payload = JSON.parse(requests[0].data); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(FINAL_URL); + expect(payload).to.nested.include({'regs.ext.us_privacy': consentString}); + expect(payload).to.nested.include({'regs.ext.gdpr': '1'}); + expect(payload).to.nested.include({'regs.coppa': '1'}); + expect(requests[0].bidRequest).to.exist; + expect(requests[0].bidRequest.requestedMediaType).to.equal('video'); + }); + + it('instream video bidRequest with deals and slot size as 1 dimensional array', function () { + const requests = spec.buildRequests(instreamVideoBidRequestsSingleArraySlotAndDeals, bidderRequest); + const payload = JSON.parse(requests[0].data); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(FINAL_URL); + expect(requests[0].bidRequest).to.exist; + expect(requests[0].bidRequest.requestedMediaType).to.equal('video'); + }); + + it('outstream video bidRequest fields with slot size as 2 dimensional array', function () { + sinon.stub(config, 'getConfig').withArgs('coppa').returns(true); + const requests = spec.buildRequests(outstreamVideoBidRequests, bidderRequest); + config.getConfig.restore(); + const payload = JSON.parse(requests[0].data); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(FINAL_URL); + expect(payload).to.nested.include({'imp[0].video.w': 640}); + expect(payload).to.nested.include({'imp[0].video.h': 480}); + expect(payload).to.nested.include({'regs.ext.us_privacy': consentString}); + expect(payload).to.nested.include({'regs.ext.gdpr': '1'}); + expect(payload).to.nested.include({'regs.coppa': '1'}); + expect(requests[0].bidRequest).to.exist; + expect(requests[0].bidRequest.requestedMediaType).to.equal('video'); + }); + + it('outstream video bidRequest fields with deals and slot size as 1 dimensional array', function () { + const requests = spec.buildRequests(outstreamVideoBidRequestsSingleArraySlotAndDeals, bidderRequest); + const payload = JSON.parse(requests[0].data); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.equal(FINAL_URL); + expect(payload).to.nested.include({'imp[0].video.w': 640}); + expect(payload).to.nested.include({'imp[0].video.h': 480}); + expect(payload).to.nested.include({'imp[0].pmp.deals[0].id': 'deal_1'}); + expect(payload).to.nested.include({'imp[0].pmp.deals[1].id': 'deal_2'}); + expect(requests[0].bidRequest).to.exist; + expect(requests[0].bidRequest.requestedMediaType).to.equal('video'); + }); + }); + + describe('interpretResponse', function () { + const bidRequest = { + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, + 'mediaTypes': {'banner': {'sizes': [[300, 250], [300, 600]]}}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'sizes': [[300, 250], [300, 600]], + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + 'requestedMediaType': 'banner' + }; + const nativeBidRequest = { + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, + 'mediaTypes': {'native': { + 'title': {'required': true, 'len': 80}, + 'image': {'required': true, 'sizes': [300, 250]}, + 'icon': {'required': true, 'sizes': [50, 50]}, + 'sponsoredBy': {'required': true}, + 'cta': {'required': true}, + 'body': {'required': true} + }}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + 'requestedMediaType': 'native', + 'assets': [ + {'id': 1, 'required': 1, 'title': {'len': 80}}, + {'id': 2, 'required': 1, 'img': {'type': 3, 'w': 300, 'h': 250}}, + {'id': 3, 'required': 1, 'img': {'type': 1, 'w': 50, 'h': 50}}, + {'id': 4, 'required': 1, 'data': {'type': 1}}, + {'id': 5, 'required': 1, 'data': {'type': 12}}, + {'id': 6, 'required': 1, 'data': {'type': 2, 'len': 100}} + ] + }; + const instreamVideoBidRequest = { + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, + 'mediaTypes': {'video': {'context': 'instream', 'playerSize': [640, 480]}}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + 'requestedMediaType': 'video' + }; + const outstreamVideoBidRequest = { + 'bidder': BIDDER_CODE, + 'params': {'site_id': SITE_ID, + 'bidfloor': 1.00, + 'outstreamType': 'slider_top_left', + 'outstreamConfig': + {'ad_unit_audio': 1, 'show_player_close_button_after': 5, 'hide_player_control': 0}}, + 'mediaTypes': {'video': {'context': 'outstream', 'playerSize': [640, 480]}}, + 'adUnitCode': 'adUnitCode', + 'transactionId': 'transactionId', + 'bidId': 'bidId', + 'bidderRequestId': 'bidderRequestId', + 'auctionId': 'auctionId', + 'requestedMediaType': 'video' + }; + const request = { + 'method': 'POST', + 'url': FINAL_URL, + 'data': '', + bidRequest + }; + + it('check empty response', function () { + const result = spec.interpretResponse({}, request); + expect(result).to.deep.equal([]); + }); + + it('check if id missing in response', function () { + const serverResponse = {'body': {'seatbid': [{}]}, 'headers': {}}; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.deep.equal([]); + }); + + it('check if seatbid present in response', function () { + const serverResponse = {'body': {'id': 'id'}, 'headers': {}}; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.deep.equal([]); + }); + + it('check empty array response seatbid', function () { + const serverResponse = {'body': {'id': 'id', 'seatbid': []}, 'headers': {}}; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.deep.equal([]); + }); + + it('check bid present in seatbid', function () { + const serverResponse = {'body': {'id': 'id', 'seatbid': [{}]}, 'headers': {}}; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(0); + }); + + it('check empty array bid in seatbid', function () { + const serverResponse = {'body': {'id': 'id', 'seatbid': [{'bid': []}]}, 'headers': {}}; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(0); + }); + + it('banner response missing bid price', function () { + const serverResponse = {'body': {'id': 'id', 'seatbid': [{'bid': [{'id': 1}]}]}, 'headers': {}}; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(1); + expect(result[0].requestId).to.equal('bidId'); + expect(result[0].bidderCode).to.equal(request.bidRequest.bidder); + }); + + it('banner response', function () { + const serverResponse = { + 'body': { + 'id': 'id', + 'seatbid': [ + { + 'bid': [ + { + 'id': '1', + 'price': 1, + 'w': 300, + 'h': 250, + 'adm': 'test adm', + 'nurl': 'url' + } + ] + } + ] + }, + 'headers': {} + }; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(1); + expect(result[0].requestId).to.equal('bidId'); + expect(result[0].cpm).to.equal(1); + expect(result[0].size).to.equal(bidRequest.mediaTypes.banner.sizes); + expect(result[0].width).to.equal(300); + expect(result[0].height).to.equal(250); + expect(result[0].ad).to.equal(decodeURIComponent(serverResponse.body.seatbid[0].bid[0].adm + '')); + expect(result[0].creativeId).to.equal('1'); + expect(result[0].bidderCode).to.equal(request.bidRequest.bidder); + expect(result[0].transactionId).to.equal(request.bidRequest.transactionId); + expect(result[0].mediaType).to.equal('banner'); + }); + + it('native response missing bid price', function () { + request.bidRequest = nativeBidRequest; + const serverResponse = { + 'body': { + 'id': 'id', + 'seatbid': [ + { + 'bid': [ + { + 'id': '1', + 'w': 300, + 'h': 250, + 'adm': '{"native": {"link": {"url": "test_url"}, "imptrackers": [], "assets": [' + + '{"id": 1, "title": {"text": "Test title"}},' + + '{"id": 2, "img": {"type": 3,"url": "https://test/image", "w": 300, "h": 250}},' + + '{"id": 3, "img": {"type": 1, "url": "https://test/icon", "w": 50, "h": 50}},' + + '{"id": 4, "data": {"type": 1, "value": "Test sponsored by"}},' + + '{"id": 5, "data": {"type": 12, "value": "Test CTA"}},' + + '{"id": 6, "data": {"type": 2, "value": "Test body"}}]}}' + } + ] + } + ] + }, + 'headers': {} + }; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(1); + expect(result[0].requestId).to.equal('bidId'); + expect(result[0].bidderCode).to.equal(request.bidRequest.bidder); + }); + + it('native response', function () { + request.bidRequest = nativeBidRequest; + const serverResponse = { + 'body': { + 'id': 'id', + 'seatbid': [ + { + 'bid': [ + { + 'id': '1', + 'price': 1, + 'w': 300, + 'h': 250, + 'adm': '{"native": {"link": {"url": "test_url"}, "imptrackers": ["tracker1", "tracker2"], "assets": [' + + '{"id": 1, "title": {"text": "Test title"}},' + + '{"id": 2, "img": {"type": 3,"url": "https://test/image", "w": 300, "h": 250}},' + + '{"id": 3, "img": {"type": 1, "url": "https://test/icon", "w": 50, "h": 50}},' + + '{"id": 4, "data": {"type": 1, "value": "Test sponsored by"}},' + + '{"id": 5, "data": {"type": 12, "value": "Test CTA"}},' + + '{"id": 6, "data": {"type": 2, "value": "Test body"}}]}}' + } + ] + } + ] + }, + 'headers': {} + }; + + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(1); + expect(result[0].requestId).to.equal('bidId'); + expect(result[0].cpm).to.equal(1); + expect(result[0].native.clickUrl).to.equal('test_url'); + expect(result[0].native.impressionTrackers).to.have.lengthOf(2); + expect(result[0].native.title).to.equal('Test title'); + expect(result[0].native.image.url).to.equal('https://test/image'); + expect(result[0].native.icon.url).to.equal('https://test/icon'); + expect(result[0].native.sponsored).to.equal('Test sponsored by'); + expect(result[0].native.cta).to.equal('Test CTA'); + expect(result[0].native.desc).to.equal('Test body'); + expect(result[0].creativeId).to.equal('1'); + expect(result[0].bidderCode).to.equal(request.bidRequest.bidder); + expect(result[0].transactionId).to.equal(request.bidRequest.transactionId); + expect(result[0].mediaType).to.equal('native'); + }); + + it('video response missing bid price', function () { + request.bidRequest = instreamVideoBidRequest; + const serverResponse = { + 'body': { + 'id': 'id', + 'seatbid': [ + { + 'bid': [ + { + 'id': '1', + 'w': 640, + 'h': 480, + 'adm': '', + 'ext': { + 'vast_url': 'vast_url' + } + } + ] + } + ] + }, + 'headers': {} + }; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(1); + expect(result[0].requestId).to.equal('bidId'); + expect(result[0].bidderCode).to.equal(request.bidRequest.bidder); + }); + + it('instream video response', function () { + request.bidRequest = instreamVideoBidRequest; + const serverResponse = { + 'body': { + 'id': 'id', + 'seatbid': [ + { + 'bid': [ + { + 'id': '1', + 'price': 1, + 'w': 640, + 'h': 480, + 'adm': '', + 'ext': { + 'vast_url': 'test_vast_url?kcid=123&kaid=12&protocol=3' + } + } + ] + } + ] + }, + 'headers': {} + }; + + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(1); + expect(result[0].requestId).to.equal('bidId'); + expect(result[0].cpm).to.equal(1); + expect(result[0].width).to.equal(640); + expect(result[0].height).to.equal(480); + expect(result[0].vastUrl).to.equal('test_vast_url?kcid=123&kaid=12&protocol=3'); + expect(result[0].adserverTargeting.hb_kcid).to.equal('123'); + expect(result[0].adserverTargeting.hb_kaid).to.equal('12'); + expect(result[0].adserverTargeting.hb_protocol).to.equal('3'); + expect(result[0].creativeId).to.equal('1'); + expect(result[0].bidderCode).to.equal(request.bidRequest.bidder); + expect(result[0].transactionId).to.equal(request.bidRequest.transactionId); + expect(result[0].mediaType).to.equal('video'); + }); + + it('outstream video response', function () { + request.bidRequest = outstreamVideoBidRequest; + const serverResponse = { + 'body': { + 'id': 'id', + 'seatbid': [ + { + 'bid': [ + { + 'id': '1', + 'price': 1, + 'w': 640, + 'h': 480, + 'adm': '', + 'ext': { + 'vast_url': 'vast_url' + } + } + ] + } + ] + }, + 'headers': {} + }; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(1); + expect(result[0].requestId).to.equal('bidId'); + expect(result[0].cpm).to.equal(1); + expect(result[0].width).to.equal(640); + expect(result[0].height).to.equal(480); + expect(result[0].outstreamType).to.equal('slider_top_left'); + expect(result[0].ad).to.equal(''); + expect(result[0].renderer).to.exist; + expect(result[0].creativeId).to.equal('1'); + expect(result[0].bidderCode).to.equal(request.bidRequest.bidder); + expect(result[0].transactionId).to.equal(request.bidRequest.transactionId); + expect(result[0].mediaType).to.equal('video'); + }); + }); +}); From 91ce750022daedff8bc70f5fee94088d7a6cda18 Mon Sep 17 00:00:00 2001 From: Saar Amrani <89377180+saar120@users.noreply.github.com> Date: Tue, 16 Aug 2022 17:21:24 +0300 Subject: [PATCH 070/246] Vidazoo Bid Adapter: Cookie sync improvements (#8834) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(module): VidazooBidAdapter - send top query params to server * feat(module): changed userSync url and passes cids from responses to usersync. * feat(module): fixed failing test. Co-authored-by: Udi Talias Co-authored-by: roman --- modules/vidazooBidAdapter.js | 10 ++-- test/spec/modules/vidazooBidAdapter_spec.js | 59 +++++++++++++-------- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index 39d37d55a91..41f3f1f2888 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -1,4 +1,4 @@ -import { _each, deepAccess, parseSizesInput, parseUrl } from '../src/utils.js'; +import {_each, deepAccess, parseSizesInput, parseUrl, uniques} from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; @@ -191,17 +191,19 @@ function getUserSyncs(syncOptions, responses, gdprConsent = {}, uspConsent = '') let syncs = []; const { iframeEnabled, pixelEnabled } = syncOptions; const { gdprApplies, consentString = '' } = gdprConsent; - const params = `?gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(consentString || '')}&us_privacy=${encodeURIComponent(uspConsent || '')}` + + const cidArr = responses.filter(resp => deepAccess(resp, 'body.cid')).map(resp => resp.body.cid).filter(uniques); + const params = `?cid=${encodeURIComponent(cidArr.join(','))}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(consentString || '')}&us_privacy=${encodeURIComponent(uspConsent || '')}` if (iframeEnabled) { syncs.push({ type: 'iframe', - url: `https://prebid.cootlogix.com/api/sync/iframe/${params}` + url: `https://sync.cootlogix.com/api/sync/iframe/${params}` }); } if (pixelEnabled) { syncs.push({ type: 'image', - url: `https://prebid.cootlogix.com/api/sync/image/${params}` + url: `https://sync.cootlogix.com/api/sync/image/${params}` }); } return syncs; diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index 1b4ed4b170d..d30b9f31417 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -1,4 +1,4 @@ -import { expect } from 'chai'; +import {expect} from 'chai'; import { spec as adapter, SUPPORTED_ID_SYSTEMS, @@ -15,8 +15,8 @@ import { getVidazooSessionId, } from 'modules/vidazooBidAdapter.js'; import * as utils from 'src/utils.js'; -import { version } from 'package.json'; -import { useFakeTimers } from 'sinon'; +import {version} from 'package.json'; +import {useFakeTimers} from 'sinon'; const SUB_DOMAIN = 'openrtb'; @@ -55,6 +55,7 @@ const BIDDER_REQUEST = { const SERVER_RESPONSE = { body: { + cid: 'testcid123', results: [{ 'ad': '', 'price': 0.8, @@ -84,7 +85,7 @@ const REQUEST = { function getTopWindowQueryParams() { try { - const parsedUrl = utils.parseUrl(window.top.document.URL, { decodeSearchAsString: true }); + const parsedUrl = utils.parseUrl(window.top.document.URL, {decodeSearchAsString: true}); return parsedUrl.search; } catch (e) { return ''; @@ -196,19 +197,27 @@ describe('VidazooBidAdapter', function () { }); describe('getUserSyncs', function () { it('should have valid user sync with iframeEnabled', function () { - const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', - url: 'https://prebid.cootlogix.com/api/sync/iframe/?gdpr=0&gdpr_consent=&us_privacy=' + url: 'https://sync.cootlogix.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=' + }]); + }); + + it('should have valid user sync with cid on response', function () { + const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + expect(result).to.deep.equal([{ + type: 'iframe', + url: 'https://sync.cootlogix.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=' }]); }); it('should have valid user sync with pixelEnabled', function () { - const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ - 'url': 'https://prebid.cootlogix.com/api/sync/image/?gdpr=0&gdpr_consent=&us_privacy=', + 'url': 'https://sync.cootlogix.com/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=', 'type': 'image' }]); }) @@ -221,12 +230,12 @@ describe('VidazooBidAdapter', function () { }); it('should return empty array when there is no ad', function () { - const responses = adapter.interpretResponse({ price: 1, ad: '' }); + const responses = adapter.interpretResponse({price: 1, ad: ''}); expect(responses).to.be.empty; }); it('should return empty array when there is no price', function () { - const responses = adapter.interpretResponse({ price: null, ad: 'great ad' }); + const responses = adapter.interpretResponse({price: null, ad: 'great ad'}); expect(responses).to.be.empty; }); @@ -265,10 +274,14 @@ describe('VidazooBidAdapter', function () { const userId = (function () { switch (idSystemProvider) { - case 'lipb': return { lipbid: id }; - case 'parrableId': return { eid: id }; - case 'id5id': return { uid: id }; - default: return id; + case 'lipb': + return {lipbid: id}; + case 'parrableId': + return {eid: id}; + case 'id5id': + return {uid: id}; + default: + return id; } })(); @@ -285,18 +298,18 @@ describe('VidazooBidAdapter', function () { describe('alternate param names extractors', function () { it('should return undefined when param not supported', function () { - const cid = extractCID({ 'c_id': '1' }); - const pid = extractPID({ 'p_id': '1' }); - const subDomain = extractSubDomain({ 'sub_domain': 'prebid' }); + const cid = extractCID({'c_id': '1'}); + const pid = extractPID({'p_id': '1'}); + const subDomain = extractSubDomain({'sub_domain': 'prebid'}); expect(cid).to.be.undefined; expect(pid).to.be.undefined; expect(subDomain).to.be.undefined; }); it('should return value when param supported', function () { - const cid = extractCID({ 'cID': '1' }); - const pid = extractPID({ 'Pid': '2' }); - const subDomain = extractSubDomain({ 'subDOMAIN': 'prebid' }); + const cid = extractCID({'cID': '1'}); + const pid = extractPID({'Pid': '2'}); + const subDomain = extractSubDomain({'subDOMAIN': 'prebid'}); expect(cid).to.be.equal('1'); expect(pid).to.be.equal('2'); expect(subDomain).to.be.equal('prebid'); @@ -409,7 +422,7 @@ describe('VidazooBidAdapter', function () { now }); setStorageItem('myKey', 2020); - const { value, created } = getStorageItem('myKey'); + const {value, created} = getStorageItem('myKey'); expect(created).to.be.equal(now); expect(value).to.be.equal(2020); expect(typeof value).to.be.equal('number'); @@ -425,8 +438,8 @@ describe('VidazooBidAdapter', function () { }); it('should parse JSON value', function () { - const data = JSON.stringify({ event: 'send' }); - const { event } = tryParseJSON(data); + const data = JSON.stringify({event: 'send'}); + const {event} = tryParseJSON(data); expect(event).to.be.equal('send'); }); From 749afff9191a49fc33710e7e95514ecadcf3fcd6 Mon Sep 17 00:00:00 2001 From: Jason Piros Date: Wed, 17 Aug 2022 07:05:23 -0700 Subject: [PATCH 071/246] consumableBidAdapter - add video support (#8793) --- modules/consumableBidAdapter.js | 22 ++- .../spec/modules/consumableBidAdapter_spec.js | 139 ++++++++++++++++++ 2 files changed, 159 insertions(+), 2 deletions(-) diff --git a/modules/consumableBidAdapter.js b/modules/consumableBidAdapter.js index f6c470d82a9..6d502b24e81 100644 --- a/modules/consumableBidAdapter.js +++ b/modules/consumableBidAdapter.js @@ -1,6 +1,7 @@ import { logWarn, createTrackPixelHtml, deepAccess, isArray, deepSetValue } from '../src/utils.js'; import {config} from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; const BIDDER_CODE = 'consumable'; @@ -11,6 +12,7 @@ let bidder = 'consumable'; export const spec = { code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO], /** * Determines whether or not the given bid request is valid. @@ -82,6 +84,10 @@ export const spec = { adTypes: bid.adTypes || getSize(sizes) }, bid.params); + if (bid.mediaTypes.video && bid.mediaTypes.video.playerSize) { + placement.video = bid.mediaTypes.video; + } + if (placement.networkId && placement.siteId && placement.unitId && placement.unitName) { data.placements.push(placement); } @@ -155,6 +161,13 @@ export const spec = { if (decision.mediaType) { bid.meta.mediaType = decision.mediaType; + + if (decision.mediaType === 'video') { + bid.mediaType = 'video'; + bid.vastUrl = decision.vastUrl || undefined; + bid.vastXml = decision.vastXml || undefined; + bid.videoCacheKey = decision.uuid || undefined; + } } bidResponses.push(bid); @@ -240,8 +253,13 @@ function getSize(sizes) { } function retrieveAd(decision, unitId, unitName) { - let ad = decision.contents && decision.contents[0] && decision.contents[0].body + createTrackPixelHtml(decision.impressionUrl); - + let ad; + if (decision.contents && decision.contents[0]) { + ad = decision.contents[0].body + createTrackPixelHtml(decision.impressionUrl); + } + if (decision.vastXml) { + ad = decision.vastXml; + } return ad; } diff --git a/test/spec/modules/consumableBidAdapter_spec.js b/test/spec/modules/consumableBidAdapter_spec.js index 4e860cb771d..f71cf26b3f9 100644 --- a/test/spec/modules/consumableBidAdapter_spec.js +++ b/test/spec/modules/consumableBidAdapter_spec.js @@ -129,6 +129,53 @@ const BIDDER_REQUEST_2 = { } }; +const BIDDER_REQUEST_VIDEO = { + bidderCode: 'consumable', + auctionId: 'a4713c32-3762-4798-b342-4ab810ca770d', + bidderRequestId: '109f2a181342a9', + bidRequest: [ + { + bidder: 'consumable', + params: { + networkId: 9969, + siteId: 730181, + unitId: 123456, + unitName: 'cnsmbl-unit' + }, + placementCode: 'div-gpt-ad-1487778092495-0', + mediaTypes: { + video: { + playerSize: [188, 106], + context: 'instream', + mimes: ['application/javascript', 'application/x-mpegurl', 'video/3gpp', 'video/mp4', 'video/mpeg', 'video/ogg', 'video/webm', 'video/x-m4v', 'video/x-ms-asf', 'video/x-ms-wmv', 'video/x-msvideo'], + minduration: 0, + maxduration: 120, + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + api: [1, 2], + linearity: 1 + } + }, + bidId: '6202d555b2f94537', + bidderRequestId: '109f2a181342a9', + auctionId: 'a4713c32-3762-4798-b342-4ab810ca770d' + } + ], + gdprConsent: { + consentString: 'consent-test', + gdprApplies: true + }, + refererInfo: { + referer: 'http://example.com/page.html', + reachedTop: true, + numIframes: 2, + stack: [ + 'http://example.com/page.html', + 'http://example.com/iframe1.html', + 'http://example.com/iframe2.html' + ] + } +}; + const BIDDER_REQUEST_EMPTY = { bidderCode: 'consumable', auctionId: 'b06458ef-4fe5-4a0b-a61b-bccbcedb7b11', @@ -259,6 +306,58 @@ const AD_SERVER_RESPONSE_2 = { } }; +const AD_SERVER_RESPONSE_VIDEO_1 = { + 'headers': null, + 'body': { + 'user': { 'key': 'ue1-2d33e91b71e74929b4aeecc23f4376f1' }, + 'pixels': [{ 'type': 'image', 'url': '//sync.serverbid.com/ss/' }], + 'decisions': { + '6202d555b2f94537': { + 'adId': 3866158402, + 'creativeId': 'C1-somo-test-video', + 'width': 640, + 'height': 480, + 'pricing': { + 'clearPrice': 1.58 + }, + 'vastUrl': 'https://x.serverbid.com/rtb/v?auc=217c051d06b011ed9cbc72b17f01ec03&sc=1.575&s=22&a=9dcab16d340d664310c2135a76989fe946a9d46e5d5f24ff5e2f17bffbb7704a43638bd3f600951e&n=9&r=0&t=1658158906595', + 'uuid': 'f1e7287514ce11ed9c1de2b3ba87449a', + 'bidderName': 'consumable', + 'adomain': ['consumabletv.com'], + 'cats': ['IAB3-1'], + 'mediaType': 'video', + 'networkId': 1 + } + } + } +}; + +const AD_SERVER_RESPONSE_VIDEO_2 = { + 'headers': null, + 'body': { + 'user': { 'key': 'ue1-2d33e91b71e74929b4aeecc23f4376f1' }, + 'pixels': [{ 'type': 'image', 'url': '//sync.serverbid.com/ss/' }], + 'decisions': { + '6202d555b2f94537': { + 'adId': 3866158402, + 'creativeId': 'C1-somo-test-video', + 'width': 640, + 'height': 480, + 'pricing': { + 'clearPrice': 1.58 + }, + 'vastXml': '', + 'uuid': 'f1e7287514ce11ed9c1de2b3ba87449a', + 'bidderName': 'consumable', + 'adomain': ['consumabletv.com'], + 'cats': ['IAB3-1'], + 'mediaType': 'video', + 'networkId': 1 + } + } + } +}; + const BUILD_REQUESTS_OUTPUT = { method: 'POST', url: 'https://e.serverbid.com/api/v2', @@ -267,6 +366,14 @@ const BUILD_REQUESTS_OUTPUT = { bidderRequest: BIDDER_REQUEST_2 }; +const BUILD_REQUESTS_VIDEO_OUTPUT = { + method: 'POST', + url: 'https://e.serverbid.com/api/v2', + data: '', + bidRequest: BIDDER_REQUEST_VIDEO.bidRequest, + bidderRequest: BIDDER_REQUEST_VIDEO +}; + describe('Consumable BidAdapter', function () { let adapter = spec; @@ -382,6 +489,13 @@ describe('Consumable BidAdapter', function () { expect(data.coppa).to.be.undefined; }); + + it('should contain video object for video requests', function () { + let request = spec.buildRequests(BIDDER_REQUEST_VIDEO.bidRequest, BIDDER_REQUEST_VIDEO); + let data = JSON.parse(request.data); + + expect(data.placements[0].video).to.deep.equal(BIDDER_REQUEST_VIDEO.bidRequest[0].mediaTypes.video); + }); }); describe('interpretResponse validation', function () { it('response should have valid bidderCode', function () { @@ -421,6 +535,31 @@ describe('Consumable BidAdapter', function () { }); }); + it('registers video bids with vastUrl', function () { + let bids = spec.interpretResponse(AD_SERVER_RESPONSE_VIDEO_1, BUILD_REQUESTS_VIDEO_OUTPUT); + + bids.forEach(b => { + expect(b.mediaType).to.equal('video'); + expect(b.meta).to.have.property('mediaType', 'video'); + expect(b.vastUrl).to.equal('https://x.serverbid.com/rtb/v?auc=217c051d06b011ed9cbc72b17f01ec03&sc=1.575&s=22&a=9dcab16d340d664310c2135a76989fe946a9d46e5d5f24ff5e2f17bffbb7704a43638bd3f600951e&n=9&r=0&t=1658158906595'); + expect(b.vastXml).to.be.undefined; + expect(b.videoCacheKey).to.equal('f1e7287514ce11ed9c1de2b3ba87449a'); + }); + }) + + it('registers video bids with vastXml', function () { + let bids = spec.interpretResponse(AD_SERVER_RESPONSE_VIDEO_2, BUILD_REQUESTS_VIDEO_OUTPUT); + + bids.forEach(b => { + expect(b.mediaType).to.equal('video'); + expect(b.meta).to.have.property('mediaType', 'video'); + expect(b.vastXml).to.equal(''); + expect(b.vastUrl).to.be.undefined; + expect(b.ad).to.equal(''); + expect(b.videoCacheKey).to.equal('f1e7287514ce11ed9c1de2b3ba87449a'); + }); + }) + it('handles nobid responses', function () { let EMPTY_RESP = Object.assign({}, AD_SERVER_RESPONSE, {'body': {'decisions': null}}) let bids = spec.interpretResponse(EMPTY_RESP, BUILD_REQUESTS_OUTPUT); From 5e983cde34630acf35e354a8f25ebbbcbf5018d4 Mon Sep 17 00:00:00 2001 From: Vic R <103455651+victorlassomarketing@users.noreply.github.com> Date: Wed, 17 Aug 2022 08:20:41 -0700 Subject: [PATCH 072/246] Lasso Bid Adapter: update version and request credential setting (#8848) * update Lasso adapter request setting * update empty ad response logic --- modules/lassoBidAdapter.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/lassoBidAdapter.js b/modules/lassoBidAdapter.js index b2f3ccf9a7c..da48bc460c7 100644 --- a/modules/lassoBidAdapter.js +++ b/modules/lassoBidAdapter.js @@ -47,7 +47,7 @@ export const spec = { params: JSON.stringify(bidRequest.params), crumbs: JSON.stringify(bidRequest.crumbs), prebidVersion: '$prebid.version$', - version: 1, + version: 2, coppa: config.getConfig('coppa') == true ? 1 : 0, ccpa: bidderRequest.uspConsent || undefined } @@ -57,7 +57,7 @@ export const spec = { url: getBidRequestUrl(aimXR), data: payload, options: { - withCredentials: false + withCredentials: true }, }; }); @@ -67,7 +67,7 @@ export const spec = { const response = serverResponse && serverResponse.body; const bidResponses = []; - if (!response) { + if (!response || !response.bid.ad) { return bidResponses; } From 984b5cfa7752da9282620f17ecbcd7e2b3283631 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Wed, 17 Aug 2022 08:21:44 -0700 Subject: [PATCH 073/246] Prebid core: fix bug with some native assets being lost from ortb native responses (#8785) * Prebid core: fix bug with some native assets being lost from ortb native responses * Do not throw if native response contains assets that were not requested --- src/native.js | 109 +++++++++++++++++++-------------------- test/spec/native_spec.js | 96 ++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 57 deletions(-) diff --git a/src/native.js b/src/native.js index bd20aa5d399..470ac0b9a32 100644 --- a/src/native.js +++ b/src/native.js @@ -366,34 +366,9 @@ export function getNativeTargeting(bid, {index = auctionManager.index} = {}) { return keyValues; } -/** - * Constructs a message object containing asset values for each of the - * requested data keys. - */ -export function getAssetMessage(data, adObject) { - const message = { - message: 'assetResponse', - adId: data.adId, - assets: [], - }; - - if (adObject.native.hasOwnProperty('adTemplate')) { - message.adTemplate = getAssetValue(adObject.native['adTemplate']); - } if (adObject.native.hasOwnProperty('rendererUrl')) { - message.rendererUrl = getAssetValue(adObject.native['rendererUrl']); - } - - data.assets.forEach(asset => { - const key = getKeyByValue(CONSTANTS.NATIVE_KEYS, asset); - const value = getAssetValue(adObject.native[key]); - - message.assets.push({ key, value }); - }); - - return message; -} +const getNativeRequest = (bidResponse) => auctionManager.index.getAdUnit(bidResponse)?.nativeOrtbRequest; -export function getAllAssetsMessage(data, adObject, {getNativeReq = (bidResponse) => auctionManager.index.getAdUnit(bidResponse).nativeOrtbRequest} = {}) { +function assetsMessage(data, adObject, keys, {getNativeReq = getNativeRequest} = {}) { const message = { message: 'assetResponse', adId: data.adId, @@ -401,12 +376,12 @@ export function getAllAssetsMessage(data, adObject, {getNativeReq = (bidResponse // Pass to Prebid Universal Creative all assets, the legacy ones + the ortb ones (under ortb property) const ortbRequest = getNativeReq(adObject); - let nativeReq = adObject.native; + let nativeResp = adObject.native; const ortbResponse = adObject.native?.ortb; let legacyResponse = {}; if (ortbRequest && ortbResponse) { legacyResponse = toLegacyResponse(ortbResponse, ortbRequest); - nativeReq = { + nativeResp = { ...adObject.native, ...legacyResponse }; @@ -416,20 +391,20 @@ export function getAllAssetsMessage(data, adObject, {getNativeReq = (bidResponse } message.assets = []; - Object.keys(nativeReq).forEach(function(key) { - if (key === 'adTemplate' && nativeReq[key]) { - message.adTemplate = getAssetValue(nativeReq[key]); - } else if (key === 'rendererUrl' && nativeReq[key]) { - message.rendererUrl = getAssetValue(nativeReq[key]); + (keys == null ? Object.keys(nativeResp) : keys).forEach(function(key) { + if (key === 'adTemplate' && nativeResp[key]) { + message.adTemplate = getAssetValue(nativeResp[key]); + } else if (key === 'rendererUrl' && nativeResp[key]) { + message.rendererUrl = getAssetValue(nativeResp[key]); } else if (key === 'ext') { - Object.keys(nativeReq[key]).forEach(extKey => { - if (nativeReq[key][extKey]) { - const value = getAssetValue(nativeReq[key][extKey]); + Object.keys(nativeResp[key]).forEach(extKey => { + if (nativeResp[key][extKey]) { + const value = getAssetValue(nativeResp[key][extKey]); message.assets.push({ key: extKey, value }); } }) - } else if (nativeReq[key] && CONSTANTS.NATIVE_KEYS.hasOwnProperty(key)) { - const value = getAssetValue(nativeReq[key]); + } else if (nativeResp[key] && CONSTANTS.NATIVE_KEYS.hasOwnProperty(key)) { + const value = getAssetValue(nativeResp[key]); message.assets.push({ key, value }); } @@ -437,6 +412,19 @@ export function getAllAssetsMessage(data, adObject, {getNativeReq = (bidResponse return message; } +/** + * Constructs a message object containing asset values for each of the + * requested data keys. + */ +export function getAssetMessage(data, adObject, {getNativeReq = getNativeRequest} = {}) { + const keys = data.assets.map((k) => getKeyByValue(CONSTANTS.NATIVE_KEYS, k)); + return assetsMessage(data, adObject, keys, {getNativeReq}); +} + +export function getAllAssetsMessage(data, adObject, {getNativeReq = getNativeRequest} = {}) { + return assetsMessage(data, adObject, null, {getNativeReq}); +} + /** * Native assets can be a string or an object with a url prop. Returns the value * appropriate for sending in adserver targeting or placeholder replacement. @@ -689,40 +677,47 @@ export function legacyPropertiesToOrtbNative(legacyNative) { } export function toOrtbNativeResponse(legacyResponse, ortbRequest) { - // copy the request, so we don't pollute it with response data below - ortbRequest = deepClone(ortbRequest); - const ortbResponse = { ...legacyPropertiesToOrtbNative(legacyResponse), assets: [] }; + + function useRequestAsset(predicate, fn) { + let asset = ortbRequest.assets.find(predicate); + if (asset != null) { + asset = deepClone(asset); + fn(asset); + ortbResponse.assets.push(asset); + } + } + Object.keys(legacyResponse).filter(key => !!legacyResponse[key]).forEach(key => { const value = legacyResponse[key]; switch (key) { // process titles case 'title': - const titleAsset = ortbRequest.assets.find(asset => asset.title != null); - titleAsset.title = { - text: value - }; - ortbResponse.assets.push(titleAsset); + useRequestAsset(asset => asset.title != null, titleAsset => { + titleAsset.title = { + text: value + }; + }) break; case 'image': case 'icon': const imageType = key === 'image' ? NATIVE_IMAGE_TYPES.MAIN : NATIVE_IMAGE_TYPES.ICON; - const imageAsset = ortbRequest.assets.find(asset => asset.img != null && asset.img.type == imageType); - imageAsset.img = { - url: value - }; - ortbResponse.assets.push(imageAsset); + useRequestAsset(asset => asset.img != null && asset.img.type === imageType, imageAsset => { + imageAsset.img = { + url: value + }; + }) break; default: if (key in PREBID_NATIVE_DATA_KEYS_TO_ORTB) { - const dataAsset = ortbRequest.assets.find(asset => asset.data != null && asset.data.type === NATIVE_ASSET_TYPES[PREBID_NATIVE_DATA_KEYS_TO_ORTB[key]]); - dataAsset.data = { - value - }; - ortbResponse.assets.push(dataAsset); + useRequestAsset(asset => asset.data != null && asset.data.type === NATIVE_ASSET_TYPES[PREBID_NATIVE_DATA_KEYS_TO_ORTB[key]], dataAsset => { + dataAsset.data = { + value + }; + }) } break; } diff --git a/test/spec/native_spec.js b/test/spec/native_spec.js index a6f8e7ae891..19f417bd88f 100644 --- a/test/spec/native_spec.js +++ b/test/spec/native_spec.js @@ -400,6 +400,66 @@ describe('native.js', function () { value: bid.native.ext.foo, }); }); + + const SAMPLE_ORTB_REQUEST = toOrtbNativeRequest({ + title: 'vtitle', + body: 'vbody' + }); + const SAMPLE_ORTB_RESPONSE = { + native: { + ortb: { + link: { + url: 'url' + }, + assets: [ + { + id: 0, + title: { + text: 'vtitle' + } + }, + { + id: 1, + data: { + value: 'vbody' + } + } + ] + } + } + } + describe('getAllAssetsMessage', () => { + it('returns assets in legacy format for ortb responses', () => { + const actual = getAllAssetsMessage({}, SAMPLE_ORTB_RESPONSE, {getNativeReq: () => SAMPLE_ORTB_REQUEST}); + expect(actual.assets).to.eql([ + { + key: 'clickUrl', + value: 'url' + }, + { + key: 'title', + value: 'vtitle' + }, + { + key: 'body', + value: 'vbody' + }, + ]) + }); + }); + describe('getAssetsMessage', () => { + Object.entries({ + 'hb_native_title': {key: 'title', value: 'vtitle'}, + 'hb_native_body': {key: 'body', value: 'vbody'} + }).forEach(([tkey, assetVal]) => { + it(`returns ${tkey} asset in legacy format for ortb responses`, () => { + const actual = getAssetMessage({ + assets: [tkey] + }, SAMPLE_ORTB_RESPONSE, {getNativeReq: () => SAMPLE_ORTB_REQUEST}) + expect(actual.assets).to.eql([assetVal]) + }) + }) + }) }); describe('validate native openRTB', function () { @@ -1022,3 +1082,39 @@ describe('fireClickTrackers', () => { urls.forEach(url => sinon.assert.calledWith(fetchURL, url)); }) }) + +describe('toOrtbNativeResponse', () => { + it('should work when there are unrequested assets in the response', () => { + const legacyResponse = { + 'title': 'vtitle', + 'body': 'vbody' + } + const request = toOrtbNativeRequest({ + title: { + required: 'true' + }, + + }); + const ortbResponse = toOrtbNativeResponse(legacyResponse, request); + expect(ortbResponse.assets.length).to.eql(1); + }); + + it('should not modify the request', () => { + const legacyResponse = { + title: 'vtitle' + } + const request = toOrtbNativeRequest({ + title: { + required: true + } + }); + const requestCopy = JSON.parse(JSON.stringify(request)); + const response = toOrtbNativeResponse(legacyResponse, request); + expect(request).to.eql(requestCopy); + sinon.assert.match(response.assets[0], { + title: { + text: 'vtitle' + } + }) + }) +}) From a4fdf7d98d7d0b1522d1fa3b1fbf47d2c5230e93 Mon Sep 17 00:00:00 2001 From: omerBrowsi <54346241+omerBrowsi@users.noreply.github.com> Date: Wed, 17 Aug 2022 20:45:03 +0300 Subject: [PATCH 074/246] Browsi RTD Module: add support for page view billable events (#8829) * real time data module, browsi sub module for real time data, new hook bidsBackCallback, fix for config unsubscribe * change timeout&primary ad server only to auctionDelay update docs * support multiple providers * change promise to callbacks configure submodule on submodules.json * bug fixes * use Prebid ajax * tests fix * browsi real time data provider improvements * real time data module, browsi sub module for real time data, new hook bidsBackCallback, fix for config unsubscribe * change timeout&primary ad server only to auctionDelay update docs * support multiple providers * change promise to callbacks configure submodule on submodules.json * bug fixes * use Prebid ajax * tests fix * browsi real time data provider improvements * Browsi RTD provider - add billiable event type * Browsi RTD provider - add billiable event type * lint fixes --- modules/browsiRtdProvider.js | 34 ++++++++++++----- test/spec/modules/browsiRtdProvider_spec.js | 41 ++++++++++++++++++++- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/modules/browsiRtdProvider.js b/modules/browsiRtdProvider.js index ffc946cbb33..d39e500e4f9 100644 --- a/modules/browsiRtdProvider.js +++ b/modules/browsiRtdProvider.js @@ -57,6 +57,16 @@ export function addBrowsiTag(data) { return script; } +export function sendPageviewEvent(eventType) { + if (eventType === 'PAGEVIEW') { + events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { + vendor: 'browsi', + type: 'pageview', + billingId: generateUUID() + }) + } +} + /** * collect required data from page * send data to browsi server to get predictions @@ -262,10 +272,11 @@ function getPredictionsFromServer(url) { try { const data = JSON.parse(response); if (data && data.p && data.kn) { - setData({p: data.p, kn: data.kn, pmd: data.pmd}); + setData({p: data.p, kn: data.kn, pmd: data.pmd, bet: data.bet}); } else { setData({}); } + sendPageviewEvent(data.bet); addBrowsiTag(data); } catch (err) { logError('unable to parse data'); @@ -336,19 +347,22 @@ export const browsiSubmodule = { function getTargetingData(uc, c, us, a) { const targetingData = getRTD(uc); - const auctionId = a.auctionId + const auctionId = a.auctionId; + const sendAdRequestEvent = _browsiData['bet'] === 'AD_REQUEST'; uc.forEach(auc => { if (isNumber(_ic[auc])) { _ic[auc] = _ic[auc] + 1; } - const transactionId = a.adUnits.find(adUnit => adUnit.code === auc).transactionId; - events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { - vendor: 'browsi', - type: 'adRequest', - billingId: generateUUID(), - transactionId: transactionId, - auctionId: auctionId - }) + if (sendAdRequestEvent) { + const transactionId = a.adUnits.find(adUnit => adUnit.code === auc).transactionId; + events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { + vendor: 'browsi', + type: 'adRequest', + billingId: generateUUID(), + transactionId: transactionId, + auctionId: auctionId + }) + } }); logInfo('Browsi RTD provider returned targeting data', targetingData, 'for', uc) return targetingData; diff --git a/test/spec/modules/browsiRtdProvider_spec.js b/test/spec/modules/browsiRtdProvider_spec.js index c36b48c5105..19483047827 100644 --- a/test/spec/modules/browsiRtdProvider_spec.js +++ b/test/spec/modules/browsiRtdProvider_spec.js @@ -3,6 +3,7 @@ import {makeSlot} from '../integration/faker/googletag.js'; import * as utils from '../../../src/utils' import * as events from '../../../src/events'; import * as sinon from 'sinon'; +import {sendPageviewEvent} from '../../../modules/browsiRtdProvider.js'; describe('browsi Real time data sub module', function () { const conf = { @@ -160,7 +161,19 @@ describe('browsi Real time data sub module', function () { }) }) - describe('should emit billable event', function () { + describe('should emit ad request billable event', function () { + before(() => { + const data = { + p: { + 'adUnit1': {ps: {0: 0.234}}, + 'adUnit2': {ps: {0: 0.134}}}, + kn: 'bv', + pmd: undefined, + bet: 'AD_REQUEST' + }; + browsiRTD.setData(data); + }) + beforeEach(() => { eventsEmitSpy.resetHistory(); }) @@ -232,4 +245,30 @@ describe('browsi Real time data sub module', function () { expect(callArguments).to.eql(expectedCall); }) }) + + describe('should emit pageveiw billable event', function () { + beforeEach(() => { + eventsEmitSpy.resetHistory(); + }) + it('should send event if type is correct', function () { + sendPageviewEvent('PAGEVIEW') + + const expectedCall = { + vendor: 'browsi', + type: 'pageview', + } + + expect(eventsEmitSpy.callCount).to.equal(1); + const callArguments = eventsEmitSpy.getCalls()[0].args[1]; + // billing id is random, we can't check its value + delete callArguments['billingId']; + expect(callArguments).to.eql(expectedCall); + }) + it('should not send event if type is incorrect', function () { + sendPageviewEvent('AD_REQUEST'); + sendPageviewEvent('INACTIVE'); + sendPageviewEvent(undefined); + expect(eventsEmitSpy.callCount).to.equal(0); + }) + }) }); From 2fd7e0f5b88892ac0116050d4c386a621dacf64d Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Wed, 17 Aug 2022 11:02:09 -0700 Subject: [PATCH 075/246] Readme: remove dead dev dependency and maintenance badges (#8849) * Readme: remove dead dev dependency badge and issue * Remove maintainability and add back issues --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 11dc01e8f07..bbc0d79ab41 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ [![Build Status](https://circleci.com/gh/prebid/Prebid.js.svg?style=svg)](https://circleci.com/gh/prebid/Prebid.js) [![Percentage of issues still open](http://isitmaintained.com/badge/open/prebid/Prebid.js.svg)](http://isitmaintained.com/project/prebid/Prebid.js "Percentage of issues still open") -[![Code Climate](https://codeclimate.com/github/prebid/Prebid.js/badges/gpa.svg)](https://codeclimate.com/github/prebid/Prebid.js) [![Coverage Status](https://coveralls.io/repos/github/prebid/Prebid.js/badge.svg)](https://coveralls.io/github/prebid/Prebid.js) -[![devDependencies Status](https://david-dm.org/prebid/Prebid.js/dev-status.svg)](https://david-dm.org/prebid/Prebid.js?type=dev) [![Total Alerts](https://img.shields.io/lgtm/alerts/g/prebid/Prebid.js.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/prebid/Prebid.js/alerts/) # Prebid.js From c5f33d36c691448dcba967a172f8dc75b4285082 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Wed, 17 Aug 2022 19:56:47 +0000 Subject: [PATCH 076/246] Prebid 7.11.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 749fafaebc4..01d037dee63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.10.0-pre", + "version": "7.11.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 58898eb0110..2b0a7215765 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.11.0-pre", + "version": "7.11.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 059b7c5cbf25806f09af9dc21604260b53507d7e Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Wed, 17 Aug 2022 19:56:48 +0000 Subject: [PATCH 077/246] Increment version to 7.12.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 01d037dee63..a6ae1e5a3e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.11.0", + "version": "7.12.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 2b0a7215765..a9a4c120c48 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.11.0", + "version": "7.12.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 94d34d1783fd6712613247d6696b8cc2ca9579a6 Mon Sep 17 00:00:00 2001 From: m-oranskaya <99481039+m-oranskaya@users.noreply.github.com> Date: Thu, 18 Aug 2022 20:13:17 +1100 Subject: [PATCH 078/246] Adriver Bid and Id Modules: buyerid bug fix (#8768) * initial commit * adriver id submodule add * add id system tests, fix adriver bid adapter tests * adriver: fix buyerid * remarks fixing * removal of excess * delete custom parameter * bug fixes --- modules/adriverBidAdapter.js | 17 +++++------------ modules/adriverIdSystem.js | 3 ++- test/spec/modules/adriverBidAdapter_spec.js | 19 ++++++------------- test/spec/modules/adriverIdSystem_spec.js | 11 +++-------- 4 files changed, 16 insertions(+), 34 deletions(-) diff --git a/modules/adriverBidAdapter.js b/modules/adriverBidAdapter.js index e95f83d2c7b..b19c8318754 100644 --- a/modules/adriverBidAdapter.js +++ b/modules/adriverBidAdapter.js @@ -1,6 +1,6 @@ // ADRIVER BID ADAPTER for Prebid 1.13 import { logInfo, getWindowLocation, getBidIdParameter, _each } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'adriver'; @@ -22,8 +22,6 @@ export const spec = { }, buildRequests: function (validBidRequests, bidderRequest) { - logInfo('validBidRequests', validBidRequests); - let win = getWindowLocation(); let customID = Math.round(Math.random() * 999999999) + '-' + Math.round(new Date() / 1000) + '-1-46-'; let siteId = getBidIdParameter('siteid', validBidRequests[0].params) + ''; @@ -99,22 +97,17 @@ export const spec = { }); }); - let userid = validBidRequests[0].userId; - let adrcidCookie = storage.getDataFromLocalStorage('adrcid') || validBidRequests[0].userId.adrcid; - + let adrcidCookie = storage.getDataFromLocalStorage('adrcid') || validBidRequests[0].userId?.adrcid; if (adrcidCookie) { - payload.adrcid = adrcidCookie; - payload.id5 = userid.id5id; - payload.sharedid = userid.pubcid; - payload.unifiedid = userid.tdid; + payload.user.buyerid = adrcidCookie; } const payloadString = JSON.stringify(payload); return { method: 'POST', url: ADRIVER_BID_URL, - data: payloadString, - }; + data: payloadString + } }, interpretResponse: function (serverResponse, bidRequest) { diff --git a/modules/adriverIdSystem.js b/modules/adriverIdSystem.js index 6a492fac508..fb8ce99ec16 100644 --- a/modules/adriverIdSystem.js +++ b/modules/adriverIdSystem.js @@ -73,7 +73,8 @@ export const adriverIdSubmodule = { callback(); } }; - ajax(url, callbacks, undefined, {method: 'GET'}); + let newUrl = url + '&cid=' + (storage.getDataFromLocalStorage('adrcid') || storage.getCookie('adrcid')); + ajax(newUrl, callbacks, undefined, {method: 'GET'}); } }; return {callback: resp}; diff --git a/test/spec/modules/adriverBidAdapter_spec.js b/test/spec/modules/adriverBidAdapter_spec.js index 12c0a15fb06..33084877c14 100644 --- a/test/spec/modules/adriverBidAdapter_spec.js +++ b/test/spec/modules/adriverBidAdapter_spec.js @@ -297,25 +297,18 @@ describe('adriverAdapter', function () { { adrcid: undefined } ] cookieValues.forEach(cookieValue => describe('test cookie exist or not behavior', function () { - let expectedValues = { - adrcid: cookieValue.adrcid, - at: '', - cur: '', - tmax: '', - site: '', - id: '', - user: '', - device: '', - imp: '' - } + let expectedValues = [ + 'buyerid', + 'ext' + ] it('check adrcid if it exists', function () { bidRequests[0].userId.adrcid = cookieValue.adrcid; const payload = JSON.parse(spec.buildRequests(bidRequests).data); if (cookieValue.adrcid) { - expect(Object.keys(payload)).to.have.members(Object.keys(expectedValues)); + expect(Object.keys(payload.user)).to.have.members(expectedValues); } else { - expect(payload.adrcid).to.equal(undefined); + expect(payload.user.buyerid).to.equal(0); } }); })); diff --git a/test/spec/modules/adriverIdSystem_spec.js b/test/spec/modules/adriverIdSystem_spec.js index 29d965d5ed4..bc7d3f191d2 100644 --- a/test/spec/modules/adriverIdSystem_spec.js +++ b/test/spec/modules/adriverIdSystem_spec.js @@ -32,7 +32,6 @@ describe('AdriverIdSystem', function () { expect(request.url).to.include('https://ad.adriver.ru/cgi-bin/json.cgi'); request.respond(503, null, 'Unavailable'); expect(logErrorStub.calledOnce).to.be.true; - expect(callbackSpy.calledOnce).to.be.true; }); it('test call user sync url with the right params', function() { @@ -67,16 +66,12 @@ describe('AdriverIdSystem', function () { let request = server.requests[0]; request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ adrcid: response.adrcid })); - let expectedExpiration = new Date(); - expectedExpiration.setTime(expectedExpiration.getTime() + 86400 * 1825 * 1000); + let now = new Date(); + now.setTime(now.getTime() + 86400 * 1825 * 1000); const minimalDate = new Date(0).toString(); - function dateStringFor(date, maxDeltaMs = 2000) { - return sinon.match((val) => Math.abs(date.getTime() - new Date(val).getTime()) <= maxDeltaMs) - } - if (response.adrcid) { - expect(setCookieStub.calledWith('adrcid', response.adrcid, dateStringFor(expectedExpiration))).to.be.true; + expect(setCookieStub.calledWith('adrcid', response.adrcid, now.toUTCString())).to.be.true; expect(setLocalStorageStub.calledWith('adrcid', response.adrcid)).to.be.true; } else { expect(setCookieStub.calledWith('adrcid', '', minimalDate)).to.be.false; From 940339a3ebb9d425af261ab1535d32b3717c49ac Mon Sep 17 00:00:00 2001 From: Love Sharma Date: Thu, 18 Aug 2022 09:36:21 -0400 Subject: [PATCH 079/246] handle native response privacy link (#8838) Co-authored-by: Zicong Zhou --- src/native.js | 1 + test/spec/native_spec.js | 144 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) diff --git a/src/native.js b/src/native.js index 470ac0b9a32..0e48470d300 100644 --- a/src/native.js +++ b/src/native.js @@ -735,6 +735,7 @@ function toLegacyResponse(ortbResponse, ortbRequest) { const legacyResponse = {}; const requestAssets = ortbRequest?.assets || []; legacyResponse.clickUrl = ortbResponse.link.url; + legacyResponse.privacyLink = ortbResponse.privacy; for (const asset of ortbResponse?.assets || []) { const requestAsset = requestAssets.find(reqAsset => asset.id === reqAsset.id); if (asset.title) { diff --git a/test/spec/native_spec.js b/test/spec/native_spec.js index 19f417bd88f..08491edeb64 100644 --- a/test/spec/native_spec.js +++ b/test/spec/native_spec.js @@ -44,6 +44,106 @@ const bid = { }, }; +const ortbBid = { + adId: '123', + transactionId: 'au', + native: { + ortb: { + assets: [ + { + id: 0, + title: { + text: 'Native Creative' + } + }, + { + id: 1, + data: { + value: 'Cool description great stuff' + } + }, + { + id: 2, + data: { + value: 'Do it' + } + }, + { + id: 3, + img: { + url: 'http://cdn.example.com/p/creative-image/image.png', + h: 83, + w: 127 + } + }, + { + id: 4, + img: { + url: 'http://cdn.example.com/p/creative-image/icon.jpg', + h: 742, + w: 989 + } + }, + { + id: 5, + data: { + value: 'AppNexus', + type: 1 + } + } + ], + link: { + url: 'https://www.link.example' + }, + privacy: 'https://privacy-link.example', + ver: '1.2' + } + }, +}; + +const ortbRequest = { + assets: [ + { + id: 0, + required: 0, + title: { + len: 140 + } + }, { + id: 1, + required: 0, + data: { + type: 2 + } + }, { + id: 2, + required: 0, + data: { + type: 12 + } + }, { + id: 3, + required: 0, + img: { + type: 3 + } + }, { + id: 4, + required: 0, + img: { + type: 1 + } + }, { + id: 5, + required: 0, + data: { + type: 1 + } + } + ], + ver: '1.2' +} + const bidWithUndefinedFields = { transactionId: 'au', native: { @@ -401,6 +501,50 @@ describe('native.js', function () { }); }); + it('creates native all asset message with OpenRTB format', function () { + const messageRequest = { + message: 'Prebid Native', + action: 'allAssetRequest', + adId: '123', + }; + + const message = getAllAssetsMessage(messageRequest, ortbBid, {getNativeReq: () => ortbRequest}); + + expect(message.assets.length).to.equal(8); + expect(message.assets).to.deep.include({ + key: 'body', + value: bid.native.body, + }); + expect(message.assets).to.deep.include({ + key: 'image', + value: bid.native.image.url, + }); + expect(message.assets).to.deep.include({ + key: 'clickUrl', + value: bid.native.clickUrl, + }); + expect(message.assets).to.deep.include({ + key: 'title', + value: bid.native.title, + }); + expect(message.assets).to.deep.include({ + key: 'icon', + value: bid.native.icon.url, + }); + expect(message.assets).to.deep.include({ + key: 'cta', + value: bid.native.cta, + }); + expect(message.assets).to.deep.include({ + key: 'sponsoredBy', + value: bid.native.sponsoredBy, + }); + expect(message.assets).to.deep.include({ + key: 'privacyLink', + value: ortbBid.native.ortb.privacy, + }); + }); + const SAMPLE_ORTB_REQUEST = toOrtbNativeRequest({ title: 'vtitle', body: 'vbody' From 8ef12d1f21b3866f2b47c2a65c22ed49b767496d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9onard=20Labat?= Date: Thu, 18 Aug 2022 15:49:58 +0200 Subject: [PATCH 080/246] Criteo Bid Adapter - Add support for banner+native multiformat ad unit (#8842) Previously, the use of a native adunit was exclusive with the banner type. --- modules/criteoBidAdapter.js | 32 +++++++----- test/spec/modules/criteoBidAdapter_spec.js | 61 +++++++++++++--------- 2 files changed, 54 insertions(+), 39 deletions(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 1c269b2bba4..98aa58f5a70 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -469,15 +469,20 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { if (bidRequest.params.publisherSubId) { slot.publishersubid = bidRequest.params.publisherSubId; } - if (bidRequest.params.nativeCallback || deepAccess(bidRequest, `mediaTypes.${NATIVE}`)) { + + if (bidRequest.params.nativeCallback || hasNativeMediaType(bidRequest)) { slot.native = true; if (!checkNativeSendId(bidRequest)) { logWarn(LOG_PREFIX + 'all native assets containing URL should be sent as placeholders with sendId(icon, image, clickUrl, displayUrl, privacyLink, privacyIcon)'); } - slot.sizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes'), parseNativeSize); - } else { + } + + if (hasBannerMediaType(bidRequest)) { slot.sizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes'), parseSize); + } else { + slot.sizes = []; } + if (hasVideoMediaType(bidRequest)) { const video = { playersizes: parseSizes(deepAccess(bidRequest, 'mediaTypes.video.playerSize'), parseSize), @@ -554,17 +559,18 @@ function parseSize(size) { return size[0] + 'x' + size[1]; } -function parseNativeSize(size) { - if (size[0] === undefined && size[1] === undefined) { - return '2x2'; - } - return size[0] + 'x' + size[1]; -} - function hasVideoMediaType(bidRequest) { return deepAccess(bidRequest, 'mediaTypes.video') !== undefined; } +function hasBannerMediaType(bidRequest) { + return deepAccess(bidRequest, 'mediaTypes.banner') !== undefined; +} + +function hasNativeMediaType(bidRequest) { + return deepAccess(bidRequest, 'mediaTypes.native') !== undefined; +} + function hasValidVideoMediaType(bidRequest) { let isValid = true; @@ -646,18 +652,18 @@ function enrichSlotWithFloors(slot, bidRequest) { if (bidRequest.mediaTypes?.banner) { slotFloors.banner = {}; const bannerSizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes')) - bannerSizes.forEach(bannerSize => slotFloors.banner[parseSize(bannerSize).toString()] = bidRequest.getFloor({size: bannerSize, mediaType: BANNER})); + bannerSizes.forEach(bannerSize => slotFloors.banner[parseSize(bannerSize).toString()] = bidRequest.getFloor({ size: bannerSize, mediaType: BANNER })); } if (bidRequest.mediaTypes?.video) { slotFloors.video = {}; const videoSizes = parseSizes(deepAccess(bidRequest, 'mediaTypes.video.playerSize')) - videoSizes.forEach(videoSize => slotFloors.video[parseSize(videoSize).toString()] = bidRequest.getFloor({size: videoSize, mediaType: VIDEO})); + videoSizes.forEach(videoSize => slotFloors.video[parseSize(videoSize).toString()] = bidRequest.getFloor({ size: videoSize, mediaType: VIDEO })); } if (bidRequest.mediaTypes?.native) { slotFloors.native = {}; - slotFloors.native['*'] = bidRequest.getFloor({size: '*', mediaType: NATIVE}); + slotFloors.native['*'] = bidRequest.getFloor({ size: '*', mediaType: NATIVE }); } if (Object.keys(slotFloors).length > 0) { diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 7252232676f..7af5f5d77a2 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -13,7 +13,7 @@ import * as utils from 'src/utils.js'; import * as refererDetection from 'src/refererDetection.js'; import { config } from '../../../src/config.js'; import * as storageManager from 'src/storageManager.js'; -import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes.js'; +import { BANNER, NATIVE, VIDEO } from '../../../src/mediaTypes.js'; describe('The Criteo bidding adapter', function () { let utilsMock, sandbox; @@ -707,7 +707,7 @@ describe('The Criteo bidding adapter', function () { expect(ortbRequest.slots[0].sizes[0]).to.equal('undefinedxundefined'); }); - it('should properly detect and get sizes of native sizeless banner', function () { + it('should properly detect and forward native flag', function () { const bidRequests = [ { mediaTypes: { @@ -722,11 +722,10 @@ describe('The Criteo bidding adapter', function () { ]; const request = spec.buildRequests(bidRequests, bidderRequest); const ortbRequest = request.data; - expect(ortbRequest.slots[0].sizes).to.have.lengthOf(1); - expect(ortbRequest.slots[0].sizes[0]).to.equal('2x2'); + expect(ortbRequest.slots[0].native).to.equal(true); }); - it('should properly detect and get size of native sizeless banner', function () { + it('should properly detect and forward native flag', function () { const bidRequests = [ { mediaTypes: { @@ -741,8 +740,7 @@ describe('The Criteo bidding adapter', function () { ]; const request = spec.buildRequests(bidRequests, bidderRequest); const ortbRequest = request.data; - expect(ortbRequest.slots[0].sizes).to.have.lengthOf(1); - expect(ortbRequest.slots[0].sizes[0]).to.equal('2x2'); + expect(ortbRequest.slots[0].native).to.equal(true); }); it('should properly build a networkId request', function () { @@ -1258,11 +1256,13 @@ describe('The Criteo bidding adapter', function () { if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { return { currency: 'USD', - floor: 1.0}; + floor: 1.0 + }; } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { return { currency: 'USD', - floor: 2.0}; + floor: 2.0 + }; } else { return {} } @@ -1273,9 +1273,10 @@ describe('The Criteo bidding adapter', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.slots[0].ext.floors).to.deep.equal({ 'banner': { - '300x250': {'currency': 'USD', 'floor': 1}, - '728x90': {'currency': 'USD', 'floor': 2} - }}); + '300x250': { 'currency': 'USD', 'floor': 1 }, + '728x90': { 'currency': 'USD', 'floor': 2 } + } + }); }); it('should properly build a video request with several player sizes with floors', function () { @@ -1297,11 +1298,13 @@ describe('The Criteo bidding adapter', function () { if (inputParams.mediaType === VIDEO && inputParams.size[0] === 300 && inputParams.size[1] === 250) { return { currency: 'USD', - floor: 1.0}; + floor: 1.0 + }; } else if (inputParams.mediaType === VIDEO && inputParams.size[0] === 728 && inputParams.size[1] === 90) { return { currency: 'USD', - floor: 2.0}; + floor: 2.0 + }; } else { return {} } @@ -1312,9 +1315,10 @@ describe('The Criteo bidding adapter', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.slots[0].ext.floors).to.deep.equal({ 'video': { - '300x250': {'currency': 'USD', 'floor': 1}, - '728x90': {'currency': 'USD', 'floor': 2} - }}); + '300x250': { 'currency': 'USD', 'floor': 1 }, + '728x90': { 'currency': 'USD', 'floor': 2 } + } + }); }); it('should properly build a multi format request with floors', function () { @@ -1347,19 +1351,23 @@ describe('The Criteo bidding adapter', function () { if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { return { currency: 'USD', - floor: 1.0}; + floor: 1.0 + }; } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { return { currency: 'USD', - floor: 2.0}; + floor: 2.0 + }; } else if (inputParams.mediaType === VIDEO && inputParams.size[0] === 640 && inputParams.size[1] === 480) { return { currency: 'EUR', - floor: 3.2}; + floor: 3.2 + }; } else if (inputParams.mediaType === NATIVE && inputParams.size === '*') { return { currency: 'YEN', - floor: 4.99}; + floor: 4.99 + }; } else { return {} } @@ -1371,15 +1379,16 @@ describe('The Criteo bidding adapter', function () { expect(request.data.slots[0].ext.data.someContextAttribute).to.deep.equal('abc'); expect(request.data.slots[0].ext.floors).to.deep.equal({ 'banner': { - '300x250': {'currency': 'USD', 'floor': 1}, - '728x90': {'currency': 'USD', 'floor': 2} + '300x250': { 'currency': 'USD', 'floor': 1 }, + '728x90': { 'currency': 'USD', 'floor': 2 } }, 'video': { - '640x480': {'currency': 'EUR', 'floor': 3.2} + '640x480': { 'currency': 'EUR', 'floor': 3.2 } }, 'native': { - '*': {'currency': 'YEN', 'floor': 4.99} - }}); + '*': { 'currency': 'YEN', 'floor': 4.99 } + } + }); }); }); From 442931dd2f257826db611725120e0ee748423d74 Mon Sep 17 00:00:00 2001 From: wsusrasp <106743463+wsusrasp@users.noreply.github.com> Date: Thu, 18 Aug 2022 16:51:46 +0200 Subject: [PATCH 081/246] Ras Bid Adapter: support for SlotSequence parameter (#8792) * add rasbidadapter pos param * Read pos off the adunit * rename conflicting pos parameter for clarity --- modules/rasBidAdapter.js | 8 ++++++++ modules/rasBidAdapter.md | 27 +++++++++++++------------ test/spec/modules/rasBidAdapter_spec.js | 2 ++ 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/modules/rasBidAdapter.js b/modules/rasBidAdapter.js index 909b6a7b795..7bc3cf66b0d 100644 --- a/modules/rasBidAdapter.js +++ b/modules/rasBidAdapter.js @@ -1,3 +1,4 @@ +import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import { isEmpty, getAdUnitSizes, parseSizesInput, deepAccess } from '../src/utils.js'; @@ -92,11 +93,18 @@ const getSlots = (bidRequests) => { const batchSize = bidRequests.length; for (let i = 0; i < batchSize; i++) { const adunit = bidRequests[i]; + const slotSequence = utils.deepAccess(adunit, 'params.slotSequence'); + const sizes = parseSizesInput(getAdUnitSizes(adunit)).join(','); + queryString += `&slot${i}=${encodeURIComponent(adunit.params.slot)}&id${i}=${encodeURIComponent(adunit.bidId)}&composition${i}=CHILD`; + if (sizes.length) { queryString += `&iusizes${i}=${encodeURIComponent(sizes)}`; } + if (slotSequence !== undefined) { + queryString += `&pos${i}=${encodeURIComponent(slotSequence)}`; + } } return queryString; }; diff --git a/modules/rasBidAdapter.md b/modules/rasBidAdapter.md index 5cf75c3446d..384ba0b611f 100644 --- a/modules/rasBidAdapter.md +++ b/modules/rasBidAdapter.md @@ -34,17 +34,18 @@ var adUnits = [{ # Parameters -| Name | Scope | Type | Description | Example -| --- | --- | --- | --- | --- -| network | required | String | Specific identifier provided by RAS | `"4178463"` -| site | required | String | Specific identifier name (case-insensitive) that is associated with this ad unit and provided by RAS | `"example_com"` -| area | required | String | Ad unit category name; only case-insensitive alphanumeric with underscores and hyphens are allowed | `"sport"` -| slot | required | String | Ad unit placement name (case-insensitive) provided by RAS | `"slot"` -| pageContext | optional | Object | Web page context data | `{}` -| pageContext.dr | optional | String | Document referrer URL address | `"https://example.com/"` -| pageContext.du | optional | String | Document URL address | `"https://example.com/sport/football/article.html?id=932016a5-02fc-4d5c-b643-fafc2f270f06"` +| Name | Scope | Type | Description | Example +| --- | --- | --- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| --- +| network | required | String | Specific identifier provided by RAS | `"4178463"` +| site | required | String | Specific identifier name (case-insensitive) that is associated with this ad unit and provided by RAS | `"example_com"` +| area | required | String | Ad unit category name; only case-insensitive alphanumeric with underscores and hyphens are allowed | `"sport"` +| slot | required | String | Ad unit placement name (case-insensitive) provided by RAS | `"slot"` +| slotSequence | optional | Number | Ad unit sequence position provided by RAS | `1` +| pageContext | optional | Object | Web page context data | `{}` +| pageContext.dr | optional | String | Document referrer URL address | `"https://example.com/"` +| pageContext.du | optional | String | Document URL address | `"https://example.com/sport/football/article.html?id=932016a5-02fc-4d5c-b643-fafc2f270f06"` | pageContext.dv | optional | String | Document virtual address as slash-separated path that may consist of any number of parts (case-insensitive alphanumeric with underscores and hyphens); first part should be the same as `site` value and second as `area` value; next parts may reflect website navigation | `"example_com/sport/football"` -| pageContext.keyWords | optional | String[] | List of keywords associated with this ad unit; only case-insensitive alphanumeric with underscores and hyphens are allowed | `["euro", "lewandowski"]` -| pageContext.keyValues | optional | Object | Key-values associated with this ad unit (case-insensitive); following characters are not allowed in the values: `" ' = ! + # * ~ ; ^ ( ) < > [ ] & @` | `{}` -| pageContext.keyValues.ci | optional | String | Content unique identifier | `"932016a5-02fc-4d5c-b643-fafc2f270f06"` -| pageContext.keyValues.adunit | optional | String | Ad unit name | `"example_com/sport"` +| pageContext.keyWords | optional | String[] | List of keywords associated with this ad unit; only case-insensitive alphanumeric with underscores and hyphens are allowed | `["euro", "lewandowski"]` +| pageContext.keyValues | optional | Object | Key-values associated with this ad unit (case-insensitive); following characters are not allowed in the values: `" ' = ! + # * ~ ; ^ ( ) < > [ ] & @` | `{}` +| pageContext.keyValues.ci | optional | String | Content unique identifier | `"932016a5-02fc-4d5c-b643-fafc2f270f06"` +| pageContext.keyValues.adunit | optional | String | Ad unit name | `"example_com/sport"` diff --git a/test/spec/modules/rasBidAdapter_spec.js b/test/spec/modules/rasBidAdapter_spec.js index 324bf782672..8c378aaa416 100644 --- a/test/spec/modules/rasBidAdapter_spec.js +++ b/test/spec/modules/rasBidAdapter_spec.js @@ -58,6 +58,7 @@ describe('rasBidAdapter', function () { slot: 'test', area: 'areatest', site: 'test', + slotSequence: '0', network: '4178463' } }; @@ -140,6 +141,7 @@ describe('rasBidAdapter', function () { expect(requests[0].url).to.have.string('DV=test%2Fareatest'); expect(requests[0].url).to.have.string('kwrd=val1%2Bval2'); expect(requests[0].url).to.have.string('kvadunit=test%2Fareatest'); + expect(requests[0].url).to.have.string('pos0=0'); }); }); From d77309aad8fc00faaddf42da163ace477c41e91e Mon Sep 17 00:00:00 2001 From: Catalin Ciocov Date: Thu, 18 Aug 2022 17:54:53 +0300 Subject: [PATCH 082/246] Improve Digital adapter: refactor code to align with latest RAZR creative tags (#8827) --- modules/improvedigitalBidAdapter.js | 78 ++++++++++++------- src/adloader.js | 3 +- .../modules/improvedigitalBidAdapter_spec.js | 22 +++--- 3 files changed, 61 insertions(+), 42 deletions(-) diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js index 4996f0efaf0..c8fc8eb7a2a 100644 --- a/modules/improvedigitalBidAdapter.js +++ b/modules/improvedigitalBidAdapter.js @@ -19,6 +19,7 @@ import {Renderer} from '../src/Renderer.js'; import {createEidsArray} from './userId/eids.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import {loadExternalScript} from '../src/adloader.js'; const BIDDER_CODE = 'improvedigital'; const CREATIVE_TTL = 300; @@ -212,7 +213,7 @@ export const spec = { ID_RESPONSE.buildAd(bid, bidRequest, bidObject); - ID_RAZR.addBidData({ + ID_RAZR.forwardBid({ bidRequest, bid }); @@ -640,37 +641,58 @@ const ID_OUTSTREAM = { }; const ID_RAZR = { - RENDERER_URL: 'https://razr.improvedigital.com/renderer.js', - addBidData({bid, bidRequest}) { - if (this.isValidBid(bid)) { - bid.renderer = Renderer.install({ - url: this.RENDERER_URL, - config: {bidRequest} - }); - bid.renderer.setRender(this.render); + RENDERER_URL: 'https://cdn.360yield.com/razr/tag.js', + + forwardBid({bidRequest, bid}) { + if (bid.mediaType !== BANNER) { + return; } - }, - isValidBid(bid) { - return bid && /razr:\/\//.test(bid.ad); + const cfg = { + prebid: { + bidRequest, + bid + } + }; + + const cfgStr = JSON.stringify(cfg).replace(/<\/script>/g, '\\x3C/script>'); + const s = ``; + bid.ad = bid.ad.replace(/]*>/, match => match + s); + + this.installListener(); }, - render(bid) { - const {bidRequest} = bid.renderer.getConfig(); - - const payload = { - type: 'prebid', - bidRequest, - bid, - config: mergeDeep( - {}, - config.getConfig('improvedigital.rendererConfig'), - deepAccess(bidRequest, 'params.rendererConfig') - ) - }; + installListener() { + if (this._listenerInstalled) { + return; + } + + window.addEventListener('message', function(e) { + const data = e.data?.razr?.load; + if (!data) { + return; + } + + if (e.source) { + data.source = e.source; + if (data.id) { + e.source.postMessage({ + razr: { + id: data.id + } + }, '*'); + } + } + + const ns = window.razr = window.razr || {}; + ns.q = ns.q || []; + ns.q.push(data); + + if (!ns.loaded) { + loadExternalScript(ID_RAZR.RENDERER_URL, BIDDER_CODE); + } + }); - const razr = window.razr = window.razr || {}; - razr.queue = razr.queue || []; - razr.queue.push(payload); + this._listenerInstalled = true; } }; diff --git a/src/adloader.js b/src/adloader.js index 1e7995a9dc6..6b7427d3e52 100644 --- a/src/adloader.js +++ b/src/adloader.js @@ -18,7 +18,8 @@ const _approvedLoadExternalJSList = [ 'ftrackId', 'inskin', 'hadron', - 'medianet' + 'medianet', + 'improvedigital' ] /** diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js index a882f5ca5ef..19e91cbde96 100644 --- a/test/spec/modules/improvedigitalBidAdapter_spec.js +++ b/test/spec/modules/improvedigitalBidAdapter_spec.js @@ -1031,7 +1031,7 @@ describe('Improve Digital Adapter Tests', function () { width: 728, height: 90, ttl: 300, - ad: '  ', + ad: '  ', creativeId: '510265', dealId: 320896, netRevenue: false, @@ -1042,6 +1042,12 @@ describe('Improve Digital Adapter Tests', function () { } ]; + const multiFormatExpectedBid = [ + Object.assign({}, expectedBid[0], { + ad: '  ' + }) + ]; + const expectedTwoBids = [ expectedBid[0], { @@ -1051,7 +1057,7 @@ describe('Improve Digital Adapter Tests', function () { width: 300, height: 250, ttl: 300, - ad: '  ', + ad: '  ', creativeId: '479163', dealId: 320896, netRevenue: false, @@ -1091,7 +1097,7 @@ describe('Improve Digital Adapter Tests', function () { it('should return a well-formed display bid for multi-format ad unit', function () { const bids = spec.interpretResponse(serverResponse, {bidderRequest: multiFormatBidderRequest}); - expect(bids).to.deep.equal(expectedBid); + expect(bids).to.deep.equal(multiFormatExpectedBid); }); it('should return two bids', function () { @@ -1233,16 +1239,6 @@ describe('Improve Digital Adapter Tests', function () { bids = spec.interpretResponse(videoResponse, {bidderRequest: multiFormatBidderRequest}); expect(bids[0].mediaType).to.equal(VIDEO); }); - - it('should not affect non-RAZR bids', function () { - const bids = spec.interpretResponse(serverResponse, {bidderRequest}); - expect(bids[0].renderer).to.not.exist; - }); - - it('should detect RAZR bids', function () { - const bids = spec.interpretResponse(serverResponseRazr, {bidderRequest}); - expect(bids[0].renderer).to.exist; - }); }); describe('getUserSyncs', function () { From 4e9ccd5b207f7195bdd420b7ed0af6a29b16fb0d Mon Sep 17 00:00:00 2001 From: Love Sharma Date: Thu, 18 Aug 2022 10:57:33 -0400 Subject: [PATCH 083/246] IX Bid Adapter: Native OpenRTB Request Support (#8853) * fix native click trackers to only fire on click * fix unit tests for ix * remove version for native requests * remove unnecessary request conversion Co-authored-by: Zicong Zhou --- modules/ixBidAdapter.js | 319 +------------------------ test/spec/modules/ixBidAdapter_spec.js | 314 ++++++++++++++++-------- 2 files changed, 223 insertions(+), 410 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index ce4fdcc2431..61e518d306d 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -25,7 +25,6 @@ import {find} from '../src/polyfill.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {INSTREAM, OUTSTREAM} from '../src/video.js'; import {Renderer} from '../src/Renderer.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; const BIDDER_CODE = 'ix'; const ALIAS_BIDDER_CODE = 'roundel'; @@ -100,93 +99,6 @@ const VIDEO_PARAMS_ALLOW_LIST = [ 'delivery', 'pos', 'companionad', 'api', 'companiontype', 'ext', 'playerSize', 'w', 'h' ]; -const NATIVE_ASSET_TYPES = { - TITLE: 100, - IMG: 200, - VIDEO: 300, - DATA: 400 -}; -const NATIVE_IMAGE_TYPES = { - ICON: 1, - MAIN: 3 -}; -const NATIVE_DATA_TYPES = { - SPONSORED: 1, - DESC: 2, - RATING: 3, - LIKES: 4, - DOWNLOADS: 5, - PRICE: 6, - SALEPRICE: 7, - PHONE: 8, - ADDRESS: 9, - DESC2: 10, - DISPLAYURL: 11, - CTATEXT: 12 -}; -const NATIVE_DATA_MAP = { - [NATIVE_DATA_TYPES.SPONSORED]: 'sponsoredBy', - [NATIVE_DATA_TYPES.DESC]: 'body', - [NATIVE_DATA_TYPES.RATING]: 'rating', - [NATIVE_DATA_TYPES.LIKES]: 'likes', - [NATIVE_DATA_TYPES.DOWNLOADS]: 'downloads', - [NATIVE_DATA_TYPES.PRICE]: 'price', - [NATIVE_DATA_TYPES.SALEPRICE]: 'salePrice', - [NATIVE_DATA_TYPES.PHONE]: 'phone', - [NATIVE_DATA_TYPES.ADDRESS]: 'address', - [NATIVE_DATA_TYPES.DESC2]: 'body2', - [NATIVE_DATA_TYPES.DISPLAYURL]: 'displayUrl', - [NATIVE_DATA_TYPES.CTATEXT]: 'cta' -}; -const NATIVE_ASSETS_MAP = { - 'title': { assetType: NATIVE_ASSET_TYPES.TITLE }, - 'icon': { assetType: NATIVE_ASSET_TYPES.IMG, subtype: NATIVE_IMAGE_TYPES.ICON }, - 'image': { assetType: NATIVE_ASSET_TYPES.IMG, subtype: NATIVE_IMAGE_TYPES.MAIN }, - 'sponsoredBy': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.SPONSORED }, - 'body': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.DESC }, - 'rating': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.RATING }, - 'likes': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.LIKES }, - 'downloads': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.DOWNLOADS }, - 'price': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.PRICE }, - 'salePrice': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.SALEPRICE }, - 'phone': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.PHONE }, - 'address': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.ADDRESS }, - 'body2': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.DESC2 }, - 'displayUrl': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.DISPLAYURL }, - 'cta': { assetType: NATIVE_ASSET_TYPES.DATA, subtype: NATIVE_DATA_TYPES.CTATEXT }, - 'video': { assetType: NATIVE_ASSET_TYPES.VIDEO } -}; -const NATIVE_ALLOWED_PROPERTIES = [ - 'rendererUrl', - 'sendTargetingKeys', - 'adTemplate', - 'type', - 'ext', - 'privacyLink', - 'clickUrl', - 'privacyIcon' -]; -const NATIVE_ASSET_DEFAULT = { - TITLE: { - LEN: 25 - }, - VIDEO: { - MIMES: [ - 'video/mp4', - 'video/webm' - ], - MINDURATION: 0, - MAXDURATION: 120, - PROTOCOLS: [2, 3, 5, 6], - } -}; -const NATIVE_EVENT_TYPES = { - IMRESSION: 1 -}; -const NATIVE_EVENT_TRACKING_METHOD = { - IMG: 1, - JS: 2 -}; const LOCAL_STORAGE_KEY = 'ixdiag'; let hasRegisteredHandler = false; export const storage = getStorageManager({gvlid: GLOBAL_VENDOR_ID, bidderCode: BIDDER_CODE}); @@ -307,50 +219,13 @@ function bidToVideoImp(bid) { */ function bidToNativeImp(bid) { const imp = bidToImp(bid); - const nativeAdUnitRef = deepAccess(bid, 'mediaTypes.native'); - - const assets = []; - - // Convert all native assets to imp object - for (const [adUnitProperty, adUnitValues] of Object.entries(nativeAdUnitRef)) { - if (!NATIVE_ASSETS_MAP[adUnitProperty]) { - continue; - } - - const { assetType, subtype } = NATIVE_ASSETS_MAP[adUnitProperty]; - let asset; - switch (assetType) { - case NATIVE_ASSET_TYPES.TITLE: - asset = createNativeTitleRequest(adUnitValues); - break; - case NATIVE_ASSET_TYPES.IMG: - asset = createNativeImgRequest(adUnitValues, subtype); - break; - case NATIVE_ASSET_TYPES.VIDEO: - asset = createNativeVideoRequest(adUnitValues); - break; - case NATIVE_ASSET_TYPES.DATA: - asset = createNativeDataRequest(adUnitValues, subtype); - break; - } - asset.id = assetType + (subtype || 0); - assets.push(asset); - } - - if (assets.length === 0) { - logWarn('IX Bid Adapter: Native bid does not contain recognised assets in [mediaTypes.native]'); - return {}; - } - const request = { - assets: assets, - ver: '1.2', - eventtrackers: [{ - event: 1, - methods: [1, 2] - }], - privacy: 1 - }; + const request = bid.nativeOrtbRequest + request.eventtrackers = [{ + event: 1, + methods: [1, 2] + }]; + request.privacy = 1; imp.native = { request: JSON.stringify(request), @@ -365,80 +240,6 @@ function bidToNativeImp(bid) { return imp; } -/** - * Converts native bid asset to a native impression asset - * @param {object} bidAsset PBJS bid asset object - * @returns {object} IX impression asset object - */ -function createNativeTitleRequest(bidAsset) { - return { - required: bidAsset.required ? 1 : 0, - title: { - len: bidAsset.len ? bidAsset.len : NATIVE_ASSET_DEFAULT.TITLE.LEN, - ext: bidAsset.ext - } - } -} - -/** - * Converts native bid asset to a native impression asset - * @param {object} bidAsset PBJS bid asset object - * @param {int} type The image type - * @returns {object} IX impression asset object - */ -function createNativeImgRequest(bidAsset, type) { - let asset = { - required: bidAsset.required ? 1 : 0, - img: { - type: type, - mimes: bidAsset.mimes, - ext: bidAsset.ext - } - } - - if (bidAsset.hasOwnProperty('sizes') && bidAsset.sizes.length === 2) { - asset.img.wmin = bidAsset.sizes[0]; - asset.img.hmin = bidAsset.sizes[1]; - } - - return asset -} - -/** - * Converts native bid asset to a native impression asset - * @param {object} bidAsset PBJS bid asset object - * @returns {object} IX impression asset object - */ -function createNativeVideoRequest(bidAsset) { - return { - required: bidAsset.required ? 1 : 0, - video: { - mimes: bidAsset.mimes ? bidAsset.mimes : NATIVE_ASSET_DEFAULT.VIDEO.MIMES, - minduration: bidAsset.minduration ? bidAsset.minduration : NATIVE_ASSET_DEFAULT.VIDEO.MINDURATION, - maxduration: bidAsset.maxduration ? bidAsset.maxduration : NATIVE_ASSET_DEFAULT.VIDEO.MAXDURATION, - protocols: bidAsset.protocols ? bidAsset.protocols : NATIVE_ASSET_DEFAULT.VIDEO.PROTOCOLS, - ext: bidAsset.ext - } - } -} - -/** - * Converts native bid asset to a native impression asset - * @param {object} bidAsset PBJS bid asset object - * @param {int} type The image type - * @returns {object} IX impression asset object - */ -function createNativeDataRequest(bidAsset, type) { - return { - required: bidAsset.required ? 1 : 0, - data: { - type: type, - len: bidAsset.len, - ext: bidAsset.ext - } - } -} - /** * Converts an incoming PBJS bid to an IX Impression * @param {object} bid PBJS bid object @@ -559,7 +360,7 @@ function parseBid(rawBid, currency, bidRequest) { bid.mediaTypes = bidRequest.mediaTypes; bid.ttl = isValidExpiry ? rawBid.exp : VIDEO_TIME_TO_LIVE; } else if (parsedAdm && parsedAdm.native) { - bid.native = interpretNativeAdm(parsedAdm.native); + bid.native = {ortb: parsedAdm.native}; bid.width = rawBid.w ? rawBid.w : 1; bid.height = rawBid.h ? rawBid.h : 1; bid.mediaType = NATIVE; @@ -583,84 +384,6 @@ function parseBid(rawBid, currency, bidRequest) { return bid; } -/** - * Parse native adm and set native asset key names recognized by Prebid.js - * @param {string} adm Native adm complience - */ -function interpretNativeAdm(nativeResponse) { - const native = { - clickUrl: nativeResponse.link.url, - privacyLink: nativeResponse.privacy - }; - - for (const asset of nativeResponse.assets) { - const subtype = asset.id % 100; - const assetType = asset.id - subtype; - - switch (assetType) { - case NATIVE_ASSET_TYPES.TITLE: - native.title = asset.title && asset.title.text; - break; - case NATIVE_ASSET_TYPES.IMG: - const image = { - url: asset.img && asset.img.url, - height: asset.img && asset.img.h, - width: asset.img && asset.img.w - }; - native[subtype === NATIVE_IMAGE_TYPES.ICON ? 'icon' : 'image'] = image; - break; - case NATIVE_ASSET_TYPES.VIDEO: - native.video = asset.video && asset.video.vasttag; - break; - case NATIVE_ASSET_TYPES.DATA: - setDataAsset(native, asset, subtype); - break; - default: - logWarn(`IX Bid Adapter: native asset ID ${asset.id} could not be recognized`); - } - } - - setTrackers(native, nativeResponse); - return native; -} - -function setDataAsset(native, asset, type) { - if (!(type in NATIVE_DATA_MAP)) { - logWarn(`IX Bid Adapter: native data asset type ${type} is not supported`); - return; - } - native[NATIVE_DATA_MAP[type]] = asset.data && asset.data.value; -} - -function setTrackers(native, nativeResponse) { - native.impressionTrackers = [] - - if (Array.isArray(nativeResponse.imptrackers)) { - native.impressionTrackers.push(...nativeResponse.imptrackers) - } - - if (Array.isArray(nativeResponse.link.clicktrackers)) { - native.impressionTrackers.push(...nativeResponse.link.clicktrackers) - } - - if (Array.isArray(nativeResponse.eventtrackers)) { - nativeResponse.eventtrackers.forEach(tracker => { - if (tracker.event !== NATIVE_EVENT_TYPES.IMRESSION) { - return - } - - switch (tracker.method) { - case NATIVE_EVENT_TRACKING_METHOD.IMG: - native.impressionTrackers.push(tracker.url); - break; - case NATIVE_EVENT_TRACKING_METHOD.JS: - native.javascriptTrackers = ``; - break; - } - }) - } -} - /** * Determines whether or not the given object is valid size format. * @@ -758,25 +481,13 @@ function isValidBidFloorParams(bidFloor, bidFloorCur) { bidFloorCur.match(curRegex)); } -function nativeMediaTypeValid(nativeObj) { - if (nativeObj === undefined) { - return true; - } - - let hasValidAsset = false; - - for (const property in nativeObj) { - if (!(property in NATIVE_ASSETS_MAP) && !NATIVE_ALLOWED_PROPERTIES.includes(property)) { - logError('IX Bid Adapter: native', { bidder: BIDDER_CODE, code: ERROR_CODES.PROPERTY_NOT_INCLUDED }); - return false; - } - - if (property in NATIVE_ASSETS_MAP) { - hasValidAsset = true; - } +function nativeMediaTypeValid(bid) { + const nativeMediaTypes = deepAccess(bid, 'mediaTypes.native'); + if (nativeMediaTypes === undefined) { + return true } - return hasValidAsset; + return bid.nativeOrtbRequest && Array.isArray(bid.nativeOrtbRequest.assets) && bid.nativeOrtbRequest.assets.length > 0 } /** @@ -837,8 +548,6 @@ function getEidInfo(allEids) { * */ function buildRequest(validBidRequests, bidderRequest, impressions, version) { - // convert Native ORTB definition to old-style prebid native definition - validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); // Always use secure HTTPS protocol. let baseUrl = SECURE_BID_URL; // Get ids from Prebid User ID Modules @@ -966,7 +675,6 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { // Use the siteId in the first bid request as the main siteId. siteID = validBidRequests[0].params.siteId; payload.s = siteID; - payload.v = version; if (version) { payload.v = version; } @@ -1603,7 +1311,6 @@ export const spec = { const paramsSize = deepAccess(bid, 'params.size'); const mediaTypeBannerSizes = deepAccess(bid, 'mediaTypes.banner.sizes'); const mediaTypeVideoRef = deepAccess(bid, 'mediaTypes.video'); - const mediaTypeNativeRef = deepAccess(bid, 'mediaTypes.native'); const mediaTypeVideoPlayerSize = deepAccess(bid, 'mediaTypes.video.playerSize'); const hasBidFloor = bid.params.hasOwnProperty('bidFloor'); const hasBidFloorCur = bid.params.hasOwnProperty('bidFloorCur'); @@ -1670,7 +1377,7 @@ export const spec = { } } - return nativeMediaTypeValid(mediaTypeNativeRef); + return nativeMediaTypeValid(bid); }, /** diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 243b702f03d..7360024eed2 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -10,7 +10,6 @@ describe('IndexexchangeAdapter', function () { const IX_SECURE_ENDPOINT = 'https://htlb.casalemedia.com/openrtb/pbjs'; const VIDEO_ENDPOINT_VERSION = 8.1; const BANNER_ENDPOINT_VERSION = 7.2; - const NATIVE_ENDPOINT_VERSION = undefined; const SAMPLE_SCHAIN = { 'ver': '1.0', @@ -378,6 +377,7 @@ describe('IndexexchangeAdapter', function () { required: false }, title: { + len: 25, required: true }, body: { @@ -387,13 +387,20 @@ describe('IndexexchangeAdapter', function () { required: true }, video: { - required: false + required: false, + mimes: ['video/mp4', 'video/webm'], + minduration: 0, + maxduration: 120, + protocols: [2, 3, 5, 6] }, sponsoredBy: { required: true } } }, + nativeOrtbRequest: { + assets: [{id: 0, required: 0, img: {type: 1}}, {id: 1, required: 1, title: {len: 140}}, {id: 2, required: 1, data: {type: 2}}, {id: 3, required: 1, img: {type: 3}}, {id: 4, required: false, video: {mimes: ['video/mp4', 'video/webm'], minduration: 0, maxduration: 120, protocols: [2, 3, 5, 6]}}] + }, adUnitCode: 'div-gpt-ad-1460505748563-0', transactionId: '173f49a8-7549-4218-a23c-e7ba59b47231', bidId: '1a2b3c4f', @@ -432,6 +439,9 @@ describe('IndexexchangeAdapter', function () { } } }, + nativeOrtbRequest: { + assets: [{id: 0, required: 0, img: {type: 1}}, {id: 1, required: 1, title: {len: 140}}, {id: 2, required: 1, data: {type: 2}}, {id: 3, required: 1, img: {type: 3}}, {id: 4, required: false, video: {mimes: ['video/mp4', 'video/webm'], minduration: 0, maxduration: 120, protocols: [2, 3, 5, 6]}}] + }, adUnitCode: 'div-gpt-ad-1460505748562-0', transactionId: '173f49a8-7549-4218-a23c-e7ba59b47230', bidId: '1a2b3c4e', @@ -442,7 +452,7 @@ describe('IndexexchangeAdapter', function () { ]; const DEFAULT_NATIVE_IMP = { - request: '{"assets":[{"required":0,"img":{"type":1},"id":201},{"required":1,"title":{"len":25},"id":100},{"required":1,"data":{"type":2},"id":402},{"required":1,"img":{"type":3},"id":203},{"required":0,"video":{"mimes":["video/mp4","video/webm"],"minduration":0,"maxduration":120,"protocols":[2,3,5,6]},"id":300},{"required":1,"data":{"type":1},"id":401}],"ver":"1.2","eventtrackers":[{"event":1,"methods":[1,2]}],"privacy":1}', + request: '{"assets":[{"id":0,"required":0,"img":{"type":1}},{"id":1,"required":1,"title":{"len":140}},{"id":2,"required":1,"data":{"type":2}},{"id":3,"required":1,"img":{"type":3}},{"id":4,"required":false,"video":{"mimes":["video/mp4","video/webm"],"minduration":0,"maxduration":120,"protocols":[2,3,5,6]}}],"eventtrackers":[{"event":1,"methods":[1,2]}],"privacy":1}', ver: '1.2' } @@ -588,7 +598,7 @@ describe('IndexexchangeAdapter', function () { advbrandid: 303325, advbrand: 'OECTA' }, - adm: '{"native":{"ver":"1.2","assets":[{"id":201,"img":{"url":"https://cdn.liftoff.io/customers/1209/creatives/2501-icon-250x250.png","w":250,"h":250}},{"id":203,"img":{"url":"https://cdn.liftoff.io/customers/5a9cab9cc6/image/lambda_png/a0355879b06c09b09232.png","w":1200,"h":627}},{"id":401,"data":{"value":"autodoc.co.uk"}},{"id":402,"data":{"value":"Les pièces automobiles dont vous avez besoin, toujours sous la main."}},{"id":100,"title":{"text":"Autodoc"}},{"id":300,"video":{"vasttag":"blah"}}],"link":{"url":"https://play.google.com/store/apps/details?id=de.autodoc.gmbh","clicktrackers":["https://click.liftoff.io/v1/campaign_click/blah"]},"eventtrackers":[{"event":1,"method":1,"url":"https://impression-europe.liftoff.io/index/impression"},{"event":1,"method":1,"url":"https://a701.casalemedia.com/impression/v1"}],"privacy":"https://privacy.link.com"}}' + adm: '{"native":{"ver":"1.2","assets":[{"id":0,"img":{"url":"https://cdn.liftoff.io/customers/1209/creatives/2501-icon-250x250.png","w":250,"h":250}},{"id":1,"img":{"url":"https://cdn.liftoff.io/customers/5a9cab9cc6/image/lambda_png/a0355879b06c09b09232.png","w":1200,"h":627}},{"id":2,"data":{"value":"autodoc.co.uk"}},{"id":3,"data":{"value":"Les pièces automobiles dont vous avez besoin, toujours sous la main."}},{"id":4,"title":{"text":"Autodoc"}},{"id":5,"video":{"vasttag":"blah"}}],"link":{"url":"https://play.google.com/store/apps/details?id=de.autodoc.gmbh","clicktrackers":["https://click.liftoff.io/v1/campaign_click/blah"]},"eventtrackers":[{"event":1,"method":1,"url":"https://impression-europe.liftoff.io/index/impression"},{"event":1,"method":1,"url":"https://a701.casalemedia.com/impression/v1"}],"privacy":"https://privacy.link.com"}}' } ], seat: '3970' @@ -864,7 +874,7 @@ describe('IndexexchangeAdapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - it('should return true when required params found for a banner or video ad', function () { + it('should return true when required params found for a banner, video or native ad', function () { expect(spec.isBidRequestValid(DEFAULT_BANNER_VALID_BID[0])).to.equal(true); expect(spec.isBidRequestValid(DEFAULT_VIDEO_VALID_BID[0])).to.equal(true); expect(spec.isBidRequestValid(DEFAULT_NATIVE_VALID_BID[0])).to.equal(true); @@ -1064,15 +1074,12 @@ describe('IndexexchangeAdapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should fail when native contains unrecongized properties', function () { + it('should fail if native openRTB object contains no valid assets', function () { let bid = utils.deepClone(DEFAULT_NATIVE_VALID_BID[0]); - bid.mediaTypes.native.test = {} + bid.nativeOrtbRequest = {} expect(spec.isBidRequestValid(bid)).to.be.false; - }); - it('should fail if native mediaTypes should contains no valid assets', function () { - let bid = utils.deepClone(DEFAULT_NATIVE_VALID_BID[0]); - bid.mediaTypes.native = {} + bid.nativeOrtbRequest = {assets: []} expect(spec.isBidRequestValid(bid)).to.be.false; }); }); @@ -2145,7 +2152,7 @@ describe('IndexexchangeAdapter', function () { it('should have native request', () => { const nativeImpression = JSON.parse(request[1].data.r).imp[0]; - expect(request[1].data.v).to.equal(NATIVE_ENDPOINT_VERSION); + expect(request[1].data.hasOwnProperty('v')).to.equal(false); expect(nativeImpression.id).to.equal(DEFAULT_NATIVE_VALID_BID[0].bidId); expect(nativeImpression.native).to.deep.equal(DEFAULT_NATIVE_IMP); }); @@ -2491,7 +2498,7 @@ describe('IndexexchangeAdapter', function () { const request = spec.buildRequests(DEFAULT_NATIVE_VALID_BID, DEFAULT_OPTION); const query = request[0].data; - expect(query.v).to.equal(NATIVE_ENDPOINT_VERSION); + expect(query.hasOwnProperty('v')).to.equal(false); expect(query.s).to.equal(DEFAULT_NATIVE_VALID_BID[0].params.siteId); expect(query.r).to.exist; expect(query.ac).to.equal('j'); @@ -2516,92 +2523,148 @@ describe('IndexexchangeAdapter', function () { const request = spec.buildRequests(DEFAULT_NATIVE_VALID_BID, DEFAULT_OPTION); const nativeImpression = JSON.parse(request[0].data.r).imp[0]; - expect(request[0].data.v).to.equal(NATIVE_ENDPOINT_VERSION); + expect(request[0].data.hasOwnProperty('v')).to.equal(false); expect(nativeImpression.id).to.equal(DEFAULT_NATIVE_VALID_BID[0].bidId); expect(nativeImpression.native).to.deep.equal(DEFAULT_NATIVE_IMP); }); it('should build request with given asset properties', function() { let bid = utils.deepClone(DEFAULT_NATIVE_VALID_BID) - bid[0].mediaTypes.native = { - title: { - len: 200 - }, - video: { - mimes: [ - 'javascript' - ], - minduration: 10, - maxduration: 60, - protocols: [1] - } + bid[0].nativeOrtbRequest = { + assets: [{id: 0, required: 0, title: {len: 140}}, {id: 1, required: 0, video: {mimes: ['javascript'], minduration: 10, maxduration: 60, protocols: [1]}}] } const request = spec.buildRequests(bid, DEFAULT_OPTION); const nativeImpression = JSON.parse(request[0].data.r).imp[0]; - expect(nativeImpression.native).to.deep.equal({request: '{"assets":[{"required":0,"title":{"len":200},"id":100},{"required":0,"video":{"mimes":["javascript"],"minduration":10,"maxduration":60,"protocols":[1]},"id":300}],"ver":"1.2","eventtrackers":[{"event":1,"methods":[1,2]}],"privacy":1}', ver: '1.2'}); + expect(nativeImpression.native).to.deep.equal({request: '{"assets":[{"id":0,"required":0,"title":{"len":140}},{"id":1,"required":0,"video":{"mimes":["javascript"],"minduration":10,"maxduration":60,"protocols":[1]}}],"eventtrackers":[{"event":1,"methods":[1,2]}],"privacy":1}', ver: '1.2'}); }); it('should build request with all possible Prebid asset properties', function() { let bid = utils.deepClone(DEFAULT_NATIVE_VALID_BID) - bid[0].mediaTypes.native = { - title: { - required: false - }, - body: { - required: false - }, - body2: { - required: false - }, - sponsoredBy: { - required: false - }, - icon: { - required: false - }, - image: { - required: false - }, - clickUrl: { - required: false - }, - displayUrl: { - required: false - }, - privacyLink: { - required: false - }, - privacyIcon: { - required: false - }, - cta: { - required: false - }, - rating: { - required: false - }, - downloads: { - required: false - }, - likes: { - required: false - }, - price: { - required: false - }, - salePrice: { - required: false - }, - address: { - required: false - }, - phone: { - required: false - }, + bid[0].nativeOrtbRequest = { + 'ver': '1.2', + 'assets': [ + { + 'id': 0, + 'required': 0, + 'title': { + 'len': 140 + } + }, + { + 'id': 1, + 'required': 0, + 'data': { + 'type': 2 + } + }, + { + 'id': 2, + 'required': 0, + 'data': { + 'type': 10 + } + }, + { + 'id': 3, + 'required': 0, + 'data': { + 'type': 1 + } + }, + { + 'id': 4, + 'required': 0, + 'img': { + 'type': 1 + } + }, + { + 'id': 5, + 'required': 0, + 'img': { + 'type': 3 + } + }, + { + 'id': 6, + 'required': 0 + }, + { + 'id': 7, + 'required': 0, + 'data': { + 'type': 11 + } + }, + { + 'id': 8, + 'required': 0 + }, + { + 'id': 9, + 'required': 0 + }, + { + 'id': 10, + 'required': 0, + 'data': { + 'type': 12 + } + }, + { + 'id': 11, + 'required': 0, + 'data': { + 'type': 3 + } + }, + { + 'id': 12, + 'required': 0, + 'data': { + 'type': 5 + } + }, + { + 'id': 13, + 'required': 0, + 'data': { + 'type': 4 + } + }, + { + 'id': 14, + 'required': 0, + 'data': { + 'type': 6 + } + }, + { + 'id': 15, + 'required': 0, + 'data': { + 'type': 7 + } + }, + { + 'id': 16, + 'required': 0, + 'data': { + 'type': 9 + } + }, + { + 'id': 17, + 'required': 0, + 'data': { + 'type': 8 + } + } + ] } const request = spec.buildRequests(bid, DEFAULT_OPTION); const nativeImpression = JSON.parse(request[0].data.r).imp[0]; - expect(nativeImpression.native).to.deep.equal({request: '{"assets":[{"required":0,"title":{"len":25},"id":100},{"required":0,"data":{"type":2},"id":402},{"required":0,"data":{"type":10},"id":410},{"required":0,"data":{"type":1},"id":401},{"required":0,"img":{"type":1},"id":201},{"required":0,"img":{"type":3},"id":203},{"required":0,"data":{"type":11},"id":411},{"required":0,"data":{"type":12},"id":412},{"required":0,"data":{"type":3},"id":403},{"required":0,"data":{"type":5},"id":405},{"required":0,"data":{"type":4},"id":404},{"required":0,"data":{"type":6},"id":406},{"required":0,"data":{"type":7},"id":407},{"required":0,"data":{"type":9},"id":409},{"required":0,"data":{"type":8},"id":408}],"ver":"1.2","eventtrackers":[{"event":1,"methods":[1,2]}],"privacy":1}', ver: '1.2'}); + expect(nativeImpression.native).to.deep.equal({request: '{"ver":"1.2","assets":[{"id":0,"required":0,"title":{"len":140}},{"id":1,"required":0,"data":{"type":2}},{"id":2,"required":0,"data":{"type":10}},{"id":3,"required":0,"data":{"type":1}},{"id":4,"required":0,"img":{"type":1}},{"id":5,"required":0,"img":{"type":3}},{"id":6,"required":0},{"id":7,"required":0,"data":{"type":11}},{"id":8,"required":0},{"id":9,"required":0},{"id":10,"required":0,"data":{"type":12}},{"id":11,"required":0,"data":{"type":3}},{"id":12,"required":0,"data":{"type":5}},{"id":13,"required":0,"data":{"type":4}},{"id":14,"required":0,"data":{"type":6}},{"id":15,"required":0,"data":{"type":7}},{"id":16,"required":0,"data":{"type":9}},{"id":17,"required":0,"data":{"type":8}}],"eventtrackers":[{"event":1,"methods":[1,2]}],"privacy":1}', ver: '1.2'}); }) }); @@ -3091,27 +3154,70 @@ describe('IndexexchangeAdapter', function () { advertiserDomains: ['www.abc.com'] }, native: { - body: 'Les pièces automobiles dont vous avez besoin, toujours sous la main.', - clickUrl: 'https://play.google.com/store/apps/details?id=de.autodoc.gmbh', - icon: { - height: 250, - width: 250, - url: 'https://cdn.liftoff.io/customers/1209/creatives/2501-icon-250x250.png' - }, - image: { - height: 627, - width: 1200, - url: 'https://cdn.liftoff.io/customers/5a9cab9cc6/image/lambda_png/a0355879b06c09b09232.png' - }, - impressionTrackers: [ - 'https://click.liftoff.io/v1/campaign_click/blah', - 'https://impression-europe.liftoff.io/index/impression', - 'https://a701.casalemedia.com/impression/v1' - ], - privacyLink: 'https://privacy.link.com', - sponsoredBy: 'autodoc.co.uk', - title: 'Autodoc', - video: 'blah' + ortb: { + assets: [ + { + 'id': 0, + 'img': { + 'h': 250, + 'url': 'https://cdn.liftoff.io/customers/1209/creatives/2501-icon-250x250.png', + 'w': 250 + } + }, + { + 'id': 1, + 'img': { + 'h': 627, + 'url': 'https://cdn.liftoff.io/customers/5a9cab9cc6/image/lambda_png/a0355879b06c09b09232.png', + 'w': 1200 + } + }, + { + 'data': { + 'value': 'autodoc.co.uk' + }, + 'id': 2 + }, + { + 'data': { + 'value': 'Les pièces automobiles dont vous avez besoin, toujours sous la main.' + }, + 'id': 3 + }, + { + 'id': 4, + 'title': { + 'text': 'Autodoc' + } + }, + { + 'id': 5, + 'video': { + 'vasttag': 'blah' + } + } + ], + 'eventtrackers': [ + { + 'event': 1, + 'method': 1, + 'url': 'https://impression-europe.liftoff.io/index/impression' + }, + { + 'event': 1, + 'method': 1, + 'url': 'https://a701.casalemedia.com/impression/v1' + } + ], + 'link': { + 'clicktrackers': [ + 'https://click.liftoff.io/v1/campaign_click/blah' + ], + 'url': 'https://play.google.com/store/apps/details?id=de.autodoc.gmbh' + }, + 'privacy': 'https://privacy.link.com', + 'ver': '1.2' + } }, ttl: 3600 } From 296a081949b11a09b789f17f1b7dba5a72b7d5b3 Mon Sep 17 00:00:00 2001 From: Mike Miller Date: Thu, 18 Aug 2022 13:46:13 -0400 Subject: [PATCH 084/246] Update Sonobi adapter with GVLID (#8860) --- modules/sonobiBidAdapter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index c6100ac8b40..3c841cc4d8a 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -11,6 +11,7 @@ const OUTSTREAM_REDNERER_URL = 'https://mtrx.go.sonobi.com/sbi_outstream_rendere export const spec = { code: BIDDER_CODE, + gvlid: 104, supportedMediaTypes: [BANNER, VIDEO], /** * Determines whether or not the given bid request is valid. From ed74e44a6cec85a6eb572d19033b57e2cef7a482 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 18 Aug 2022 11:06:44 -0700 Subject: [PATCH 085/246] dgkeyword RTD provider: fix tests causing ID5 test failures (#8862) --- modules/dgkeywordRtdProvider.js | 9 +++++++++ test/spec/modules/dgkeywordRtdProvider_spec.js | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/dgkeywordRtdProvider.js b/modules/dgkeywordRtdProvider.js index cea33014144..e2a29375f25 100644 --- a/modules/dgkeywordRtdProvider.js +++ b/modules/dgkeywordRtdProvider.js @@ -25,6 +25,15 @@ export function getDgKeywordsAndSet(reqBidsConfigObj, callback, moduleConfig, us const timeout = (moduleConfig && moduleConfig.params && moduleConfig.params.timeout && Number(moduleConfig.params.timeout) > 0) ? Number(moduleConfig.params.timeout) : PROFILE_TIMEOUT_MS; const url = (moduleConfig && moduleConfig.params && moduleConfig.params.url) ? moduleConfig.params.url : URL + encodeURIComponent(window.location.href); const adUnits = reqBidsConfigObj.adUnits || getGlobal().adUnits; + callback = (function(cb) { + let done = false; + return function () { + if (!done) { + done = true; + return cb.apply(this, arguments); + } + } + })(callback); let isFinish = false; logMessage('[dgkeyword sub module]', adUnits, timeout); let setKeywordTargetBidders = getTargetBidderOfDgKeywords(adUnits); diff --git a/test/spec/modules/dgkeywordRtdProvider_spec.js b/test/spec/modules/dgkeywordRtdProvider_spec.js index a145f429557..5ecec48f6b4 100644 --- a/test/spec/modules/dgkeywordRtdProvider_spec.js +++ b/test/spec/modules/dgkeywordRtdProvider_spec.js @@ -293,8 +293,8 @@ describe('Digital Garage Keyword Module', function () { moduleConfig, null ); + const request = server.requests[0]; setTimeout(() => { - const request = server.requests[0]; if (request) { request.respond( 200, From f69cc666f12d03fbb5b0c4342d17f71103dd1d2f Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Thu, 18 Aug 2022 19:02:43 -0400 Subject: [PATCH 086/246] Id5 id configurable fetch flow (#8784) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paweł Kowalski --- modules/id5IdSystem.js | 271 ++++++---- modules/id5IdSystem.md | 30 +- test/spec/modules/id5IdSystem_spec.js | 701 +++++++++++++++++++------- 3 files changed, 705 insertions(+), 297 deletions(-) diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index 96ec1fed754..95ce5c94e46 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -7,19 +7,19 @@ import { deepAccess, - logInfo, deepSetValue, - logError, isEmpty, isEmptyStr, + logError, + logInfo, logWarn, safeJSONParse } from '../src/utils.js'; -import { ajax } from '../src/ajax.js'; -import { submodule } from '../src/hook.js'; -import { getRefererInfo } from '../src/refererDetection.js'; -import { getStorageManager } from '../src/storageManager.js'; -import { uspDataHandler } from '../src/adapterManager.js'; +import {ajax} from '../src/ajax.js'; +import {submodule} from '../src/hook.js'; +import {getRefererInfo} from '../src/refererDetection.js'; +import {getStorageManager} from '../src/storageManager.js'; +import {uspDataHandler} from '../src/adapterManager.js'; const MODULE_NAME = 'id5Id'; const GVLID = 131; @@ -28,10 +28,11 @@ export const ID5_STORAGE_NAME = 'id5id'; export const ID5_PRIVACY_STORAGE_NAME = `${ID5_STORAGE_NAME}_privacy`; const LOCAL_STORAGE = 'html5'; const LOG_PREFIX = 'User ID - ID5 submodule: '; +const ID5_API_CONFIG_URL = 'https://id5-sync.com/api/config/prebid' // order the legacy cookie names in reverse priority order so the last // cookie in the array is the most preferred to use -const LEGACY_COOKIE_NAMES = [ 'pbjs-id5id', 'id5id.1st', 'id5id' ]; +const LEGACY_COOKIE_NAMES = ['pbjs-id5id', 'id5id.1st', 'id5id']; export const storage = getStorageManager({gvlid: GVLID, moduleName: MODULE_NAME}); @@ -102,92 +103,27 @@ export const id5IdSubmodule = { /** * performs action to obtain id and return a value in the callback's response argument * @function getId - * @param {SubmoduleConfig} config + * @param {SubmoduleConfig} submoduleConfig * @param {ConsentData} consentData * @param {(Object|undefined)} cacheIdObj * @returns {IdResponse|undefined} */ - getId(config, consentData, cacheIdObj) { - if (!hasRequiredConfig(config)) { + getId(submoduleConfig, consentData, cacheIdObj) { + if (!hasRequiredConfig(submoduleConfig)) { return undefined; } - const url = `https://id5-sync.com/g/v2/${config.params.partner}.json`; - const hasGdpr = (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) ? 1 : 0; - const usp = uspDataHandler.getConsentData(); - const referer = getRefererInfo(); - const signature = (cacheIdObj && cacheIdObj.signature) ? cacheIdObj.signature : getLegacyCookieSignature(); - const data = { - 'partner': config.params.partner, - 'gdpr': hasGdpr, - 'nbPage': incrementNb(config.params.partner), - 'o': 'pbjs', - 'rf': referer.topmostLocation, - 'top': referer.reachedTop ? 1 : 0, - 'u': referer.stack[0] || window.location.href, - 'v': '$prebid.version$' - }; - - // pass in optional data, but only if populated - if (hasGdpr && typeof consentData.consentString !== 'undefined' && !isEmpty(consentData.consentString) && !isEmptyStr(consentData.consentString)) { - data.gdpr_consent = consentData.consentString; - } - if (typeof usp !== 'undefined' && !isEmpty(usp) && !isEmptyStr(usp)) { - data.us_privacy = usp; - } - if (typeof signature !== 'undefined' && !isEmptyStr(signature)) { - data.s = signature; - } - if (typeof config.params.pd !== 'undefined' && !isEmptyStr(config.params.pd)) { - data.pd = config.params.pd; - } - if (typeof config.params.provider !== 'undefined' && !isEmptyStr(config.params.provider)) { - data.provider = config.params.provider; - } - - const abTestingConfig = getAbTestingConfig(config); - if (abTestingConfig.enabled === true) { - data.ab_testing = { - enabled: true, - control_group_pct: abTestingConfig.controlGroupPct // The server validates - }; - } - - const resp = function (callback) { - const callbacks = { - success: response => { - let responseObj; - if (response) { - try { - responseObj = JSON.parse(response); - logInfo(LOG_PREFIX + 'response received from the server', responseObj); - - resetNb(config.params.partner); - - if (responseObj.privacy) { - storeInLocalStorage(ID5_PRIVACY_STORAGE_NAME, JSON.stringify(responseObj.privacy), NB_EXP_DAYS); - } - - // TODO: remove after requiring publishers to use localstorage and - // all publishers have upgraded - if (config.storage.type === LOCAL_STORAGE) { - removeLegacyCookies(config.params.partner); - } - } catch (error) { - logError(LOG_PREFIX + error); - } - } - callback(responseObj); - }, - error: error => { + const resp = function (cbFunction) { + new IdFetchFlow(submoduleConfig, consentData, cacheIdObj, uspDataHandler.getConsentData()).execute() + .then(response => { + cbFunction(response) + }) + .catch(error => { logError(LOG_PREFIX + 'getId fetch encountered an error', error); - callback(); - } - }; - logInfo(LOG_PREFIX + 'requesting an ID from the server', data); - ajax(url, callbacks, JSON.stringify(data), { method: 'POST', withCredentials: true }); + cbFunction(); + }); }; - return { callback: resp }; + return {callback: resp}; }, /** @@ -212,6 +148,139 @@ export const id5IdSubmodule = { } }; +class IdFetchFlow { + constructor(submoduleConfig, gdprConsentData, cacheIdObj, usPrivacyData) { + this.submoduleConfig = submoduleConfig + this.gdprConsentData = gdprConsentData + this.cacheIdObj = cacheIdObj + this.usPrivacyData = usPrivacyData + } + + execute() { + return this.#callForConfig(this.submoduleConfig) + .then(fetchFlowConfig => { + return this.#callForExtensions(fetchFlowConfig.extensionsCall) + .then(extensionsData => { + return this.#callId5Fetch(fetchFlowConfig.fetchCall, extensionsData) + }) + }) + .then(fetchCallResponse => { + try { + resetNb(this.submoduleConfig.params.partner); + if (fetchCallResponse.privacy) { + storeInLocalStorage(ID5_PRIVACY_STORAGE_NAME, JSON.stringify(fetchCallResponse.privacy), NB_EXP_DAYS); + } + } catch (error) { + logError(LOG_PREFIX + error); + } + return fetchCallResponse; + }) + } + + #ajaxPromise(url, data, options) { + return new Promise((resolve, reject) => { + ajax(url, + { + success: function (res) { + resolve(res) + }, + error: function (err) { + reject(err) + } + }, data, options) + }) + } + + // eslint-disable-next-line no-dupe-class-members + #callForConfig(submoduleConfig) { + let url = submoduleConfig.params.configUrl || ID5_API_CONFIG_URL; // override for debug/test purposes only + return this.#ajaxPromise(url, JSON.stringify(submoduleConfig), {method: 'POST'}) + .then(response => { + let responseObj = JSON.parse(response); + logInfo(LOG_PREFIX + 'config response received from the server', responseObj); + return responseObj; + }); + } + + // eslint-disable-next-line no-dupe-class-members + #callForExtensions(extensionsCallConfig) { + if (extensionsCallConfig === undefined) { + return Promise.resolve(undefined) + } + let extensionsUrl = extensionsCallConfig.url + let method = extensionsCallConfig.method || 'GET' + let data = method === 'GET' ? undefined : JSON.stringify(extensionsCallConfig.body || {}) + return this.#ajaxPromise(extensionsUrl, data, {'method': method}) + .then(response => { + let responseObj = JSON.parse(response); + logInfo(LOG_PREFIX + 'extensions response received from the server', responseObj); + return responseObj; + }) + } + + // eslint-disable-next-line no-dupe-class-members + #callId5Fetch(fetchCallConfig, extensionsData) { + let url = fetchCallConfig.url; + let additionalData = fetchCallConfig.overrides || {}; + let data = { + ...this.#createFetchRequestData(), + ...additionalData, + extensions: extensionsData + }; + return this.#ajaxPromise(url, JSON.stringify(data), {method: 'POST', withCredentials: true}) + .then(response => { + let responseObj = JSON.parse(response); + logInfo(LOG_PREFIX + 'fetch response received from the server', responseObj); + return responseObj; + }); + } + + // eslint-disable-next-line no-dupe-class-members + #createFetchRequestData() { + const params = this.submoduleConfig.params; + const hasGdpr = (this.gdprConsentData && typeof this.gdprConsentData.gdprApplies === 'boolean' && this.gdprConsentData.gdprApplies) ? 1 : 0; + const referer = getRefererInfo(); + const signature = (this.cacheIdObj && this.cacheIdObj.signature) ? this.cacheIdObj.signature : getLegacyCookieSignature(); + const nbPage = incrementNb(params.partner); + const data = { + 'partner': params.partner, + 'gdpr': hasGdpr, + 'nbPage': nbPage, + 'o': 'pbjs', + 'rf': referer.topmostLocation, + 'top': referer.reachedTop ? 1 : 0, + 'u': referer.stack[0] || window.location.href, + 'v': '$prebid.version$', + 'storage': this.submoduleConfig.storage + }; + + // pass in optional data, but only if populated + if (hasGdpr && this.gdprConsentData.consentString !== undefined && !isEmpty(this.gdprConsentData.consentString) && !isEmptyStr(this.gdprConsentData.consentString)) { + data.gdpr_consent = this.gdprConsentData.consentString; + } + if (this.usPrivacyData !== undefined && !isEmpty(this.usPrivacyData) && !isEmptyStr(this.usPrivacyData)) { + data.us_privacy = this.usPrivacyData; + } + if (signature !== undefined && !isEmptyStr(signature)) { + data.s = signature; + } + if (params.pd !== undefined && !isEmptyStr(params.pd)) { + data.pd = params.pd; + } + if (params.provider !== undefined && !isEmptyStr(params.provider)) { + data.provider = params.provider; + } + const abTestingConfig = params.abTesting || {enabled: false}; + + if (abTestingConfig.enabled) { + data.ab_testing = { + enabled: true, control_group_pct: abTestingConfig.controlGroupPct // The server validates + }; + } + return data + } +} + function hasRequiredConfig(config) { if (!config || !config.params || !config.params.partner || typeof config.params.partner !== 'number') { logError(LOG_PREFIX + 'partner required to be defined as a number'); @@ -242,25 +311,29 @@ export function expDaysStr(expDays) { export function nbCacheName(partnerId) { return `${ID5_STORAGE_NAME}_${partnerId}_nb`; } + export function storeNbInCache(partnerId, nb) { storeInLocalStorage(nbCacheName(partnerId), nb, NB_EXP_DAYS); } + export function getNbFromCache(partnerId) { let cacheNb = getFromLocalStorage(nbCacheName(partnerId)); return (cacheNb) ? parseInt(cacheNb) : 0; } + function incrementNb(partnerId) { const nb = (getNbFromCache(partnerId) + 1); storeNbInCache(partnerId, nb); return nb; } + function resetNb(partnerId) { storeNbInCache(partnerId, 0); } function getLegacyCookieSignature() { let legacyStoredValue; - LEGACY_COOKIE_NAMES.forEach(function(cookie) { + LEGACY_COOKIE_NAMES.forEach(function (cookie) { if (storage.getCookie(cookie)) { legacyStoredValue = safeJSONParse(storage.getCookie(cookie)) || legacyStoredValue; } @@ -268,21 +341,6 @@ function getLegacyCookieSignature() { return (legacyStoredValue && legacyStoredValue.signature) || ''; } -/** - * Remove our legacy cookie values. Needed until we move all publishers - * to html5 storage in a future release - * @param {integer} partnerId - */ -function removeLegacyCookies(partnerId) { - logInfo(LOG_PREFIX + 'removing legacy cookies'); - LEGACY_COOKIE_NAMES.forEach(function(cookie) { - storage.setCookie(`${cookie}`, ' ', expDaysStr(-1)); - storage.setCookie(`${cookie}_nb`, ' ', expDaysStr(-1)); - storage.setCookie(`${cookie}_${partnerId}_nb`, ' ', expDaysStr(-1)); - storage.setCookie(`${cookie}_last`, ' ', expDaysStr(-1)); - }); -} - /** * This will make sure we check for expiration before accessing local storage * @param {string} key @@ -303,6 +361,7 @@ export function getFromLocalStorage(key) { storage.removeDataFromLocalStorage(key); return null; } + /** * Ensure that we always set an expiration in local storage since * by default it's not required @@ -315,14 +374,4 @@ export function storeInLocalStorage(key, value, expDays) { storage.setDataInLocalStorage(`${key}`, value); } -/** - * gets the existing abTesting config or generates a default config with abTesting off - * - * @param {SubmoduleConfig|undefined} config - * @returns {Object} an object which always contains at least the property "enabled" - */ -function getAbTestingConfig(config) { - return deepAccess(config, 'params.abTesting', { enabled: false }); -} - submodule('userId', id5IdSubmodule); diff --git a/modules/id5IdSystem.md b/modules/id5IdSystem.md index 11f8ffc5609..cf90290b1d8 100644 --- a/modules/id5IdSystem.md +++ b/modules/id5IdSystem.md @@ -29,7 +29,8 @@ pbjs.setConfig({ abTesting: { // optional enabled: true, // false by default controlGroupPct: 0.1 // valid values are 0.0 - 1.0 (inclusive) - } + }, + disableExtensions: false // optional }, storage: { type: 'html5', // "html5" is the required storage type @@ -43,21 +44,22 @@ pbjs.setConfig({ }); ``` -| Param under userSync.userIds[] | Scope | Type | Description | Example | +| Param under userSync.userIds[] | Scope | Type | Description | Example | | --- | --- | --- | --- | --- | -| name | Required | String | The name of this module: `"id5Id"` | `"id5Id"` | -| params | Required | Object | Details for the ID5 ID. | | -| params.partner | Required | Number | This is the ID5 Partner Number obtained from registering with ID5. | `173` | -| params.pd | Optional | String | Partner-supplied data used for linking ID5 IDs across domains. See [our documentation](https://support.id5.io/portal/en/kb/articles/passing-partner-data-to-id5) for details on generating the string. Omit the parameter or leave as an empty string if no data to supply | `"MT1iNTBjY..."` | -| params.provider | Optional | String | An identifier provided by ID5 to technology partners who manage Prebid setups on behalf of publishers. Reach out to [ID5](mailto:prebid@id5.io) if you have questions about this parameter | `pubmatic-identity-hub` | -| params.abTesting | Optional | Object | Allows publishers to easily run an A/B Test. If enabled and the user is in the Control Group, the ID5 ID will NOT be exposed to bid adapters for that request | Disabled by default | -| params.abTesting.enabled | Optional | Boolean | Set this to `true` to turn on this feature | `true` or `false` | +| name | Required | String | The name of this module: `"id5Id"` | `"id5Id"` | +| params | Required | Object | Details for the ID5 ID. | | +| params.partner | Required | Number | This is the ID5 Partner Number obtained from registering with ID5. | `173` | +| params.pd | Optional | String | Partner-supplied data used for linking ID5 IDs across domains. See [our documentation](https://support.id5.io/portal/en/kb/articles/passing-partner-data-to-id5) for details on generating the string. Omit the parameter or leave as an empty string if no data to supply | `"MT1iNTBjY..."` | +| params.provider | Optional | String | An identifier provided by ID5 to technology partners who manage Prebid setups on behalf of publishers. Reach out to [ID5](mailto:prebid@id5.io) if you have questions about this parameter | `pubmatic-identity-hub` | +| params.abTesting | Optional | Object | Allows publishers to easily run an A/B Test. If enabled and the user is in the Control Group, the ID5 ID will NOT be exposed to bid adapters for that request | Disabled by default | +| params.abTesting.enabled | Optional | Boolean | Set this to `true` to turn on this feature | `true` or `false` | | params.abTesting.controlGroupPct | Optional | Number | Must be a number between `0.0` and `1.0` (inclusive) and is used to determine the percentage of requests that fall into the control group (and thus not exposing the ID5 ID). For example, a value of `0.20` will result in 20% of requests without an ID5 ID and 80% with an ID. | `0.1` | -| storage | Required | Object | Storage settings for how the User ID module will cache the ID5 ID locally | | -| storage.type | Required | String | This is where the results of the user ID will be stored. ID5 **requires** `"html5"`. | `"html5"` | -| storage.name | Required | String | The name of the local storage where the user ID will be stored. ID5 **requires** `"id5id"`. | `"id5id"` | -| storage.expires | Optional | Integer | How long (in days) the user ID information will be stored. ID5 recommends `90`. | `90` | -| storage.refreshInSeconds | Optional | Integer | How many seconds until the ID5 ID will be refreshed. ID5 strongly recommends 8 hours between refreshes | `8*3600` | +| params.disableExtensions | Optional | Boolean | Set this to `true` to force turn off extensions call. Default `false` | `true` or `false` | +| storage | Required | Object | Storage settings for how the User ID module will cache the ID5 ID locally | | +| storage.type | Required | String | This is where the results of the user ID will be stored. ID5 **requires** `"html5"`. | `"html5"` | +| storage.name | Required | String | The name of the local storage where the user ID will be stored. ID5 **requires** `"id5id"`. | `"id5id"` | +| storage.expires | Optional | Integer | How long (in days) the user ID information will be stored. ID5 recommends `90`. | `90` | +| storage.refreshInSeconds | Optional | Integer | How many seconds until the ID5 ID will be refreshed. ID5 strongly recommends 8 hours between refreshes | `8*3600` | **ATTENTION:** As of Prebid.js v4.14.0, ID5 requires `storage.type` to be `"html5"` and `storage.name` to be `"id5id"`. Using other values will display a warning today, but in an upcoming release, it will prevent the ID5 module from loading. This change is to ensure the ID5 module in Prebid.js interoperates properly with the [ID5 API](https://github.com/id5io/id5-api.js) and to reduce the size of publishers' first-party cookies that are sent to their web servers. If you have any questions, please reach out to us at [prebid@id5.io](mailto:prebid@id5.io). diff --git a/test/spec/modules/id5IdSystem_spec.js b/test/spec/modules/id5IdSystem_spec.js index a54542f7278..8c0f8ad9cf3 100644 --- a/test/spec/modules/id5IdSystem_spec.js +++ b/test/spec/modules/id5IdSystem_spec.js @@ -5,28 +5,36 @@ import { ID5_PRIVACY_STORAGE_NAME, ID5_STORAGE_NAME, id5IdSubmodule, - nbCacheName, storage, + nbCacheName, + storage, storeInLocalStorage, storeNbInCache, } from 'modules/id5IdSystem.js'; import {coreStorage, init, requestBidsHook, setSubmoduleRegistry} from 'modules/userId/index.js'; import {config} from 'src/config.js'; -import {server} from 'test/mocks/xhr.js'; import * as events from 'src/events.js'; import CONSTANTS from 'src/constants.json'; import * as utils from 'src/utils.js'; +import {uspDataHandler} from 'src/adapterManager.js'; import 'src/prebid.js'; import {hook} from '../../../src/hook.js'; import {mockGdprConsent} from '../../helpers/consentData.js'; let expect = require('chai').expect; -describe('ID5 ID System', function() { +describe('ID5 ID System', function () { const ID5_MODULE_NAME = 'id5Id'; const ID5_EIDS_NAME = ID5_MODULE_NAME.toLowerCase(); const ID5_SOURCE = 'id5-sync.com'; const ID5_TEST_PARTNER_ID = 173; const ID5_ENDPOINT = `https://id5-sync.com/g/v2/${ID5_TEST_PARTNER_ID}.json`; + const ID5_API_CONFIG_URL = `https://id5-sync.com/api/config/prebid`; + const ID5_EXTENSIONS_ENDPOINT = 'https://extensions.id5-sync.com/test'; + const ID5_API_CONFIG = { + fetchCall: { + url: ID5_ENDPOINT + } + }; const ID5_NB_STORAGE_NAME = nbCacheName(ID5_TEST_PARTNER_ID); const ID5_STORED_ID = 'storedid5id'; const ID5_STORED_SIGNATURE = '123456'; @@ -58,6 +66,7 @@ describe('ID5 ID System', function() { } } } + function getId5ValueConfig(value) { return { name: ID5_MODULE_NAME, @@ -68,6 +77,7 @@ describe('ID5 ID System', function() { } } } + function getUserSyncConfig(userIds) { return { userSync: { @@ -76,12 +86,15 @@ describe('ID5 ID System', function() { } } } + function getFetchLocalStorageConfig() { return getUserSyncConfig([getId5FetchConfig(ID5_STORAGE_NAME, 'html5')]); } + function getValueConfig(value) { return getUserSyncConfig([getId5ValueConfig(value)]); } + function getAdUnitMock(code = 'adUnit-code') { return { code, @@ -91,15 +104,81 @@ describe('ID5 ID System', function() { }; } + function callSubmoduleGetId(config, consentData, cacheIdObj) { + return new Promise((resolve) => { + id5IdSubmodule.getId(config, consentData, cacheIdObj).callback((response) => { + resolve(response) + }) + }); + } + + class XhrServerMock { + constructor(server) { + this.currentRequestIdx = 0 + this.server = server + } + + expectFirstRequest() { + return this.#expectRequest(0); + } + + expectNextRequest() { + return this.#expectRequest(++this.currentRequestIdx) + } + + expectConfigRequest() { + return this.expectFirstRequest() + .then(configRequest => { + expect(configRequest.url).is.eq(ID5_API_CONFIG_URL); + expect(configRequest.method).is.eq('POST'); + return configRequest; + }) + } + + respondWithConfigAndExpectNext(configRequest, config = ID5_API_CONFIG) { + configRequest.respond(200, {'Content-Type': 'application/json'}, JSON.stringify(config)); + return this.expectNextRequest() + } + + expectFetchRequest() { + return this.expectConfigRequest() + .then(configRequest => { + return this.respondWithConfigAndExpectNext(configRequest, ID5_API_CONFIG); + }).then(request => { + expect(request.url).is.eq(ID5_API_CONFIG.fetchCall.url); + expect(request.method).is.eq('POST'); + return request; + }) + } + + #expectRequest(index) { + let server = this.server + return new Promise(function (resolve) { + (function waitForCondition() { + if (server.requests && server.requests.length > index) return resolve(server.requests[index]); + setTimeout(waitForCondition, 30); + })(); + }) + .then(request => { + return request + }); + } + + hasReceivedAnyRequest() { + let requests = this.server.requests; + return requests && requests.length > 0; + } + } + before(() => { hook.ready(); }); - describe('Check for valid publisher config', function() { - it('should fail with invalid config', function() { + describe('Check for valid publisher config', function () { + it('should fail with invalid config', function () { // no config - expect(id5IdSubmodule.getId()).to.be.eq(undefined); - expect(id5IdSubmodule.getId({ })).to.be.eq(undefined); + expect(id5IdSubmodule.getId()).is.eq(undefined); + expect(id5IdSubmodule.getId({})).is.eq(undefined); // valid params, invalid storage expect(id5IdSubmodule.getId({ params: { partner: 123 } })).to.be.eq(undefined); @@ -113,7 +192,7 @@ describe('ID5 ID System', function() { expect(id5IdSubmodule.getId({ storage: { name: 'name', type: 'html5', }, params: { partner: 'abc' } })).to.be.eq(undefined); }); - it('should warn with non-recommended storage params', function() { + it('should warn with non-recommended storage params', function () { let logWarnStub = sinon.stub(utils, 'logWarn'); id5IdSubmodule.getId({ storage: { name: 'name', type: 'html5', }, params: { partner: 123 } }); @@ -126,189 +205,457 @@ describe('ID5 ID System', function() { }); }); - describe('Xhr Requests from getId()', function() { - const responseHeader = { 'Content-Type': 'application/json' }; - let callbackSpy = sinon.spy(); + describe('Xhr Requests from getId()', function () { + const responseHeader = {'Content-Type': 'application/json'}; - beforeEach(function() { - callbackSpy.resetHistory(); + beforeEach(function () { }); - afterEach(function () { + afterEach(function () { + uspDataHandler.reset() }); it('should call the ID5 server and handle a valid response', function () { - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, undefined).callback; - submoduleCallback(callbackSpy); - - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(request.url).to.contain(ID5_ENDPOINT); - expect(request.withCredentials).to.be.true; - expect(requestBody.partner).to.eq(ID5_TEST_PARTNER_ID); - expect(requestBody.o).to.eq('pbjs'); - expect(requestBody.pd).to.be.undefined; - expect(requestBody.s).to.be.undefined; - expect(requestBody.provider).to.be.undefined - expect(requestBody.v).to.eq('$prebid.version$'); - expect(requestBody.gdpr).to.exist; - expect(requestBody.gdpr_consent).to.be.undefined; - expect(requestBody.us_privacy).to.be.undefined; - - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - expect(callbackSpy.calledOnce).to.be.true; - expect(callbackSpy.lastCall.lastArg).to.deep.equal(ID5_JSON_RESPONSE); + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let config = getId5FetchConfig(); + let submoduleResponse = callSubmoduleGetId(config, undefined, undefined); + + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(fetchRequest.url).to.contain(ID5_ENDPOINT); + expect(fetchRequest.withCredentials).is.true; + expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); + expect(requestBody.o).is.eq('pbjs'); + expect(requestBody.pd).is.undefined; + expect(requestBody.s).is.undefined; + expect(requestBody.provider).is.undefined + expect(requestBody.v).is.eq('$prebid.version$'); + expect(requestBody.gdpr).is.eq(0); + expect(requestBody.gdpr_consent).is.undefined; + expect(requestBody.us_privacy).is.undefined; + expect(requestBody.storage).is.deep.eq(config.storage) + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + .then(submoduleResponse => { + expect(submoduleResponse).is.deep.equal(ID5_JSON_RESPONSE); + }); + }); + + it('should call the ID5 server with gdpr data ', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let consentData = { + gdprApplies: true, + consentString: 'consentString' + } + + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), consentData, undefined); + + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); + expect(requestBody.gdpr).to.eq(1); + expect(requestBody.gdpr_consent).is.eq(consentData.consentString); + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + .then(submoduleResponse => { + expect(submoduleResponse).is.deep.equal(ID5_JSON_RESPONSE); + }); + }); + + it('should call the ID5 server without gdpr data when gdpr not applies ', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let consentData = { + gdprApplies: false, + consentString: 'consentString' + } + + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), consentData, undefined); + + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.gdpr).to.eq(0); + expect(requestBody.gdpr_consent).is.undefined + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + .then(submoduleResponse => { + expect(submoduleResponse).is.deep.equal(ID5_JSON_RESPONSE); + }); + }); + + it('should call the ID5 server with us privacy consent', function () { + let usPrivacyString = '1YN-'; + uspDataHandler.setConsentData(usPrivacyString) + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let consentData = { + gdprApplies: true, + consentString: 'consentString' + } + + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), consentData, undefined); + + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); + expect(requestBody.us_privacy).to.eq(usPrivacyString); + + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + .then(submoduleResponse => { + expect(submoduleResponse).is.deep.equal(ID5_JSON_RESPONSE); + }); }); it('should call the ID5 server with no signature field when no stored object', function () { - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, undefined).callback; - submoduleCallback(callbackSpy); + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, undefined); + + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.s).is.undefined; + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + }); - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(requestBody.s).to.be.undefined; + it('should call the ID5 server for config with submodule config object', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let id5FetchConfig = getId5FetchConfig(); + id5FetchConfig.params.extraParam = { + x: 'X', + y: { + a: 1, + b: '3' + } + } + let submoduleResponse = callSubmoduleGetId(id5FetchConfig, undefined, undefined); + + return xhrServerMock.expectConfigRequest() + .then(configRequest => { + let requestBody = JSON.parse(configRequest.requestBody) + expect(requestBody).is.deep.eq(id5FetchConfig) + return xhrServerMock.respondWithConfigAndExpectNext(configRequest) + }) + .then(fetchRequest => { + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + }); - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + it('should call the ID5 server for config with overridden url', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let id5FetchConfig = getId5FetchConfig(); + id5FetchConfig.params.configUrl = 'http://localhost/x/y/z' + + let submoduleResponse = callSubmoduleGetId(id5FetchConfig, undefined, undefined); + + return xhrServerMock.expectFirstRequest() + .then(configRequest => { + expect(configRequest.url).is.eq('http://localhost/x/y/z') + return xhrServerMock.respondWithConfigAndExpectNext(configRequest) + }) + .then(fetchRequest => { + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) }); - it('should call the ID5 server with signature field from stored object', function () { - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); + it('should call the ID5 server with additional data when provided', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, undefined); + + return xhrServerMock.expectConfigRequest() + .then(configRequest => { + return xhrServerMock.respondWithConfigAndExpectNext(configRequest, { + fetchCall: { + url: ID5_ENDPOINT, + overrides: { + arg1: '123', + arg2: { + x: '1', + y: 2 + } + } + } + }); + }) + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); + expect(requestBody.o).is.eq('pbjs'); + expect(requestBody.v).is.eq('$prebid.version$'); + expect(requestBody.arg1).is.eq('123') + expect(requestBody.arg2).is.deep.eq({ + x: '1', + y: 2 + }) + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + }); - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(requestBody.s).to.eq(ID5_STORED_SIGNATURE); + it('should call the ID5 server with extensions', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, undefined); + + return xhrServerMock.expectConfigRequest() + .then(configRequest => { + return xhrServerMock.respondWithConfigAndExpectNext(configRequest, { + fetchCall: { + url: ID5_ENDPOINT + }, + extensionsCall: { + url: ID5_EXTENSIONS_ENDPOINT, + method: 'GET' + } + }); + }) + .then(extensionsRequest => { + expect(extensionsRequest.url).is.eq(ID5_EXTENSIONS_ENDPOINT) + expect(extensionsRequest.method).is.eq('GET') + extensionsRequest.respond(200, responseHeader, JSON.stringify({ + lb: 'ex' + })) + return xhrServerMock.expectNextRequest(); + }) + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); + expect(requestBody.o).is.eq('pbjs'); + expect(requestBody.v).is.eq('$prebid.version$'); + expect(requestBody.extensions).is.deep.eq({ + lb: 'ex' + }) + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + }); + + it('should call the ID5 server with extensions fetched with POST', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, undefined); + + return xhrServerMock.expectConfigRequest() + .then(configRequest => { + return xhrServerMock.respondWithConfigAndExpectNext(configRequest, { + fetchCall: { + url: ID5_ENDPOINT + }, + extensionsCall: { + url: ID5_EXTENSIONS_ENDPOINT, + method: 'POST', + body: { + x: '1', + y: 2 + } + } + }); + }) + .then(extensionsRequest => { + expect(extensionsRequest.url).is.eq(ID5_EXTENSIONS_ENDPOINT) + expect(extensionsRequest.method).is.eq('POST') + let requestBody = JSON.parse(extensionsRequest.requestBody) + expect(requestBody).is.deep.eq({ + x: '1', + y: 2 + }) + extensionsRequest.respond(200, responseHeader, JSON.stringify({ + lb: 'post', + })) + return xhrServerMock.expectNextRequest(); + }) + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.partner).is.eq(ID5_TEST_PARTNER_ID); + expect(requestBody.o).is.eq('pbjs'); + expect(requestBody.v).is.eq('$prebid.version$'); + expect(requestBody.extensions).is.deep.eq({ + lb: 'post' + }) + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + }); - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + it('should call the ID5 server with signature field from stored object', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); + + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.s).is.eq(ID5_STORED_SIGNATURE); + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) }); it('should call the ID5 server with pd field when pd config is set', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) const pubData = 'b50ca08271795a8e7e4012813f23d505193d75c0f2e2bb99baa63aa822f66ed3'; let id5Config = getId5FetchConfig(); id5Config.params.pd = pubData; - let submoduleCallback = id5IdSubmodule.getId(id5Config, undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); - - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(requestBody.pd).to.eq(pubData); + let submoduleResponse = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.pd).is.eq(pubData); + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse; + }) }); it('should call the ID5 server with no pd field when pd config is not set', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) let id5Config = getId5FetchConfig(); id5Config.params.pd = undefined; - let submoduleCallback = id5IdSubmodule.getId(id5Config, undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); + let submoduleResponse = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(requestBody.pd).to.be.undefined; - - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.pd).is.undefined; + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse; + }) }); it('should call the ID5 server with nb=1 when no stored value exists and reset after', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) coreStorage.removeDataFromLocalStorage(ID5_NB_STORAGE_NAME); - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); - - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(requestBody.nbPage).to.eq(1); - - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - - expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(0); + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); + + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.nbPage).is.eq(1); + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + .then(() => { + expect(getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(0); + }) }); it('should call the ID5 server with incremented nb when stored value exists and reset after', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) storeNbInCache(ID5_TEST_PARTNER_ID, 1); - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); - - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(requestBody.nbPage).to.eq(2); - - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - - expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(0); + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); + + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.nbPage).is.eq(2); + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + .then(() => { + expect(getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(0); + }) }); it('should call the ID5 server with ab_testing object when abTesting is turned on', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) let id5Config = getId5FetchConfig(); - id5Config.params.abTesting = { enabled: true, controlGroupPct: 0.234 } - - let submoduleCallback = id5IdSubmodule.getId(id5Config, undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); + id5Config.params.abTesting = {enabled: true, controlGroupPct: 0.234} - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(requestBody.ab_testing.enabled).to.eq(true); - expect(requestBody.ab_testing.control_group_pct).to.eq(0.234); + let submoduleResponse = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.ab_testing.enabled).is.eq(true); + expect(requestBody.ab_testing.control_group_pct).is.eq(0.234); + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse; + }); }); it('should call the ID5 server without ab_testing object when abTesting is turned off', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) let id5Config = getId5FetchConfig(); - id5Config.params.abTesting = { enabled: false, controlGroupPct: 0.55 } - - let submoduleCallback = id5IdSubmodule.getId(id5Config, undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); + id5Config.params.abTesting = {enabled: false, controlGroupPct: 0.55} - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(requestBody.ab_testing).to.be.undefined; + let submoduleResponse = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.ab_testing).is.undefined; + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }); }); it('should call the ID5 server without ab_testing when when abTesting is not set', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) let id5Config = getId5FetchConfig(); - let submoduleCallback = id5IdSubmodule.getId(id5Config, undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); - - let request = server.requests[0]; - let requestBody = JSON.parse(request.requestBody); - expect(requestBody.ab_testing).to.be.undefined; + let submoduleResponse = callSubmoduleGetId(id5Config, undefined, ID5_STORED_OBJ); - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return xhrServerMock.expectFetchRequest() + .then(fetchRequest => { + let requestBody = JSON.parse(fetchRequest.requestBody); + expect(requestBody.ab_testing).is.undefined; + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }); }); it('should store the privacy object from the ID5 server response', function () { - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); - let request = server.requests[0]; - - let responseObject = utils.deepClone(ID5_JSON_RESPONSE); - responseObject.privacy = { + const privacy = { jurisdiction: 'gdpr', id5_consent: true }; - request.respond(200, responseHeader, JSON.stringify(responseObject)); - expect(getFromLocalStorage(ID5_PRIVACY_STORAGE_NAME)).to.be.eq(JSON.stringify(responseObject.privacy)); - coreStorage.removeDataFromLocalStorage(ID5_PRIVACY_STORAGE_NAME); + + return xhrServerMock.expectFetchRequest() + .then(request => { + let responseObject = utils.deepClone(ID5_JSON_RESPONSE); + responseObject.privacy = privacy; + request.respond(200, responseHeader, JSON.stringify(responseObject)); + return submoduleResponse + }) + .then(() => { + expect(getFromLocalStorage(ID5_PRIVACY_STORAGE_NAME)).is.eq(JSON.stringify(privacy)); + coreStorage.removeDataFromLocalStorage(ID5_PRIVACY_STORAGE_NAME); + }) }); it('should not store a privacy object if not part of ID5 server response', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) coreStorage.removeDataFromLocalStorage(ID5_PRIVACY_STORAGE_NAME); - let submoduleCallback = id5IdSubmodule.getId(getId5FetchConfig(), undefined, ID5_STORED_OBJ).callback; - submoduleCallback(callbackSpy); - - let request = server.requests[0]; - - request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - expect(getFromLocalStorage(ID5_PRIVACY_STORAGE_NAME)).to.be.null; + let submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), undefined, ID5_STORED_OBJ); + + return xhrServerMock.expectFetchRequest() + .then(request => { + let responseObject = utils.deepClone(ID5_JSON_RESPONSE); + responseObject.privacy = undefined; + request.respond(200, responseHeader, JSON.stringify(responseObject)); + return submoduleResponse + }) + .then(() => { + expect(getFromLocalStorage(ID5_PRIVACY_STORAGE_NAME)).is.null; + }); }); describe('when legacy cookies are set', () => { @@ -327,11 +674,11 @@ describe('ID5 ID System', function() { }) }); - describe('Request Bids Hook', function() { + describe('Request Bids Hook', function () { let adUnits; let sandbox; - beforeEach(function() { + beforeEach(function () { sandbox = sinon.sandbox.create(); mockGdprConsent(sandbox); sinon.stub(events, 'getEvents').returns([]); @@ -340,7 +687,7 @@ describe('ID5 ID System', function() { coreStorage.removeDataFromLocalStorage(ID5_NB_STORAGE_NAME); adUnits = [getAdUnitMock()]; }); - afterEach(function() { + afterEach(function () { events.getEvents.restore(); coreStorage.removeDataFromLocalStorage(ID5_STORAGE_NAME); coreStorage.removeDataFromLocalStorage(`${ID5_STORAGE_NAME}_last`); @@ -359,8 +706,8 @@ describe('ID5 ID System', function() { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property(`userId.${ID5_EIDS_NAME}`); - expect(bid.userId.id5id.uid).to.equal(ID5_STORED_ID); - expect(bid.userIdAsEids[0]).to.deep.equal({ + expect(bid.userId.id5id.uid).is.equal(ID5_STORED_ID); + expect(bid.userIdAsEids[0]).is.deep.equal({ source: ID5_SOURCE, uids: [{ id: ID5_STORED_ID, @@ -373,7 +720,7 @@ describe('ID5 ID System', function() { }); }); done(); - }, { adUnits }); + }, {adUnits}); }); it('should add config value ID to bids', function (done) { @@ -385,15 +732,15 @@ describe('ID5 ID System', function() { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property(`userId.${ID5_EIDS_NAME}`); - expect(bid.userId.id5id.uid).to.equal(ID5_STORED_ID); - expect(bid.userIdAsEids[0]).to.deep.equal({ + expect(bid.userId.id5id.uid).is.equal(ID5_STORED_ID); + expect(bid.userIdAsEids[0]).is.deep.equal({ source: ID5_SOURCE, - uids: [{ id: ID5_STORED_ID, atype: 1 }] + uids: [{id: ID5_STORED_ID, atype: 1}] }); }); }); done(); - }, { adUnits }); + }, {adUnits}); }); it('should set nb=1 in cache when no stored nb value exists and cached ID', function (done) { @@ -405,7 +752,7 @@ describe('ID5 ID System', function() { config.setConfig(getFetchLocalStorageConfig()); requestBidsHook((adUnitConfig) => { - expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(1); + expect(getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(1); done() }, {adUnits}); }); @@ -419,19 +766,20 @@ describe('ID5 ID System', function() { config.setConfig(getFetchLocalStorageConfig()); requestBidsHook(() => { - expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(2); + expect(getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(2); done() }, {adUnits}); }); it('should call ID5 servers with signature and incremented nb post auction if refresh needed', function () { - storeInLocalStorage(ID5_STORAGE_NAME, JSON.stringify(ID5_STORED_OBJ), 1); + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let initialLocalStorageValue = JSON.stringify(ID5_STORED_OBJ); + storeInLocalStorage(ID5_STORAGE_NAME, initialLocalStorageValue, 1); storeInLocalStorage(`${ID5_STORAGE_NAME}_last`, expDaysStr(-1), 1); - storeNbInCache(ID5_TEST_PARTNER_ID, 1); + storeNbInCache(ID5_TEST_PARTNER_ID, 1); let id5Config = getFetchLocalStorageConfig(); id5Config.userSync.userIds[0].storage.refreshInSeconds = 2; - init(config); setSubmoduleRegistry([id5IdSubmodule]); config.setConfig(id5Config); @@ -441,53 +789,62 @@ describe('ID5 ID System', function() { resolve() }, {adUnits}); }).then(() => { - expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(2); - expect(server.requests).to.be.empty; + expect(xhrServerMock.hasReceivedAnyRequest()).is.false; events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); - return new Promise((resolve) => setTimeout(resolve)) - }).then(() => { - let request = server.requests[0]; + return xhrServerMock.expectFetchRequest() + }).then(request => { let requestBody = JSON.parse(request.requestBody); - expect(request.url).to.contain(ID5_ENDPOINT); - expect(requestBody.s).to.eq(ID5_STORED_SIGNATURE); - expect(requestBody.nbPage).to.eq(2); - - const responseHeader = { 'Content-Type': 'application/json' }; + expect(requestBody.s).is.eq(ID5_STORED_SIGNATURE); + expect(requestBody.nbPage).is.eq(2); + expect(getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(2); + const responseHeader = {'Content-Type': 'application/json'}; request.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); - expect(decodeURIComponent(getFromLocalStorage(ID5_STORAGE_NAME))).to.be.eq(JSON.stringify(ID5_JSON_RESPONSE)); - expect(getNbFromCache(ID5_TEST_PARTNER_ID)).to.be.eq(0); + return new Promise(function (resolve) { + (function waitForCondition() { + if (getFromLocalStorage(ID5_STORAGE_NAME) !== initialLocalStorageValue) return resolve(); + setTimeout(waitForCondition, 30); + })(); + }) + }).then(() => { + expect(decodeURIComponent(getFromLocalStorage(ID5_STORAGE_NAME))).is.eq(JSON.stringify(ID5_JSON_RESPONSE)); + expect(getNbFromCache(ID5_TEST_PARTNER_ID)).is.eq(0); }) }); }); - describe('Decode stored object', function() { - const expectedDecodedObject = { id5id: { uid: ID5_STORED_ID, ext: { linkType: ID5_STORED_LINK_TYPE } } }; + describe('Decode stored object', function () { + const expectedDecodedObject = {id5id: {uid: ID5_STORED_ID, ext: {linkType: ID5_STORED_LINK_TYPE}}}; - it('should properly decode from a stored object', function() { - expect(id5IdSubmodule.decode(ID5_STORED_OBJ, getId5FetchConfig())).to.deep.equal(expectedDecodedObject); + it('should properly decode from a stored object', function () { + expect(id5IdSubmodule.decode(ID5_STORED_OBJ, getId5FetchConfig())).is.deep.equal(expectedDecodedObject); }); - it('should return undefined if passed a string', function() { - expect(id5IdSubmodule.decode('somestring', getId5FetchConfig())).to.eq(undefined); + it('should return undefined if passed a string', function () { + expect(id5IdSubmodule.decode('somestring', getId5FetchConfig())).is.eq(undefined); }); }); - describe('A/B Testing', function() { - const expectedDecodedObjectWithIdAbOff = { id5id: { uid: ID5_STORED_ID, ext: { linkType: ID5_STORED_LINK_TYPE } } }; - const expectedDecodedObjectWithIdAbOn = { id5id: { uid: ID5_STORED_ID, ext: { linkType: ID5_STORED_LINK_TYPE, abTestingControlGroup: false } } }; - const expectedDecodedObjectWithoutIdAbOn = { id5id: { uid: '', ext: { linkType: 0, abTestingControlGroup: true } } }; + describe('A/B Testing', function () { + const expectedDecodedObjectWithIdAbOff = {id5id: {uid: ID5_STORED_ID, ext: {linkType: ID5_STORED_LINK_TYPE}}}; + const expectedDecodedObjectWithIdAbOn = { + id5id: { + uid: ID5_STORED_ID, + ext: {linkType: ID5_STORED_LINK_TYPE, abTestingControlGroup: false} + } + }; + const expectedDecodedObjectWithoutIdAbOn = {id5id: {uid: '', ext: {linkType: 0, abTestingControlGroup: true}}}; let testConfig, storedObject; - beforeEach(function() { + beforeEach(function () { testConfig = getId5FetchConfig(); storedObject = utils.deepClone(ID5_STORED_OBJ); }); - describe('A/B Testing Config is Set', function() { + describe('A/B Testing Config is Set', function () { let randStub; - beforeEach(function() { - randStub = sinon.stub(Math, 'random').callsFake(function() { + beforeEach(function () { + randStub = sinon.stub(Math, 'random').callsFake(function () { return 0.25; }); }); @@ -495,39 +852,39 @@ describe('ID5 ID System', function() { randStub.restore(); }); - describe('Decode', function() { + describe('Decode', function () { let logErrorSpy; - beforeEach(function() { + beforeEach(function () { logErrorSpy = sinon.spy(utils, 'logError'); }); - afterEach(function() { + afterEach(function () { logErrorSpy.restore(); }); it('should not set abTestingControlGroup extension when A/B testing is off', function () { let decoded = id5IdSubmodule.decode(storedObject, testConfig); - expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOff); + expect(decoded).is.deep.equal(expectedDecodedObjectWithIdAbOff); }); it('should set abTestingControlGroup to false when A/B testing is on but in normal group', function () { - storedObject.ab_testing = { result: 'normal' }; + storedObject.ab_testing = {result: 'normal'}; let decoded = id5IdSubmodule.decode(storedObject, testConfig); - expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOn); + expect(decoded).is.deep.equal(expectedDecodedObjectWithIdAbOn); }); it('should not expose ID when everyone is in control group', function () { - storedObject.ab_testing = { result: 'control' }; + storedObject.ab_testing = {result: 'control'}; storedObject.universal_uid = ''; storedObject.link_type = 0; let decoded = id5IdSubmodule.decode(storedObject, testConfig); - expect(decoded).to.deep.equal(expectedDecodedObjectWithoutIdAbOn); + expect(decoded).is.deep.equal(expectedDecodedObjectWithoutIdAbOn); }); it('should log A/B testing errors', function () { - storedObject.ab_testing = { result: 'error' }; + storedObject.ab_testing = {result: 'error'}; let decoded = id5IdSubmodule.decode(storedObject, testConfig); - expect(decoded).to.deep.equal(expectedDecodedObjectWithIdAbOff); + expect(decoded).is.deep.equal(expectedDecodedObjectWithIdAbOff); sinon.assert.calledOnce(logErrorSpy); }); }); From 4a3daec6f1854f1b323140b84e8edb8e93d4b699 Mon Sep 17 00:00:00 2001 From: JacobKlein26 <42449375+JacobKlein26@users.noreply.github.com> Date: Thu, 18 Aug 2022 22:28:54 -0400 Subject: [PATCH 087/246] NextMillenium Bid Adapter: Remove ortb2 referrerInfo (#8868) * remove ortb2, get device/site manually * updated tests * remove fallbacks * no need to craete variable if there is no fallback (return in place) * removed one test case Co-authored-by: Yakov Klein --- modules/nextMillenniumBidAdapter.js | 31 +++++++++++++++---- .../modules/nextMillenniumBidAdapter_spec.js | 9 ++---- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index f873e5b5c29..87d2e53568f 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -26,11 +26,12 @@ export const spec = { _each(validBidRequests, function(bid) { window.nmmRefreshCounts[bid.adUnitCode] = window.nmmRefreshCounts[bid.adUnitCode] || 0; const id = getPlacementId(bid) + let sizes = bid.sizes + if (sizes && !Array.isArray(sizes[0])) sizes = [sizes] + + const site = getSiteObj() + const device = getDeviceObj() - if (bid.sizes && !Array.isArray(bid.sizes[0])) bid.sizes = [bid.sizes] - if (!bid.ortb2) bid.ortb2 = {} - if (!bid.ortb2.device) bid.ortb2.device = {} - bid.ortb2.device.referrer = (getRefererInfo && getRefererInfo().ref) || '' const postBody = { 'id': bid.auctionId, 'ext': { @@ -46,10 +47,11 @@ export const spec = { 'scrollTop': window.pageYOffset || document.documentElement.scrollTop } }, - ...bid.ortb2, + device, + site, 'imp': [{ 'banner': { - 'format': (bid.sizes || []).map(s => { return {w: s[0], h: s[1]} }) + 'format': (sizes || []).map(s => { return {w: s[0], h: s[1]} }) }, 'ext': { 'prebid': { @@ -198,4 +200,21 @@ function getTopWindow(curWindow, nesting = 0) { } } +function getSiteObj() { + const refInfo = (getRefererInfo && getRefererInfo()) || {} + + return { + page: refInfo.page, + ref: refInfo.ref, + domain: refInfo.domain + } +} + +function getDeviceObj() { + return { + w: window.innerWidth || window.document.documentElement.clientWidth || window.document.body.clientWidth || 0, + h: window.innerHeight || window.document.documentElement.clientHeight || window.document.body.clientHeight || 0, + } +} + registerBidder(spec); diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index 40005356fd8..303025888dc 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -104,9 +104,9 @@ describe('nextMillenniumBidAdapterTests', function() { expect(JSON.parse(request[0].data).ext.nextMillennium.refresh_count).to.equal(3); }); - it('Check if ORTB was added', function() { + it('Check if domain was added', function() { const request = spec.buildRequests(bidRequestData) - expect(JSON.parse(request[0].data).site.domain).to.equal('example.com') + expect(JSON.parse(request[0].data).site.domain).to.exist }) it('Check if elOffsets was added', function() { @@ -114,11 +114,6 @@ describe('nextMillenniumBidAdapterTests', function() { expect(JSON.parse(request[0].data).ext.nextMillennium.elOffsets).to.be.an('object') }) - it('Check if refferer was added', function() { - const request = spec.buildRequests(bidRequestData) - expect(JSON.parse(request[0].data).device.referrer).to.exist - }) - it('Check if imp object was added', function() { const request = spec.buildRequests(bidRequestData) expect(JSON.parse(request[0].data).imp).to.be.an('array') From 6f0a824d40d706d044b9b4c0070aed0c04bd5942 Mon Sep 17 00:00:00 2001 From: caseywhitmire <60086994+caseywhitmire@users.noreply.github.com> Date: Fri, 19 Aug 2022 08:26:29 -0700 Subject: [PATCH 088/246] adserver.js : remove unused code (#8855) --- src/adserver.js | 56 ------------------------------------------------- 1 file changed, 56 deletions(-) diff --git a/src/adserver.js b/src/adserver.js index 8d99b29c3ef..db7aaaa1dc8 100644 --- a/src/adserver.js +++ b/src/adserver.js @@ -1,61 +1,5 @@ -import { formatQS } from './utils.js'; -import { targeting } from './targeting.js'; import {hook} from './hook.js'; -// Adserver parent class -const AdServer = function(attr) { - this.name = attr.adserver; - this.code = attr.code; - this.getWinningBidByCode = function() { - return targeting.getWinningBids(this.code)[0]; - }; -}; - -// DFP ad server -// TODO: this seems to be unused? -export function dfpAdserver(options, urlComponents) { - var adserver = new AdServer(options); - adserver.urlComponents = urlComponents; - - var dfpReqParams = { - 'env': 'vp', - 'gdfp_req': '1', - 'impl': 's', - 'unviewed_position_start': '1' - }; - - var dfpParamsWithVariableValue = ['output', 'iu', 'sz', 'url', 'correlator', 'description_url', 'hl']; - - var getCustomParams = function(targeting) { - return encodeURIComponent(formatQS(targeting)); - }; - - adserver.appendQueryParams = function() { - var bid = adserver.getWinningBidByCode(); - if (bid) { - this.urlComponents.search.description_url = encodeURIComponent(bid.vastUrl); - this.urlComponents.search.cust_params = getCustomParams(bid.adserverTargeting); - this.urlComponents.search.correlator = Date.now(); - } - }; - - adserver.verifyAdserverTag = function() { - for (var key in dfpReqParams) { - if (!this.urlComponents.search.hasOwnProperty(key) || this.urlComponents.search[key] !== dfpReqParams[key]) { - return false; - } - } - for (var i in dfpParamsWithVariableValue) { - if (!this.urlComponents.search.hasOwnProperty(dfpParamsWithVariableValue[i])) { - return false; - } - } - return true; - }; - - return adserver; -}; - /** * return the GAM PPID, if available (eid for the userID configured with `userSync.ppidSource`) */ From 3a029f684ee3992233e4de1cfa5b1cfbe19d6667 Mon Sep 17 00:00:00 2001 From: philan15 <37775368+philan15@users.noreply.github.com> Date: Fri, 19 Aug 2022 19:21:46 +0300 Subject: [PATCH 089/246] Displayio Bid Adapter: custom render; fix eids payload (#8847) * Custom render; call pubmatic get user id function is removed * use refererInfo; remove call createEidsArray --- modules/displayioBidAdapter.js | 189 +++++++++--------- test/spec/modules/displayioBidAdapter_spec.js | 68 ++----- 2 files changed, 108 insertions(+), 149 deletions(-) diff --git a/modules/displayioBidAdapter.js b/modules/displayioBidAdapter.js index e039d461fc7..c3c6597dd1b 100644 --- a/modules/displayioBidAdapter.js +++ b/modules/displayioBidAdapter.js @@ -1,16 +1,16 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import {Renderer} from '../src/Renderer.js'; +import {getWindowFromDocument, logWarn} from '../src/utils.js'; -const BIDDER_VERSION = '1.0.0'; +const ADAPTER_VERSION = '1.1.0'; const BIDDER_CODE = 'displayio'; -const GVLID = 999; const BID_TTL = 300; const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; const DEFAULT_CURRENCY = 'USD'; export const spec = { code: BIDDER_CODE, - gvlid: GVLID, supportedMediaTypes: SUPPORTED_AD_TYPES, isBidRequestValid: function(bid) { return !!(bid.params && bid.params.placementId && bid.params.siteId && @@ -20,7 +20,7 @@ export const spec = { return bidRequests.map(bid => { let url = '//' + bid.params.adsSrvDomain + '/srv?method=getPlacement&app=' + bid.params.siteId + '&placement=' + bid.params.placementId; - const data = this._getPayload(bid, bidderRequest); + const data = getPayload(bid, bidderRequest); return { method: 'POST', headers: {'Content-Type': 'application/json;charset=utf-8'}, @@ -42,117 +42,112 @@ export const spec = { height: adData.h, netRevenue: true, ttl: BID_TTL, - creativeId: adData.adId || 0, - currency: DEFAULT_CURRENCY, + creativeId: adData.adId || 1, + currency: adData.cur || DEFAULT_CURRENCY, referrer: data.data.ref, - mediaType: ads[0].ad.subtype, + mediaType: ads[0].ad.subtype === 'videoVast' ? VIDEO : BANNER, ad: adData.markup, - placement: data.placement, + adUnitCode: data.adUnitCode, + renderURL: data.renderURL, adData: adData }; - if (bidResponse.vastUrl === 'videoVast') { - bidResponse.vastUrl = adData.videos[0].url + + if (bidResponse.mediaType === VIDEO) { + bidResponse.vastUrl = adData.videos[0] && adData.videos[0].url + } + + if (bidResponse.renderURL) { + bidResponse.renderer = newRenderer(bidResponse); } bidResponses.push(bidResponse); } return bidResponses; - }, - _getPayload: function (bid, bidderRequest) { - const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection; - const userSession = 'us_web_xxxxxxxxxxxx'.replace(/[x]/g, c => { - let r = Math.random() * 16 | 0; - let v = c === 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); - const { params } = bid; - const { siteId, placementId } = params; - const { refererInfo, uspConsent, gdprConsent } = bidderRequest; - const mediation = {consent: '-1', gdpr: '-1'}; - if (gdprConsent) { - if (gdprConsent.consentString !== undefined) { - mediation.consent = gdprConsent.consentString; - } - if (gdprConsent.gdprApplies !== undefined) { - mediation.gdpr = gdprConsent.gdprApplies ? '1' : '0'; - } + } +}; + +function getPayload (bid, bidderRequest) { + const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection; + const userSession = 'us_web_xxxxxxxxxxxx'.replace(/[x]/g, c => { + let r = Math.random() * 16 | 0; + let v = c === 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + const { params, adUnitCode, bidId } = bid; + const { siteId, placementId, renderURL, pageCategory, keywords } = params; + const { refererInfo, uspConsent, gdprConsent } = bidderRequest; + const mediation = {consent: '-1', gdpr: '-1'}; + if (gdprConsent && 'gdprApplies' in gdprConsent) { + if (gdprConsent.consentString !== undefined) { + mediation.consent = gdprConsent.consentString; } - const payload = { - userSession, + if (gdprConsent.gdprApplies !== undefined) { + mediation.gdpr = gdprConsent.gdprApplies ? '1' : '0'; + } + } + return { + userSession, + data: { + id: bidId, + action: 'getPlacement', + app: siteId, + placement: placementId, + adUnitCode, + renderURL, data: { - id: bid.bidId, - action: 'getPlacement', - app: siteId, - placement: placementId, - data: { - pagecat: params.pageCategory ? params.pageCategory.split(',').map(k => k.trim()) : [], - keywords: params.keywords ? params.keywords.split(',').map(k => k.trim()) : [], - lang_content: document.documentElement.lang, - lang: window.navigator.language, - // TODO: are these the correct refererInfo values? - domain: refererInfo.domain, - page: refererInfo.page, - ref: refererInfo.ref, - userids: _getUserIDs(), - geo: '', - }, - complianceData: { - child: '-1', - us_privacy: uspConsent, - dnt: window.navigator.doNotTrack, - iabConsent: {}, - mediation: { - consent: mediation.consent, - gdpr: mediation.gdpr, - } - }, - integration: 'JS', - omidpn: 'Displayio', - mediationPlatform: 0, - prebidVersion: BIDDER_VERSION, - device: { - w: window.screen.width, - h: window.screen.height, - connection_type: connection ? connection.effectiveType : '', + pagecat: pageCategory ? pageCategory.split(',').map(k => k.trim()) : [], + keywords: keywords ? keywords.split(',').map(k => k.trim()) : [], + lang_content: document.documentElement.lang, + lang: window.navigator.language, + domain: refererInfo.domain, + page: refererInfo.page, + ref: refererInfo.referer, + userids: bid.userIdAsEids || {}, + geo: '', + }, + complianceData: { + child: '-1', + us_privacy: uspConsent, + dnt: window.doNotTrack === '1' || window.navigator.doNotTrack === '1' || false, + iabConsent: {}, + mediation: { + consent: mediation.consent, + gdpr: mediation.gdpr, } + }, + integration: 'JS', + omidpn: 'Displayio', + mediationPlatform: 0, + prebidVersion: ADAPTER_VERSION, + device: { + w: window.screen.width, + h: window.screen.height, + connection_type: connection ? connection.effectiveType : '', } } - if (navigator.permissions) { - navigator.permissions.query({ name: 'geolocation' }) - .then((result) => { - if (result.state === 'granted') { - payload.data.data.geo = _getGeoData(); - } - }); - } - return payload } -}; +} + +function newRenderer(bid) { + const renderer = Renderer.install({ + id: bid.requestId, + url: bid.renderURL, + adUnitCode: bid.adUnitCode + }); -function _getUserIDs () { - let ids = {}; try { - ids = window.owpbjs.getUserIdsAsEids(); - } catch (e) {} - return ids; + renderer.setRender(webisRender); + } catch (err) { + logWarn('Prebid Error calling setRender on renderer', err); + } + + return renderer; } -async function _getGeoData () { - let geoData = null; - const getCurrentPosition = () => { - return new Promise((resolve, reject) => - navigator.geolocation.getCurrentPosition(resolve, reject) - ); - } - try { - const position = await getCurrentPosition(); - let {latitude, longitude, accuracy} = position.coords; - geoData = { - 'lat': latitude, - 'lng': longitude, - 'precision': accuracy - }; - } catch (e) {} - return geoData +function webisRender(bid, doc) { + bid.renderer.push(() => { + const win = getWindowFromDocument(doc) || window; + win.webis.init(bid.adData, bid.adUnitCode, bid.params); + }) } registerBidder(spec); diff --git a/test/spec/modules/displayioBidAdapter_spec.js b/test/spec/modules/displayioBidAdapter_spec.js index dfeb67fb467..56b8b85384b 100644 --- a/test/spec/modules/displayioBidAdapter_spec.js +++ b/test/spec/modules/displayioBidAdapter_spec.js @@ -1,5 +1,5 @@ -import { expect } from 'chai' import {spec} from 'modules/displayioBidAdapter.js' +import {BANNER} from '/src/mediaTypes' describe('Displayio adapter', function () { const BIDDER = 'displayio' @@ -12,10 +12,7 @@ describe('Displayio adapter', function () { mediaTypes: { banner: { sizes: [[320, 480]] - }, - video: { - sizes: [[360, 640]] - }, + } }, params: { siteId: 1, @@ -128,53 +125,6 @@ describe('Displayio adapter', function () { }) }) - describe('_getPayload', function () { - const payload = spec._getPayload(bidRequests[0], bidderRequest) - it('should not be empty', function() { - expect(payload).to.not.be.empty - }) - - it('should have userSession', function() { - expect(payload.userSession).to.be.a('string') - }) - - it('should have data object', function() { - expect(payload.data).to.be.a('object') - }) - - it('should have complianceData object', function() { - expect(payload.data.complianceData).to.be.a('object') - }) - - it('should have device object', function() { - expect(payload.data.device).to.be.a('object') - }) - - it('should have omidpn', function() { - expect(payload.data.omidpn).to.be.a('string') - }) - - it('should have integration', function() { - expect(payload.data.integration).to.be.a('string') - }) - - it('should have bidId', function() { - expect(payload.data.id).to.not.be.empty - }) - - it('should have action getPlacement', function() { - expect(payload.data.action).to.be.equal('getPlacement') - }) - - it('should have app parameter', function() { - expect(payload.data.app).to.be.a('number') - }) - - it('should have placement parameter', function() { - expect(payload.data.placement).to.be.a('number') - }) - }) - describe('interpretResponse', function () { const response = { body: { @@ -199,6 +149,8 @@ describe('Displayio adapter', function () { data: { data: { id: 'id_001', + adUnitCode: 'test-div', + renderURL: 'testprebid.com/render.js', data: { ref: 'testprebid.com' } @@ -235,5 +187,17 @@ describe('Displayio adapter', function () { it('should have ad', function() { expect(ir.ad).to.be.a('string') }) + + it('should have mediaType', function() { + expect(ir.mediaType).to.be.equal(BANNER) + }) + + it('should have adUnitCode', function() { + expect(ir.adUnitCode).to.be.a('string') + }) + + it('should have renderURL', function() { + expect(ir.renderURL).to.be.a('string') + }) }) }) From 7e5548e4de15edb3b554fcd6c8a19a26c0bd36e2 Mon Sep 17 00:00:00 2001 From: Gena Date: Fri, 19 Aug 2022 21:46:50 +0200 Subject: [PATCH 090/246] VidCrunch LLC bidder (#8872) --- modules/adtelligentBidAdapter.js | 2 ++ test/spec/modules/adtelligentBidAdapter_spec.js | 1 + 2 files changed, 3 insertions(+) diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index 2ee5b0f72a3..3dad2e98bcf 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -23,6 +23,7 @@ const HOST_GETTERS = { janet: () => 'ghb.bidder.jmgads.com', pgam: () => 'ghb.pgamssp.com', ocm: () => 'ghb.cenarius.orangeclickmedia.com', + vidcrunchllc: () => 'ghb.platform.vidcrunch.com', } const getUri = function (bidderCode) { let bidderWithoutSuffix = bidderCode.split('_')[0]; @@ -43,6 +44,7 @@ export const spec = { { code: 'navelix', gvlid: 380 }, 'pgam', 'ocm', + { code: 'vidcrunchllc', gvlid: 1145 }, ], supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid: function (bid) { diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js index a9b9724da3a..d0ef69ccf08 100644 --- a/test/spec/modules/adtelligentBidAdapter_spec.js +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -20,6 +20,7 @@ const aliasEP = { janet: 'https://ghb.bidder.jmgads.com/v2/auction/', pgam: 'https://ghb.pgamssp.com/v2/auction/', ocm: 'https://ghb.cenarius.orangeclickmedia.com/v2/auction/', + vidcrunchllc: 'https://ghb.platform.vidcrunch.com/v2/auction/', }; const DEFAULT_ADATPER_REQ = { bidderCode: 'adtelligent' }; From 0b71a3364cf774a87f630157ab78c1a4f0670c18 Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Mon, 22 Aug 2022 17:51:05 +0200 Subject: [PATCH 091/246] bidWatch Analytics Adapter : limit bandwidth usage + refactory (#8774) * bidWatch Analytics Adapter : code refactory * Update bidwatchAnalyticsAdapter_spec.js * Update bidwatchAnalyticsAdapter_spec.js --- modules/bidwatchAnalyticsAdapter.js | 171 ++++++++++++------ .../modules/bidwatchAnalyticsAdapter_spec.js | 40 ++-- 2 files changed, 140 insertions(+), 71 deletions(-) diff --git a/modules/bidwatchAnalyticsAdapter.js b/modules/bidwatchAnalyticsAdapter.js index ef63fee53d5..9983eb58289 100644 --- a/modules/bidwatchAnalyticsAdapter.js +++ b/modules/bidwatchAnalyticsAdapter.js @@ -12,6 +12,7 @@ const { BID_WON, BID_RESPONSE, BID_REQUESTED, + BID_TIMEOUT, } } = CONSTANTS; @@ -20,80 +21,125 @@ let allEvents = {} let auctionEnd = {} let initOptions = {} let endpoint = 'https://default' -let objectToSearchForBidderCode = ['bidderRequests', 'bidsReceived', 'noBids'] +let requestsAttributes = ['adUnitCode', 'auctionId', 'bidder', 'bidderCode', 'bidId', 'cpm', 'creativeId', 'currency', 'width', 'height', 'mediaType', 'netRevenue', 'originalCpm', 'originalCurrency', 'requestId', 'size', 'source', 'status', 'timeToRespond', 'transactionId', 'ttl', 'sizes', 'mediaTypes', 'src', 'params', 'userId', 'labelAny', 'bids']; function getAdapterNameForAlias(aliasName) { return adapterManager.aliasRegistry[aliasName] || aliasName; } -function cleanArgObject(arg, removead) { - if (typeof arg['bidderCode'] == 'string') { arg['originalBidder'] = getAdapterNameForAlias(arg['bidderCode']); } - if (typeof arg['creativeId'] == 'number') { - arg['creativeId'] = arg['creativeId'].toString(); - } - if (removead && typeof arg['ad'] != 'undefined') { - arg['ad'] = 'emptied'; - } - if (typeof arg['gdprConsent'] != 'undefined' && typeof arg['gdprConsent']['vendorData'] != 'undefined') { - arg['gdprConsent']['vendorData'] = 'emptied'; +function filterAttributes(arg, removead) { + let response = {}; + if (typeof arg == 'object') { + if (typeof arg['bidderCode'] == 'string') { + response['originalBidder'] = getAdapterNameForAlias(arg['bidderCode']); + } else if (typeof arg['bidder'] == 'string') { + response['originalBidder'] = getAdapterNameForAlias(arg['bidder']); + } + if (!removead && typeof arg['ad'] != 'undefined') { + response['ad'] = arg['ad']; + } + if (typeof arg['gdprConsent'] != 'undefined') { + response['gdprConsent'] = {}; + if (typeof arg['gdprConsent']['consentString'] != 'undefined') { response['gdprConsent']['consentString'] = arg['gdprConsent']['consentString']; } + } + requestsAttributes.forEach((attr) => { + if (typeof arg[attr] != 'undefined') { response[attr] = arg[attr]; } + }); + if (typeof response['creativeId'] == 'number') { response['creativeId'] = response['creativeId'].toString(); } } - return arg; + return response; } -function cleanArgs(arg, removead) { - Object.keys(arg).forEach(key => { - arg[key] = cleanArgObject(arg[key], removead); +function cleanAuctionEnd(args) { + let response = {}; + let filteredObj; + let objects = ['bidderRequests', 'bidsReceived', 'noBids']; + objects.forEach((attr) => { + if (Array.isArray(args[attr])) { + response[attr] = []; + args[attr].forEach((obj) => { + filteredObj = filterAttributes(obj, true); + if (typeof obj['bids'] == 'object') { + filteredObj['bids'] = []; + obj['bids'].forEach((bid) => { + filteredObj['bids'].push(filterAttributes(bid, true)); + }); + } + response[attr].push(filteredObj); + }); + } }); - return arg + return response; +} + +function cleanCreatives(args) { + return filterAttributes(args, false); } -function checkBidderCode(args, removead) { - if (typeof args == 'object') { - for (let i = 0; i < objectToSearchForBidderCode.length; i++) { - if (typeof args[objectToSearchForBidderCode[i]] == 'object') { args[objectToSearchForBidderCode[i]] = cleanArgs(args[objectToSearchForBidderCode[i]], removead) } +function enhanceMediaType(arg) { + saveEvents['bidRequested'].forEach((bidRequested) => { + if (bidRequested['auctionId'] == arg['auctionId'] && Array.isArray(bidRequested['bids'])) { + bidRequested['bids'].forEach((bid) => { + if (bid['transactionId'] == arg['transactionId'] && bid['bidId'] == arg['requestId']) { arg['mediaTypes'] = bid['mediaTypes']; } + }); } - } - if (typeof args['bidderCode'] == 'string') { args['originalBidder'] = getAdapterNameForAlias(args['bidderCode']); } else if (typeof args['bidder'] == 'string') { args['originalBidder'] = getAdapterNameForAlias(args['bidder']); } - if (typeof args['creativeId'] == 'number') { args['creativeId'] = args['creativeId'].toString(); } + }); + return arg; +} - return args +function addBidResponse(args) { + let eventType = BID_RESPONSE; + let argsCleaned = cleanCreatives(JSON.parse(JSON.stringify(args))); ; + if (allEvents[eventType] == undefined) { allEvents[eventType] = [] } + allEvents[eventType].push(argsCleaned); } -function addEvent(eventType, args) { - let argsCleaned; - if (eventType && args) { - if (allEvents[eventType] == undefined) { allEvents[eventType] = [] } - if (saveEvents[eventType] == undefined) { saveEvents[eventType] = [] } - argsCleaned = checkBidderCode(JSON.parse(JSON.stringify(args)), false); - allEvents[eventType].push(argsCleaned); - saveEvents[eventType].push(argsCleaned); - argsCleaned = checkBidderCode(JSON.parse(JSON.stringify(args)), true); - if (['auctionend', 'bidtimeout'].includes(eventType.toLowerCase())) { - if (auctionEnd[eventType] == undefined) { auctionEnd[eventType] = [] } - auctionEnd[eventType].push(argsCleaned); - } - } +function addBidRequested(args) { + let eventType = BID_REQUESTED; + let argsCleaned = filterAttributes(args, true); + if (saveEvents[eventType] == undefined) { saveEvents[eventType] = [] } + saveEvents[eventType].push(argsCleaned); +} + +function addTimeout(args) { + let eventType = BID_TIMEOUT; + if (saveEvents[eventType] == undefined) { saveEvents[eventType] = [] } + saveEvents[eventType].push(args); + let argsCleaned = []; + let argsDereferenced = JSON.parse(JSON.stringify(args)); + argsDereferenced.forEach((attr) => { + argsCleaned.push(filterAttributes(JSON.parse(JSON.stringify(attr)), false)); + }); + if (auctionEnd[eventType] == undefined) { auctionEnd[eventType] = [] } + auctionEnd[eventType].push(argsCleaned); +} + +function addAuctionEnd(args) { + let eventType = AUCTION_END; + if (saveEvents[eventType] == undefined) { saveEvents[eventType] = [] } + saveEvents[eventType].push(args); + let argsCleaned = cleanAuctionEnd(JSON.parse(JSON.stringify(args))); + if (auctionEnd[eventType] == undefined) { auctionEnd[eventType] = [] } + auctionEnd[eventType].push(argsCleaned); } function handleBidWon(args) { - args = cleanArgObject(JSON.parse(JSON.stringify(args)), true); + args = enhanceMediaType(filterAttributes(JSON.parse(JSON.stringify(args)), true)); let increment = args['cpm']; if (typeof saveEvents['auctionEnd'] == 'object') { - for (let i = 0; i < saveEvents['auctionEnd'].length; i++) { - let tmpAuction = saveEvents['auctionEnd'][i]; - if (tmpAuction['auctionId'] == args['auctionId'] && typeof tmpAuction['bidsReceived'] == 'object') { - for (let j = 0; j < tmpAuction['bidsReceived'].length; j++) { - let tmpBid = tmpAuction['bidsReceived'][j]; - if (tmpBid['transactionId'] == args['transactionId'] && tmpBid['adId'] != args['adId']) { - if (args['cpm'] < tmpBid['cpm']) { + saveEvents['auctionEnd'].forEach((auction) => { + if (auction['auctionId'] == args['auctionId'] && typeof auction['bidsReceived'] == 'object') { + auction['bidsReceived'].forEach((bid) => { + if (bid['transactionId'] == args['transactionId'] && bid['adId'] != args['adId']) { + if (args['cpm'] < bid['cpm']) { increment = 0; - } else if (increment > args['cpm'] - tmpBid['cpm']) { - increment = args['cpm'] - tmpBid['cpm']; + } else if (increment > args['cpm'] - bid['cpm']) { + increment = args['cpm'] - bid['cpm']; } } - } + }); } - } + }); } args['cpmIncrement'] = increment; if (typeof saveEvents.bidRequested == 'object' && saveEvents.bidRequested.length > 0 && saveEvents.bidRequested[0].gdprConsent) { args.gdpr = saveEvents.bidRequested[0].gdprConsent; } @@ -101,12 +147,16 @@ function handleBidWon(args) { } function handleAuctionEnd() { - ajax(endpoint + '.bidwatch.io/analytics/auctions', null, JSON.stringify(auctionEnd), {method: 'POST', withCredentials: true}); - auctionEnd = {} - if (typeof allEvents['bidResponse'] != 'undefined') { - for (let i = 0; i < allEvents['bidResponse'].length; i++) { ajax(endpoint + '.bidwatch.io/analytics/creatives', null, JSON.stringify(allEvents['bidResponse'][i]), {method: 'POST', withCredentials: true}); } - } - allEvents = {} + ajax(endpoint + '.bidwatch.io/analytics/auctions', function (data) { + let list = JSON.parse(data); + if (Array.isArray(list) && typeof allEvents['bidResponse'] != 'undefined') { + allEvents['bidResponse'].forEach((bidResponse) => { + if (list.includes(bidResponse['originalBidder'] + '_' + bidResponse['creativeId'])) { ajax(endpoint + '.bidwatch.io/analytics/creatives', null, JSON.stringify(bidResponse), {method: 'POST', withCredentials: true}); } + }); + } + allEvents = {}; + }, JSON.stringify(auctionEnd), {method: 'POST', withCredentials: true}); + auctionEnd = {}; } let bidwatchAnalytics = Object.assign(adapter({url, analyticsType}), { @@ -116,17 +166,20 @@ let bidwatchAnalytics = Object.assign(adapter({url, analyticsType}), { }) { switch (eventType) { case AUCTION_END: - addEvent(eventType, args); + addAuctionEnd(args); handleAuctionEnd(); break; case BID_WON: handleBidWon(args); break; case BID_RESPONSE: - addEvent(eventType, args); + addBidResponse(args); break; case BID_REQUESTED: - addEvent(eventType, args); + addBidRequested(args); + break; + case BID_TIMEOUT: + addTimeout(args); break; } }}); diff --git a/test/spec/modules/bidwatchAnalyticsAdapter_spec.js b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js index f827f068bb3..dddb29dbfd9 100644 --- a/test/spec/modules/bidwatchAnalyticsAdapter_spec.js +++ b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js @@ -260,15 +260,15 @@ describe('BidWatch Analytics', function () { describe('main test flow', function () { beforeEach(function () { sinon.stub(events, 'getEvents').returns([]); + sinon.spy(bidwatchAnalytics, 'track'); }); - afterEach(function () { events.getEvents.restore(); + bidwatchAnalytics.disableAnalytics(); + bidwatchAnalytics.track.restore(); }); - it('should catch events of interest', function () { - sinon.spy(bidwatchAnalytics, 'track'); - + it('test auctionEnd', function () { adapterManager.registerAnalyticsAdapter({ code: 'bidwatch', adapter: bidwatchAnalytics @@ -280,6 +280,9 @@ describe('BidWatch Analytics', function () { domain: 'test' } }); + + events.emit(constants.EVENTS.BID_REQUESTED, auctionEnd['bidderRequests'][0]); + events.emit(constants.EVENTS.BID_RESPONSE, auctionEnd['bidsReceived'][0]); events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeout); events.emit(constants.EVENTS.AUCTION_END, auctionEnd); expect(server.requests.length).to.equal(1); @@ -287,18 +290,31 @@ describe('BidWatch Analytics', function () { expect(message).to.have.property('auctionEnd').exist; expect(message.auctionEnd).to.have.lengthOf(1); expect(message.auctionEnd[0]).to.have.property('bidsReceived').and.to.have.lengthOf(1); - expect(message.auctionEnd[0].bidsReceived[0]).to.have.property('ad'); - expect(message.auctionEnd[0].bidsReceived[0].ad).to.equal('emptied'); + expect(message.auctionEnd[0].bidsReceived[0]).not.to.have.property('ad'); expect(message.auctionEnd[0]).to.have.property('bidderRequests').and.to.have.lengthOf(1); expect(message.auctionEnd[0].bidderRequests[0]).to.have.property('gdprConsent'); - expect(message.auctionEnd[0].bidderRequests[0].gdprConsent).to.have.property('vendorData'); - expect(message.auctionEnd[0].bidderRequests[0].gdprConsent.vendorData).to.equal('emptied'); + expect(message.auctionEnd[0].bidderRequests[0].gdprConsent).not.to.have.property('vendorData'); + sinon.assert.callCount(bidwatchAnalytics.track, 4); + }); + + it('test bidWon', function() { + adapterManager.registerAnalyticsAdapter({ + code: 'bidwatch', + adapter: bidwatchAnalytics + }); + + adapterManager.enableAnalytics({ + provider: 'bidwatch', + options: { + domain: 'test' + } + }); events.emit(constants.EVENTS.BID_WON, bidWon); - expect(server.requests.length).to.equal(2); - message = JSON.parse(server.requests[1].requestBody); - expect(message).to.have.property('ad').and.to.equal('emptied'); + expect(server.requests.length).to.equal(1); + let message = JSON.parse(server.requests[0].requestBody); + expect(message).not.to.have.property('ad'); expect(message).to.have.property('cpmIncrement').and.to.equal(27.4276); - sinon.assert.callCount(bidwatchAnalytics.track, 3); + sinon.assert.callCount(bidwatchAnalytics.track, 1); }); }); }); From a61f4bfdc31c6ade12c19489b6edbe2126711277 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Aug 2022 13:37:11 -0400 Subject: [PATCH 092/246] Bump tibdex/github-app-token from 1.3.0 to 1.6 (#8878) Bumps [tibdex/github-app-token](https://github.com/tibdex/github-app-token) from 1.3.0 to 1.6. - [Release notes](https://github.com/tibdex/github-app-token/releases) - [Commits](https://github.com/tibdex/github-app-token/compare/36464acb844fc53b9b8b2401da68844f6b05ebb0...f717b5ecd4534d3c4df4ce9b5c1c2214f0f7cd06) --- updated-dependencies: - dependency-name: tibdex/github-app-token dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/issue_tracker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue_tracker.yml b/.github/workflows/issue_tracker.yml index fa33ffe5c53..05d08b2b0d7 100644 --- a/.github/workflows/issue_tracker.yml +++ b/.github/workflows/issue_tracker.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Generate token id: generate_token - uses: tibdex/github-app-token@36464acb844fc53b9b8b2401da68844f6b05ebb0 + uses: tibdex/github-app-token@f717b5ecd4534d3c4df4ce9b5c1c2214f0f7cd06 with: app_id: ${{ secrets.ISSUE_APP_ID }} private_key: ${{ secrets.ISSUE_APP_PEM }} From 5be582f2e35650e0bf4dbe212bde9e936edfe75c Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Mon, 22 Aug 2022 20:44:12 +0300 Subject: [PATCH 093/246] Adkernel Bid Adapter: add impression-level FPD support (#8880) --- modules/adkernelBidAdapter.js | 40 ++++++++++++++------ test/spec/modules/adkernelBidAdapter_spec.js | 13 ++++++- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 78f00784462..ff7d3be6ebf 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -14,11 +14,12 @@ import { isPlainObject, isStr, mergeDeep, - parseGPTSingleSizeArrayToRtbSize + parseGPTSingleSizeArrayToRtbSize, + getDefinedParams } from '../src/utils.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {find, includes} from '../src/polyfill.js'; +import {find} from '../src/polyfill.js'; import {config} from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; @@ -28,10 +29,11 @@ import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; * * Please contact prebid@adkernel.com and we'll add your adapter as an alias. */ - -const VIDEO_TARGETING = Object.freeze(['mimes', 'minduration', 'maxduration', 'protocols', - 'startdelay', 'linearity', 'boxingallowed', 'playbackmethod', 'delivery', - 'pos', 'api', 'ext']); +const VIDEO_PARAMS = ['pos', 'context', 'placement', 'api', 'mimes', 'protocols', 'playbackmethod', 'minduration', 'maxduration', + 'startdelay', 'linearity', 'skip', 'skipmin', 'skipafter', 'minbitrate', 'maxbitrate', 'delivery', 'playbackend', 'boxingallowed']; +const VIDEO_FPD = ['battr', 'pos']; +const NATIVE_FPD = ['battr', 'api']; +const BANNER_FPD = ['btype', 'battr', 'pos', 'api']; const VERSION = '1.6'; const SYNC_IFRAME = 1; const SYNC_IMAGE = 2; @@ -275,18 +277,18 @@ function buildImp(bidRequest, secure) { format: sizes.map(wh => parseGPTSingleSizeArrayToRtbSize(wh)), topframe: 0 }; + populateImpFpd(imp.banner, bidRequest, BANNER_FPD); mediaType = BANNER; } else if (deepAccess(bidRequest, 'mediaTypes.video')) { let video = deepAccess(bidRequest, 'mediaTypes.video'); - imp.video = {}; + imp.video = getDefinedParams(video, VIDEO_PARAMS); + populateImpFpd(imp.video, bidRequest, VIDEO_FPD); if (video.playerSize) { sizes = video.playerSize[0]; imp.video = Object.assign(imp.video, parseGPTSingleSizeArrayToRtbSize(sizes) || {}); - } - if (bidRequest.params.video) { - Object.keys(bidRequest.params.video) - .filter(key => includes(VIDEO_TARGETING, key)) - .forEach(key => imp.video[key] = bidRequest.params.video[key]); + } else if (video.w && video.h) { + imp.video.w = video.w; + imp.video.h = video.h; } mediaType = VIDEO; } else if (deepAccess(bidRequest, 'mediaTypes.native')) { @@ -295,6 +297,7 @@ function buildImp(bidRequest, secure) { ver: '1.1', request: JSON.stringify(nativeRequest) }; + populateImpFpd(imp.native, bidRequest, NATIVE_FPD); mediaType = NATIVE; } else { throw new Error('Unsupported bid received'); @@ -338,6 +341,19 @@ function buildNativeRequest(nativeReq) { return request; } +/** + * Populate impression-level FPD from bid request + * @param target {Object} + * @param bidRequest {BidRequest} + * @param props {String[]} + */ +function populateImpFpd(target, bidRequest, props) { + if (bidRequest.ortb2Imp === undefined) { + return; + } + Object.assign(target, getDefinedParams(bidRequest.ortb2Imp, props)); +} + /** * Builds image asset request */ diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index 4b0eebdf519..45498d2734a 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -17,6 +17,9 @@ describe('Adkernel adapter', function () { banner: { sizes: [[300, 250], [300, 200]] } + }, + ortb2Imp: { + battr: [6, 7, 9] } }, bid2_zone2 = { bidder: 'adkernel', @@ -95,12 +98,12 @@ describe('Adkernel adapter', function () { params: { zoneId: 1, host: 'rtb.adkernel.com', - video: {api: [1, 2]} }, mediaTypes: { video: { context: 'instream', - playerSize: [[640, 480]] + playerSize: [[640, 480]], + api: [1, 2] } }, adUnitCode: 'ad-unit-1' @@ -293,6 +296,7 @@ describe('Adkernel adapter', function () { describe('banner request building', function () { let bidRequest, bidRequests, _; + before(function () { [_, bidRequests] = buildRequest([bid1_zone1]); bidRequest = bidRequests[0]; @@ -337,6 +341,11 @@ describe('Adkernel adapter', function () { expect(bidRequest.device).to.have.property('dnt', 1); }); + it('should copy FPD to imp.banner', function() { + expect(bidRequest.imp[0].banner).to.have.property('battr'); + expect(bidRequest.imp[0].banner.battr).to.be.eql([6, 7, 9]); + }); + it('shouldn\'t contain gdpr nor ccpa information for default request', function () { let [_, bidRequests] = buildRequest([bid1_zone1]); expect(bidRequests[0]).to.not.have.property('regs'); From 47c8ead2fe1d51282000dfe1df55b1dfa92aa53b Mon Sep 17 00:00:00 2001 From: Jason Piros Date: Mon, 22 Aug 2022 15:21:33 -0700 Subject: [PATCH 094/246] consumableBidAdapter: remove impressionUrl (#8883) --- modules/consumableBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/consumableBidAdapter.js b/modules/consumableBidAdapter.js index 6d502b24e81..87cb6052630 100644 --- a/modules/consumableBidAdapter.js +++ b/modules/consumableBidAdapter.js @@ -1,4 +1,4 @@ -import { logWarn, createTrackPixelHtml, deepAccess, isArray, deepSetValue } from '../src/utils.js'; +import { logWarn, deepAccess, isArray, deepSetValue } from '../src/utils.js'; import {config} from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; @@ -255,7 +255,7 @@ function getSize(sizes) { function retrieveAd(decision, unitId, unitName) { let ad; if (decision.contents && decision.contents[0]) { - ad = decision.contents[0].body + createTrackPixelHtml(decision.impressionUrl); + ad = decision.contents[0].body; } if (decision.vastXml) { ad = decision.vastXml; From 73c13cde704ef396a152ed9dcb8596779473107d Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 23 Aug 2022 09:03:58 -0400 Subject: [PATCH 095/246] Prebid Core: Add ttl buffer to videoCache.js (#8861) * Update videoCache.js * Update videoCache.js * Update videoCache_spec.js * Update videoCache_spec.js * master into ttl-buffer (#8869) * Update Sonobi adapter with GVLID (#8860) * dgkeyword RTD provider: fix tests causing ID5 test failures (#8862) Co-authored-by: Mike Miller Co-authored-by: Demetrio Girardi * Revert "master into ttl-buffer (#8869)" (#8879) This reverts commit b253980d38b6c801bc359066258b5988ce938865. * Name the constant Co-authored-by: Mike Miller Co-authored-by: Demetrio Girardi --- src/videoCache.js | 10 ++++++++-- test/spec/videoCache_spec.js | 14 +++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/videoCache.js b/src/videoCache.js index 219bca34726..f69a20f0139 100644 --- a/src/videoCache.js +++ b/src/videoCache.js @@ -13,6 +13,12 @@ import { ajax } from './ajax.js'; import { config } from './config.js'; import {auctionManager} from './auctionManager.js'; +/** + * Might be useful to be configurable in the future + * Depending on publisher needs + */ +const ttlBufferInSeconds = 15; + /** * @typedef {object} CacheableUrlBid * @property {string} vastUrl A URL which loads some valid VAST XML. @@ -63,11 +69,11 @@ function wrapURI(uri, impUrl) { function toStorageRequest(bid, {index = auctionManager.index} = {}) { const vastValue = bid.vastXml ? bid.vastXml : wrapURI(bid.vastUrl, bid.vastImpUrl); const auction = index.getAuction(bid); - + const ttlWithBuffer = Number(bid.ttl) + ttlBufferInSeconds; let payload = { type: 'xml', value: vastValue, - ttlseconds: Number(bid.ttl) + ttlseconds: ttlWithBuffer }; if (config.getConfig('cache.vasttrack')) { diff --git a/test/spec/videoCache_spec.js b/test/spec/videoCache_spec.js index fdb4103baed..5885dfb7cdf 100644 --- a/test/spec/videoCache_spec.js +++ b/test/spec/videoCache_spec.js @@ -155,12 +155,12 @@ describe('The video cache', function () { puts: [{ type: 'xml', value: vastXml1, - ttlseconds: 25, + ttlseconds: 40, key: customKey1 }, { type: 'xml', value: vastXml2, - ttlseconds: 25, + ttlseconds: 40, key: customKey2 }] }; @@ -205,7 +205,7 @@ describe('The video cache', function () { puts: [{ type: 'xml', value: vastXml1, - ttlseconds: 25, + ttlseconds: 40, key: customKey1, bidid: '12345abc', aid: '1234-56789-abcde', @@ -213,7 +213,7 @@ describe('The video cache', function () { }, { type: 'xml', value: vastXml2, - ttlseconds: 25, + ttlseconds: 40, key: customKey2, bidid: 'cba54321', aid: '1234-56789-abcde', @@ -276,7 +276,7 @@ describe('The video cache', function () { puts: [{ type: 'xml', value: vastXml1, - ttlseconds: 25, + ttlseconds: 40, key: customKey1, bidid: '12345abc', bidder: 'appnexus', @@ -285,7 +285,7 @@ describe('The video cache', function () { }, { type: 'xml', value: vastXml2, - ttlseconds: 25, + ttlseconds: 40, key: customKey2, bidid: 'cba54321', bidder: 'rubicon', @@ -309,7 +309,7 @@ describe('The video cache', function () { puts: [{ type: 'xml', value: expectedValue, - ttlseconds: 25 + ttlseconds: 40 }], }); } From 8d88902ff6f1479ac991d78d611e482f49c9c81c Mon Sep 17 00:00:00 2001 From: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Date: Tue, 23 Aug 2022 21:06:16 +0530 Subject: [PATCH 096/246] Added support to log extra bids from same bidder (#8886) Co-authored-by: pm-azhar-mulla --- modules/pubmaticAnalyticsAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index 38bcc4521be..c8dc7cef15d 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -382,7 +382,7 @@ function bidResponseHandler(args) { return; } - if (bid.bidder && args.bidderCode && bid.bidder !== args.bidderCode) { + if ((bid.bidder && args.bidderCode && bid.bidder !== args.bidderCode) || (bid.bidder === args.bidderCode && bid.status === SUCCESS)) { bid = copyRequiredBidDetails(args); cache.auctions[args.auctionId].adUnitCodes[args.adUnitCode].bids[args.requestId].push(bid); } From 07232521ed1d8185adc99a25a4eb4c266281d756 Mon Sep 17 00:00:00 2001 From: Ignat Khaylov Date: Wed, 24 Aug 2022 11:23:30 +0300 Subject: [PATCH 097/246] BetweenBidAdapter: default value for the cur parameter (#8870) * BetweenBidAdapter: default value for the cur parameter * fix linting Co-authored-by: Ignat Khaylov Co-authored-by: Chris Huie --- modules/betweenBidAdapter.js | 6 +++--- test/spec/modules/betweenBidAdapter_spec.js | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/modules/betweenBidAdapter.js b/modules/betweenBidAdapter.js index 2ca829b796b..ea28420481d 100644 --- a/modules/betweenBidAdapter.js +++ b/modules/betweenBidAdapter.js @@ -59,9 +59,9 @@ export const spec = { if (i.params.itu !== undefined) { params.itu = i.params.itu; } - if (i.params.cur !== undefined) { - params.cur = i.params.cur; - } + + params.cur = i.params.cur || 'USD'; + if (i.params.subid !== undefined) { params.subid = i.params.subid; } diff --git a/test/spec/modules/betweenBidAdapter_spec.js b/test/spec/modules/betweenBidAdapter_spec.js index 3baa92e35d5..a4b89ab1b65 100644 --- a/test/spec/modules/betweenBidAdapter_spec.js +++ b/test/spec/modules/betweenBidAdapter_spec.js @@ -85,6 +85,23 @@ describe('betweenBidAdapterTests', function () { expect(req_data.cur).to.equal('THX'); }); + it('validate default cur USD', function() { + let bidRequestData = [{ + bidId: 'bid1234', + bidder: 'between', + params: { + w: 240, + h: 400, + s: 1112 + }, + sizes: [[240, 400]] + }]; + + let request = spec.buildRequests(bidRequestData); + let req_data = JSON.parse(request.data)[0].data; + + expect(req_data.cur).to.equal('USD'); + }); it('validate subid param', function() { let bidRequestData = [{ bidId: 'bid1234', From 8a82e0935d7797513537e3f081a6f7afa0de0bdb Mon Sep 17 00:00:00 2001 From: BaronJHYu <254878848@qq.com> Date: Wed, 24 Aug 2022 22:17:01 +0800 Subject: [PATCH 098/246] Mediago Bid Adapter: initial adapter release (#8856) * Mediago Bid Adapter:new adapter * remove console * change spec file to fix CircleCI * change spec file to fix CircleCI * change spec file * Update mediagoBidAdapter.js * Update mediagoBidAdapter.js * rerun CurcleCi Co-authored-by: BaronYu --- modules/mediagoBidAdapter.js | 462 ++++++++++++++++++++ modules/mediagoBidAdapter.md | 34 ++ test/spec/modules/mediagoBidAdapter_spec.js | 97 ++++ 3 files changed, 593 insertions(+) create mode 100644 modules/mediagoBidAdapter.js create mode 100644 modules/mediagoBidAdapter.md create mode 100644 test/spec/modules/mediagoBidAdapter_spec.js diff --git a/modules/mediagoBidAdapter.js b/modules/mediagoBidAdapter.js new file mode 100644 index 00000000000..28ca4e0bc50 --- /dev/null +++ b/modules/mediagoBidAdapter.js @@ -0,0 +1,462 @@ +/** + * gulp serve --modules=mediagoBidAdapter,pubCommonId --nolint --notest + */ + +import * as utils from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +// import { config } from '../src/config.js'; +// import { isPubcidEnabled } from './pubCommonId.js'; + +const BIDDER_CODE = 'mediago'; +// const PROTOCOL = window.document.location.protocol; +const ENDPOINT_URL = + // ((PROTOCOL === 'https:') ? 'https' : 'http') + + 'https://rtb-us.mediago.io/api/bid?tn='; +const TIME_TO_LIVE = 500; +// const ENDPOINT_URL = '/api/bid?tn='; +const storage = getStorageManager(); +let globals = {}; +let itemMaps = {}; + +/** + * 获取随机id + * @param {number} a random number from 0 to 15 + * @return {string} random number or random string + */ +// function getRandomId( +// a // placeholder +// ) { +// // if the placeholder was passed, return +// // a random number from 0 to 15 +// return a +// ? ( +// a ^ // unless b is 8, +// ((Math.random() * // in which case +// 16) >> // a random number from +// (a / 4)) +// ) // 8 to 11 +// .toString(16) // in hexadecimal +// : ( // or otherwise a concatenated string: +// [1e7] + // 10000000 + +// 1e3 + // -1000 + +// 4e3 + // -4000 + +// 8e3 + // -80000000 + +// 1e11 +// ) // -100000000000, +// .replace( +// // replacing +// /[018]/g, // zeroes, ones, and eights with +// getRandomId // random hex digits +// ); +// } + +/* ----- mguid:start ------ */ +const COOKIE_KEY_MGUID = '__mguid_'; + +/** + * 获取用户id + * @return {string} + */ +const getUserID = () => { + const i = storage.getCookie(COOKIE_KEY_MGUID); + + if (i === null) { + const uuid = utils.generateUUID(); + storage.setCookie(COOKIE_KEY_MGUID, uuid); + return uuid; + } + return i; +}; + +/* ----- mguid:end ------ */ + +/** + * 获取一个对象的某个值,如果没有则返回空字符串 + * @param {Object} obj 对象 + * @param {...string} keys 键名 + * @return {any} + */ +function getProperty(obj, ...keys) { + let o = obj; + + for (let key of keys) { + // console.log(key, o); + if (o && o[key]) { + o = o[key]; + } else { + return ''; + } + } + return o; +} + +/** + * 是不是移动设备或者平板 + * @return {boolean} + */ +function isMobileAndTablet() { + let check = false; + (function (a) { + let reg1 = new RegExp( + [ + '(android|bbd+|meego)', + '.+mobile|avantgo|bada/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)', + '|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone', + '|p(ixi|re)/|plucker|pocket|psp|series(4|6)0|symbian|treo|up.(browser|link)|vodafone|wap', + '|windows ce|xda|xiino|android|ipad|playbook|silk', + ].join(''), + 'i' + ); + let reg2 = new RegExp( + [ + '1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)', + '|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )', + '|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55/|capi|ccwa|cdm-|cell', + '|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)', + '|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene', + '|gf-5|g-mo|go(.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c', + '|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|/)|ibro|idea|ig01|ikom', + '|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |/)|klon|kpt |kwc-|kyo(c|k)', + '|le(no|xi)|lg( g|/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50/|ma(te|ui|xo)|mc(01|21|ca)', + '|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]', + '|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)', + '|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio', + '|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55/|sa(ge|ma|mm|ms', + '|ny|va)|sc(01|h-|oo|p-)|sdk/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al', + '|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)', + '|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(.b|g1|si)|utst|', + 'v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)', + '|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-', + '|your|zeto|zte-', + ].join(''), + 'i' + ); + if (reg1.test(a) || reg2.test(a.substr(0, 4))) { + check = true; + } + })(navigator.userAgent || navigator.vendor || window.opera); + return check; +} + +/** + * 获取底价 + * @param {*} bid + * @param {*} mediaType + * @param {*} sizes + * @returns + */ +// function getBidFloor(bid, mediaType, sizes) { +// var floor; +// var size = sizes.length === 1 ? sizes[0] : "*"; +// if (typeof bid.getFloor === "function") { +// const floorInfo = bid.getFloor({ currency: "USD", mediaType, size }); +// if ( +// typeof floorInfo === "object" && +// floorInfo.currency === "USD" && +// !isNaN(parseFloat(floorInfo.floor)) +// ) { +// floor = parseFloat(floorInfo.floor); +// } +// } +// return floor; +// } +function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return utils.deepAccess(bid, 'params.bidfloor', 0); + } + + try { + const bidFloor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*', + }); + return bidFloor.floor; + } catch (_) { + return 0; + } +} + +/** + * 将尺寸转为RTB识别的尺寸 + * + * @param {Array|Object} requestSizes 配置尺寸 + * @return {Object} + */ +function transformSizes(requestSizes) { + let sizes = []; + let sizeObj = {}; + + if ( + utils.isArray(requestSizes) && + requestSizes.length === 2 && + !utils.isArray(requestSizes[0]) + ) { + sizeObj.width = parseInt(requestSizes[0], 10); + sizeObj.height = parseInt(requestSizes[1], 10); + sizes.push(sizeObj); + } else if (typeof requestSizes === 'object') { + for (let i = 0; i < requestSizes.length; i++) { + let size = requestSizes[i]; + sizeObj = {}; + sizeObj.width = parseInt(size[0], 10); + sizeObj.height = parseInt(size[1], 10); + sizes.push(sizeObj); + } + } + + return sizes; +} + +// 支持的广告尺寸 +const mediagoAdSize = [ + { w: 300, h: 250 }, + { w: 300, h: 600 }, + { w: 728, h: 90 }, + { w: 970, h: 250 }, + { w: 320, h: 50 }, + { w: 160, h: 600 }, + { w: 320, h: 180 }, + { w: 320, h: 100 }, + { w: 336, h: 280 }, +]; + +/** + * 获取广告位配置 + * @param {Array} validBidRequests an an array of bids + * @param {Object} bidderRequest The master bidRequest object + * @return {Object} + */ +function getItems(validBidRequests, bidderRequest) { + let items = []; + items = validBidRequests.map((req, i) => { + let ret = {}; + let mediaTypes = getProperty(req, 'mediaTypes'); + + let sizes = transformSizes(getProperty(req, 'sizes')); + let matchSize; + + // 确认尺寸是否符合我们要求 + for (let size of sizes) { + matchSize = mediagoAdSize.find( + (item) => size.width === item.w && size.height === item.h + ); + if (matchSize) { + break; + } + } + if (!matchSize) { + return {}; + } + + const bidFloor = getBidFloor(req); + // const gpid = + // utils.deepAccess(req, 'ortb2Imp.ext.gpid') || + // utils.deepAccess(req, 'ortb2Imp.ext.data.pbadslot') || + // utils.deepAccess(req, 'params.placementId', 0); + // console.log("wjh getItems:", req, bidFloor, gpid); + + // if (mediaTypes.native) {} + // banner广告类型 + if (mediaTypes.banner) { + let id = '' + (i + 1); + ret = { + id: id, + bidfloor: bidFloor, + banner: { + h: matchSize.h, + w: matchSize.w, + pos: 1, + }, + ext: { + // gpid: gpid, // 加入后无法返回广告 + }, + }; + itemMaps[id] = { + req, + ret, + }; + } + + return ret; + }); + return items; +} + +/** + * 获取rtb请求参数 + * + * @param {Array} validBidRequests an an array of bids + * @param {Object} bidderRequest The master bidRequest object + * @return {Object} + */ +function getParam(validBidRequests, bidderRequest) { + const pubcid = utils.deepAccess(validBidRequests[0], 'crumbs.pubcid'); + // console.log('wjh getParam', validBidRequests, bidderRequest); + let isMobile = isMobileAndTablet() ? 1 : 0; + let isTest = 0; + let auctionId = getProperty(bidderRequest, 'auctionId'); + let items = getItems(validBidRequests, bidderRequest); + + const domain = document.domain; + const location = utils.deepAccess(bidderRequest, 'refererInfo.referer'); + + const timeout = bidderRequest.timeout || 2000; + + if (items && items.length) { + let c = { + id: 'mgprebidjs_' + auctionId, + test: +isTest, + at: 1, + cur: ['USD'], + device: { + connectiontype: 0, + // ip: '64.188.178.115', + js: 1, + // language: "en", + // os: "Microsoft Windows", + // ua: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19043", + os: navigator.platform || '', + ua: navigator.userAgent, + language: /en/.test(navigator.language) ? 'en' : navigator.language, + }, + ext: {}, + user: { + buyeruid: getUserID(), + id: pubcid, + }, + site: { + name: domain, + domain: domain, + page: location, + ref: location, + mobile: isMobile, + cat: [], // todo + publisher: { + // todo + id: domain, + name: domain, + }, + }, + imp: items, + tmax: timeout, + }; + return c; + } else { + return null; + } +} + +export const spec = { + code: BIDDER_CODE, + // aliases: ['ex'], // short code + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function (bid) { + // console.log('mediago', { + // bid + // }); + if (bid.params.token) { + globals['token'] = bid.params.token; + } + return !!bid.params.token; + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {Array} validBidRequests an an array of bids + * @param {Object} bidderRequest The master bidRequest object + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests, bidderRequest) { + let payload = getParam(validBidRequests, bidderRequest); + + const payloadString = JSON.stringify(payload); + return { + method: 'POST', + url: ENDPOINT_URL + globals['token'], + data: payloadString, + }; + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse, bidRequest) { + const bids = getProperty(serverResponse, 'body', 'seatbid', 0, 'bid'); + const cur = getProperty(serverResponse, 'body', 'cur'); + + const bidResponses = []; + for (let bid of bids) { + let impid = getProperty(bid, 'impid'); + if (itemMaps[impid]) { + let bidId = getProperty(itemMaps[impid], 'req', 'bidId'); + const bidResponse = { + requestId: bidId, + cpm: getProperty(bid, 'price'), + width: getProperty(bid, 'w'), + height: getProperty(bid, 'h'), + creativeId: getProperty(bid, 'crid'), + dealId: '', + currency: cur, + netRevenue: true, + ttl: TIME_TO_LIVE, + // referrer: REFERER, + ad: getProperty(bid, 'adm'), + nurl: getProperty(bid, 'nurl'), + // adserverTargeting: { + // granularityMultiplier: 0.1, + // priceGranularity: "pbHg", + // pbMg: "0.01", + // }, + // pbMg: "0.01", + // granularityMultiplier: 0.1, + // priceGranularity: "pbHg", + }; + bidResponses.push(bidResponse); + } + } + + return bidResponses; + }, + + /** + * Register bidder specific code, which will execute if bidder timed out after an auction + * @param {data} Containing timeout specific data + */ + // onTimeout: function (data) { + // // console.log('onTimeout', data); + // // Bidder specifc code + // }, + + /** + * Register bidder specific code, which will execute if a bid from this bidder won the auction + * @param {Bid} The bid that won the auction + */ + onBidWon: function (bid) { + // console.log('onBidWon: ', bid, config.getConfig('priceGranularity')); + // Bidder specific code + if (bid['nurl']) { + utils.triggerPixel(bid['nurl']); + } + }, + + /** + * Register bidder specific code, which will execute when the adserver targeting has been set for a bid from this bidder + * @param {Bid} The bid of which the targeting has been set + */ +// onSetTargeting: function (bid) { +// // console.log('onSetTargeting', bid); +// // Bidder specific code +// }, +}; +registerBidder(spec); diff --git a/modules/mediagoBidAdapter.md b/modules/mediagoBidAdapter.md new file mode 100644 index 00000000000..464ea59f11c --- /dev/null +++ b/modules/mediagoBidAdapter.md @@ -0,0 +1,34 @@ +# Overview + +``` +Module Name: MediaGo Bidder Adapter +Module Type: Bidder Adapter +Maintainer: fangsimin@baidu.com +``` + +# Description + +Module that connects to MediaGo's demand sources + +# Test Parameters + +``` + var adUnits = [ + { + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [ + { + bidder: "mediago", + params: { + token: '' // required, send email to ext_mediago_am@baidu.com to get the corresponding token + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/mediagoBidAdapter_spec.js b/test/spec/modules/mediagoBidAdapter_spec.js new file mode 100644 index 00000000000..e77af544429 --- /dev/null +++ b/test/spec/modules/mediagoBidAdapter_spec.js @@ -0,0 +1,97 @@ +import { expect } from 'chai'; +import { spec } from 'modules/mediagoBidAdapter.js'; + +describe('mediago:BidAdapterTests', function () { + let bidRequestData = { + bidderCode: 'mediago', + auctionId: '7fae02a9-0195-472f-ba94-708d3bc2c0d9', + bidderRequestId: '4fec04e87ad785', + bids: [ + { + bidder: 'mediago', + params: { + token: '85a6b01e41ac36d49744fad726e3655d', + bidfloor: 0.01, + }, + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + adUnitCode: 'regular_iframe', + transactionId: '7b26fdae-96e6-4c35-a18b-218dda11397d', + sizes: [[300, 250]], + bidId: '54d73f19c9d47a', // todo + bidderRequestId: '4fec04e87ad785', // todo + auctionId: '883a346a-6d62-4adb-a600-0f3a869061d1', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0, + }, + ], + }; + let request = []; + + it('mediago:validate_pub_params', function () { + expect( + spec.isBidRequestValid({ + bidder: 'mediago', + params: { + token: ['85a6b01e41ac36d49744fad726e3655d'], + }, + }) + ).to.equal(true); + }); + + it('mediago:validate_generated_params', function () { + request = spec.buildRequests(bidRequestData.bids, bidRequestData); + let req_data = JSON.parse(request.data); + expect(req_data.imp).to.have.lengthOf(1); + }); + + it('mediago:validate_response_params', function () { + let adm = ""; + let temp = '%3Cscr'; + temp += 'ipt%3E'; + temp += '!function()%7B%22use%20strict%22%3Bfunction%20f(t)%7Breturn(f%3D%22function%22%3D%3Dtypeof%20Symbol%26%26%22symbol%22%3D%3Dtypeof%20Symbol.iterator%3Ffunction(t)%7Breturn%20typeof%20t%7D%3Afunction(t)%7Breturn%20t%26%26%22function%22%3D%3Dtypeof%20Symbol%26%26t.constructor%3D%3D%3DSymbol%26%26t!%3D%3DSymbol.prototype%3F%22symbol%22%3Atypeof%20t%7D)(t)%7Dfunction%20l(t)%7Bvar%20e%3D0%3Carguments.length%26%26void%200!%3D%3Dt%3Ft%3A%7B%7D%3Btry%7Be.random_t%3D(new%20Date).getTime()%2Cg(function(t)%7Bvar%20e%3D1%3Carguments.length%26%26void%200!%3D%3Darguments%5B1%5D%3Farguments%5B1%5D%3A%22%22%3Bif(%22object%22!%3D%3Df(t))return%20e%3Bvar%20n%3Dfunction(t)%7Bfor(var%20e%2Cn%3D%5B%5D%2Co%3D0%2Ci%3DObject.keys(t)%3Bo%3Ci.length%3Bo%2B%2B)e%3Di%5Bo%5D%2Cn.push(%22%22.concat(e%2C%22%3D%22).concat(t%5Be%5D))%3Breturn%20n%7D(t).join(%22%26%22)%2Co%3De.indexOf(%22%23%22)%2Ci%3De%2Ct%3D%22%22%3Breturn-1!%3D%3Do%26%26(i%3De.slice(0%2Co)%2Ct%3De.slice(o))%2Cn%26%26(i%26%26-1!%3D%3Di.indexOf(%22%3F%22)%3Fi%2B%3D%22%26%22%2Bn%3Ai%2B%3D%22%3F%22%2Bn)%2Ci%2Bt%7D(e%2C%22https%3A%2F%2Ftrace.mediago.io%2Fapi%2Flog%2Ftrack%22))%7Dcatch(t)%7B%7D%7Dfunction%20g(t%2Ce%2Cn)%7B(t%3Dt%3Ft.split(%22%3B%3B%3B%22)%3A%5B%5D).map(function(t)%7Btry%7B0%3C%3Dt.indexOf(%22%2Fapi%2Fbidder%2Ftrack%22)%26%26n%26%26(t%2B%3D%22%26inIframe%3D%22.concat(!(!self.frameElement%7C%7C%22IFRAME%22!%3Dself.frameElement.tagName)%7C%7Cwindow.frames.length!%3Dparent.frames.length%7C%7Cself!%3Dtop)%2Ct%2B%3D%22%26pos_x%3D%22.concat(n.left%2C%22%26pos_y%3D%22).concat(n.top%2C%22%26page_w%3D%22).concat(n.page_width%2C%22%26page_h%3D%22).concat(n.page_height))%7Dcatch(t)%7Bl(%7Btn%3As%2Cwinloss%3A1%2Cfe%3A2%2Cpos_err_c%3A1002%2Cpos_err_m%3At.toString()%7D)%7Dvar%20e%3Dnew%20Image%3Be.src%3Dt%2Ce.style.display%3D%22none%22%2Ce.style.visibility%3D%22hidden%22%2Ce.width%3D0%2Ce.height%3D0%2Cdocument.body.appendChild(e)%7D)%7Dvar%20d%3D%5B%22https%3A%2F%2Ftrace.mediago.io%2Fapi%2Fbidder%2Ftrack%3Ftn%3D39934c2bda4debbe4c680be1dd02f5d3%26price%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26evt%3D101%26rid%3D6e28cfaf115a354ea1ad8e1304d6d7b8%26campaignid%3D1339145%26impid%3D44-300x250-1%26offerid%3D24054386%26test%3D0%26time%3D1660789795%26cp%3DjZDh1xu6_QqJLlKVtCkiHIP_TER6gL9jeTrlHCBoxOM%26acid%3D599%26trackingid%3D99afea272c2b0e8626489674ddb7a0bb%26uid%3Da865b9ae-fa9e-4c09-8204-2db99ac7c8f7%26bm%3D2%26la%3Den%26cn%3Dus%26cid%3D3998296%26info%3DSi3oM-qfCbw2iZRYs01BkUWyH6c5CQWHrA8CQLE0VHcXAcf4ljY9dyLzQ4vAlTWd6-j_ou4ySor3e70Ll7wlKiiauQKaUkZqNoTizHm73C4FK8DYJSTP3VkhJV8RzrYk%26sid%3D128__110__1__12__28__38__163__96__58__24__47__99%26sp%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26scp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26acu%3DUSD%26scu%3DUSD%26sgcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26gprice%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26gcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26ah%3D%26de%3Dwjh.popin.cc%26iv%3D0%22%2C%22%24%7BITRACKER2%7D%22%2C%22%24%7BITRACKER3%7D%22%2C%22%24%7BITRACKER4%7D%22%2C%22%24%7BITRACKER5%7D%22%2C%22%24%7BITRACKER6%7D%22%5D%2Cp%3D%5B%22https%3A%2F%2Ftrace.mediago.io%2Fapi%2Fbidder%2Ftrack%3Ftn%3D39934c2bda4debbe4c680be1dd02f5d3%26price%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26evt%3D104%26rid%3D6e28cfaf115a354ea1ad8e1304d6d7b8%26campaignid%3D1339145%26impid%3D44-300x250-1%26offerid%3D24054386%26test%3D0%26time%3D1660789795%26cp%3DjZDh1xu6_QqJLlKVtCkiHIP_TER6gL9jeTrlHCBoxOM%26acid%3D599%26trackingid%3D99afea272c2b0e8626489674ddb7a0bb%26uid%3Da865b9ae-fa9e-4c09-8204-2db99ac7c8f7%26sid%3D128__110__1__12__28__38__163__96__58__24__47__99%26format%3D%26crid%3Dff32b6f9b3bbc45c00b78b6674a2952e%26bm%3D2%26la%3Den%26cn%3Dus%26cid%3D3998296%26info%3DSi3oM-qfCbw2iZRYs01BkUWyH6c5CQWHrA8CQLE0VHcXAcf4ljY9dyLzQ4vAlTWd6-j_ou4ySor3e70Ll7wlKiiauQKaUkZqNoTizHm73C4FK8DYJSTP3VkhJV8RzrYk%26sp%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26scp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26acu%3DUSD%26scu%3DUSD%26sgcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26gprice%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26gcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26ah%3D%26de%3Dwjh.popin.cc%26iv%3D0%22%2C%22%24%7BVTRACKER2%7D%22%2C%22%24%7BVTRACKER3%7D%22%2C%22%24%7BVTRACKER4%7D%22%2C%22%24%7BVTRACKER5%7D%22%2C%22%24%7BVTRACKER6%7D%22%5D%2Cs%3D%22f9f2b1ef23fe2759c2cad0953029a94b%22%2Cn%3Ddocument.getElementById(%22mgcontainer-99afea272c2b0e8626489674ddb7a0bb%22)%3Bn%26%26function()%7Bvar%20a%3Dn.getElementsByClassName(%22mediago-placement-track%22)%3Bif(a%26%26a.length)%7Bvar%20t%2Ce%3Dfunction(t)%7Bvar%20e%2Cn%2Co%2Ci%2Cc%2Cr%3B%22object%22%3D%3D%3Df(r%3Da%5Bt%5D)%26%26(e%3Dfunction(t)%7Btry%7Bvar%20e%3Dt.getBoundingClientRect()%2Cn%3De%26%26e.top%7C%7C-1%2Co%3De%26%26e.left%7C%7C-1%2Ci%3Ddocument.body.scrollWidth%7C%7C-1%2Ce%3Ddocument.body.scrollHeight%7C%7C-1%3Breturn%7Btop%3An.toFixed(0)%2Cleft%3Ao.toFixed(0)%2Cpage_width%3Ai%2Cpage_height%3Ae%7D%7Dcatch(o)%7Breturn%20l(%7Btn%3As%2Cwinloss%3A1%2Cfe%3A2%2Cpos_err_c%3A1001%2Cpos_err_m%3Ao.toString()%7D)%2C%7Btop%3A%22-1%22%2Cleft%3A%22-1%22%2Cpage_width%3A%22-1%22%2Cpage_height%3A%22-1%22%7D%7D%7D(r)%2C(n%3Dd%5Bt%5D)%26%26g(n%2C0%2Ce)%2Co%3Dp%5Bt%5D%2Ci%3D!1%2C(c%3Dfunction()%7BsetTimeout(function()%7Bvar%20t%2Ce%3B!i%26%26(t%3Dr%2Ce%3Dwindow.innerHeight%7C%7Cdocument.documentElement.clientHeight%7C%7Cdocument.body.clientHeight%2C(t.getBoundingClientRect()%26%26t.getBoundingClientRect().top)%3C%3De-.75*(t.offsetHeight%7C%7Ct.clientHeight))%3F(i%3D!0%2Co%26%26g(o))%3Ac()%7D%2C500)%7D)())%7D%3Bfor(t%20in%20a)e(t)%7D%7D()%7D()'; + temp += '%3B%3C%2Fscri'; + temp += 'pt%3E'; + adm += decodeURIComponent(temp); + let serverResponse = { + body: { + id: 'mgprebidjs_0b6572fc-ceba-418f-b6fd-33b41ad0ac8a', + seatbid: [ + { + bid: [ + { + id: '6e28cfaf115a354ea1ad8e1304d6d7b8', + impid: '1', + price: 0.087581, + adm: adm, + cid: '1339145', + crid: 'ff32b6f9b3bbc45c00b78b6674a2952e', + w: 300, + h: 250, + }, + ], + }, + ], + cur: 'USD', + }, + }; + + let bids = spec.interpretResponse(serverResponse); + // console.log({ + // bids + // }); + expect(bids).to.have.lengthOf(1); + + let bid = bids[0]; + + expect(bid.creativeId).to.equal('ff32b6f9b3bbc45c00b78b6674a2952e'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.currency).to.equal('USD'); + }); +}); From 9d8de1d468f46cb7863bf7ecf86d601e038869dc Mon Sep 17 00:00:00 2001 From: Alexander <32703851+pro-nsk@users.noreply.github.com> Date: Wed, 24 Aug 2022 23:11:20 +0700 Subject: [PATCH 099/246] Alkimi Bid Adapter: add adUnitCode parameter to bidder (#8897) * Alkimi bid adapter * Alkimi bid adapter * Alkimi bid adapter * alkimi adapter * onBidWon change * sign utils * auction ID as bid request ID * unit test fixes * change maintainer info * Updated the ad unit params * features support added * transfer adUnitCode * transfer adUnitCode: test Co-authored-by: Alexander Bogdanov Co-authored-by: Kalidas Engaiahraj Co-authored-by: mihanikw2g <92710748+mihanikw2g@users.noreply.github.com> Co-authored-by: Nikulin Mikhail --- modules/alkimiBidAdapter.js | 3 ++- test/spec/modules/alkimiBidAdapter_spec.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/alkimiBidAdapter.js b/modules/alkimiBidAdapter.js index ac8b5af9533..fe5a050f436 100644 --- a/modules/alkimiBidAdapter.js +++ b/modules/alkimiBidAdapter.js @@ -31,7 +31,8 @@ export const spec = { bidFloor: bidRequest.params.bidFloor, width: sizes[0].width, height: sizes[0].height, - impMediaType: getFormatType(bidRequest) + impMediaType: getFormatType(bidRequest), + adUnitCode: bidRequest.adUnitCode }) bidIds.push(bidRequest.bidId) }) diff --git a/test/spec/modules/alkimiBidAdapter_spec.js b/test/spec/modules/alkimiBidAdapter_spec.js index c98c81b706e..1ae9bb56df4 100644 --- a/test/spec/modules/alkimiBidAdapter_spec.js +++ b/test/spec/modules/alkimiBidAdapter_spec.js @@ -6,6 +6,7 @@ const REQUEST = { 'bidId': '456', 'bidder': 'alkimi', 'sizes': [[300, 250]], + 'adUnitCode': 'bannerAdUnitCode', 'mediaTypes': { 'banner': { 'sizes': [[300, 250]] @@ -138,7 +139,7 @@ describe('alkimiBidAdapter', function () { expect(bidderRequest.data.requestId).to.equal('123') expect(bidderRequest.data.referer).to.equal('http://test.com/path.html') expect(bidderRequest.data.schain).to.deep.contains({ver: '1.0', complete: 1, nodes: [{asi: 'alkimi-onboarding.com', sid: '00001', hp: 1}]}) - expect(bidderRequest.data.signRequest.bids).to.deep.contains({ token: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7, bidFloor: 0.1, width: 300, height: 250, impMediaType: 'Banner' }) + expect(bidderRequest.data.signRequest.bids).to.deep.contains({ token: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7, bidFloor: 0.1, width: 300, height: 250, impMediaType: 'Banner', adUnitCode: 'bannerAdUnitCode' }) expect(bidderRequest.data.signRequest.randomUUID).to.equal(undefined) expect(bidderRequest.data.bidIds).to.deep.contains('456') expect(bidderRequest.data.signature).to.equal(undefined) From d88091c7689c47f964d52ad1f8a23b9eea1f9fc3 Mon Sep 17 00:00:00 2001 From: prebidtappx <77485538+prebidtappx@users.noreply.github.com> Date: Wed, 24 Aug 2022 18:12:37 +0200 Subject: [PATCH 100/246] Tappx Bid Adapter: fix host info http regex (#8896) * Fix: creating host correctly when http or https are added from the beginning * Fix :: Changed double quotes for single quotes Co-authored-by: Jordi Arnau --- modules/tappxBidAdapter.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 3cd77e7b853..11aa6c76c76 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -484,9 +484,18 @@ export function _getHostInfo(validBidRequests) { domainInfo.domain = hostParam.split('/', 1)[0]; + let regexHostParamHttps = new RegExp(`^https:\/\/`); + let regexHostParamHttp = new RegExp(`^http:\/\/`); + let regexNewEndpoints = new RegExp(`^(vz.*|zz.*)\\.[a-z]{3}\\.tappx\\.com$`, 'i'); let regexClassicEndpoints = new RegExp(`^([a-z]{3}|testing)\\.[a-z]{3}\\.tappx\\.com$`, 'i'); + if (regexHostParamHttps.test(hostParam)) { + hostParam = hostParam.replace('https://', ''); + } else if (regexHostParamHttp.test(hostParam)) { + hostParam = hostParam.replace('http://', ''); + } + if (regexNewEndpoints.test(domainInfo.domain)) { domainInfo.newEndpoint = true; domainInfo.endpoint = domainInfo.domain.split('.', 1)[0] From 5362745c08efcd374767f566a8c9bb1758093789 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Wed, 24 Aug 2022 12:38:29 -0600 Subject: [PATCH 101/246] Prebid core: optimize getRefererInfo to run only once per page (#8864) --- src/refererDetection.js | 13 ++++++++++++- test/spec/modules/enrichmentFpdModule_spec.js | 3 ++- test/spec/modules/fpdModule_spec.js | 3 ++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/refererDetection.js b/src/refererDetection.js index 15c080f5c69..28da182c7ab 100644 --- a/src/refererDetection.js +++ b/src/refererDetection.js @@ -11,6 +11,8 @@ import { config } from './config.js'; import {logWarn} from './utils.js'; +let RI = new WeakMap(); + /** * Prepend a URL with the page's protocol (http/https), if necessary. */ @@ -252,10 +254,19 @@ export function detectReferer(win) { }; } - return refererInfo; + return function() { + if (!RI.has(win)) { + RI.set(win, Object.freeze(refererInfo())); + } + return RI.get(win); + } } /** * @type {function(): refererInfo} */ export const getRefererInfo = detectReferer(window); + +export function resetRefererInfo() { + RI = new WeakMap(); +} diff --git a/test/spec/modules/enrichmentFpdModule_spec.js b/test/spec/modules/enrichmentFpdModule_spec.js index 7d7e463c015..3cc18f952cc 100644 --- a/test/spec/modules/enrichmentFpdModule_spec.js +++ b/test/spec/modules/enrichmentFpdModule_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { getRefererInfo } from 'src/refererDetection.js'; +import {getRefererInfo, resetRefererInfo} from 'src/refererDetection.js'; import { processFpd, coreStorage } from 'modules/enrichmentFpdModule.js'; describe('the first party data enrichment module', function() { @@ -20,6 +20,7 @@ describe('the first party data enrichment module', function() { }); beforeEach(function() { + resetRefererInfo(); querySelectorStub = sinon.stub(window.top.document, 'querySelector'); querySelectorStub.withArgs("link[rel='canonical']").returns(canonical); querySelectorStub.withArgs("meta[name='keywords']").returns(keywords); diff --git a/test/spec/modules/fpdModule_spec.js b/test/spec/modules/fpdModule_spec.js index 498bed29243..cf2ad4afe6f 100644 --- a/test/spec/modules/fpdModule_spec.js +++ b/test/spec/modules/fpdModule_spec.js @@ -1,6 +1,6 @@ import {expect} from 'chai'; import {config} from 'src/config.js'; -import {getRefererInfo} from 'src/refererDetection.js'; +import {getRefererInfo, resetRefererInfo} from 'src/refererDetection.js'; import {processFpd, registerSubmodules, startAuctionHook, reset} from 'modules/fpdModule/index.js'; import * as enrichmentModule from 'modules/enrichmentFpdModule.js'; import * as validationModule from 'modules/validationFpdModule/index.js'; @@ -70,6 +70,7 @@ describe('the first party data module', function () { }); beforeEach(function() { + resetRefererInfo(); querySelectorStub = sinon.stub(window.top.document, 'querySelector'); querySelectorStub.withArgs("link[rel='canonical']").returns(canonical); querySelectorStub.withArgs("meta[name='keywords']").returns(keywords); From 66fc005e1deaeee3fc1132ee324aee6efba54183 Mon Sep 17 00:00:00 2001 From: Jason Quaccia Date: Thu, 25 Aug 2022 03:52:32 -0700 Subject: [PATCH 102/246] Prebid Core: Batch Video Cache Requests feature (#8765) * batch video cache request integration * got rid of console log statement * removed video.html * addressed feedback * removed video file and uncommented test * reverted package-lock.json * retriggering circle ci * addressed changes * got rid of video html test file * removed unused methods from spec file * refactored auction.js and updated tests * updated test * removed video file * fixed test * moved logic where batch config options are referenced into the returned func of batchingCache * removed test video file * reverted a few other changes * moved batch config code outside of batchingCache func * always forget to remove this file after haha Co-authored-by: Jason Quaccia --- src/auction.js | 73 ++++++++++++++++++++++++++++-------- test/spec/videoCache_spec.js | 30 +++++++++++++++ 2 files changed, 88 insertions(+), 15 deletions(-) diff --git a/src/auction.js b/src/auction.js index b7deeccfd82..da805ae41dd 100644 --- a/src/auction.js +++ b/src/auction.js @@ -514,28 +514,71 @@ function tryAddVideoBid(auctionInstance, bidResponse, afterBidAdded, {index = au } } -export const callPrebidCache = hook('async', function(auctionInstance, bidResponse, afterBidAdded, videoMediaType) { - store([bidResponse], function (error, cacheIds) { - if (error) { - logWarn(`Failed to save to the video cache: ${error}. Video bid must be discarded.`); - - doCallbacksIfTimedout(auctionInstance, bidResponse); - } else { - if (cacheIds[0].uuid === '') { - logWarn(`Supplied video cache key was already in use by Prebid Cache; caching attempt was rejected. Video bid must be discarded.`); +const storeInCache = (batch) => { + store(batch.map(entry => entry.bidResponse), function (error, cacheIds) { + cacheIds.forEach((cacheId, i) => { + const { auctionInstance, bidResponse, afterBidAdded } = batch[i]; + if (error) { + logWarn(`Failed to save to the video cache: ${error}. Video bid must be discarded.`); doCallbacksIfTimedout(auctionInstance, bidResponse); } else { - bidResponse.videoCacheKey = cacheIds[0].uuid; + if (cacheId.uuid === '') { + logWarn(`Supplied video cache key was already in use by Prebid Cache; caching attempt was rejected. Video bid must be discarded.`); - if (!bidResponse.vastUrl) { - bidResponse.vastUrl = getCacheUrl(bidResponse.videoCacheKey); + doCallbacksIfTimedout(auctionInstance, bidResponse); + } else { + bidResponse.videoCacheKey = cacheId.uuid; + + if (!bidResponse.vastUrl) { + bidResponse.vastUrl = getCacheUrl(bidResponse.videoCacheKey); + } + addBidToAuction(auctionInstance, bidResponse); + afterBidAdded(); } - addBidToAuction(auctionInstance, bidResponse); - afterBidAdded(); } - } + }); }); +}; + +let batchSize, batchTimeout; +config.getConfig('cache', (cacheConfig) => { + batchSize = typeof cacheConfig.cache.batchSize === 'number' && cacheConfig.cache.batchSize > 0 + ? cacheConfig.cache.batchSize + : 1; + batchTimeout = typeof cacheConfig.cache.batchTimeout === 'number' && cacheConfig.cache.batchTimeout > 0 + ? cacheConfig.cache.batchTimeout + : 0; +}); + +export const batchingCache = (timeout = setTimeout, cache = storeInCache) => { + let batches = [[]]; + let debouncing = false; + const noTimeout = cb => cb(); + + return function(auctionInstance, bidResponse, afterBidAdded) { + const batchFunc = batchTimeout > 0 ? timeout : noTimeout; + if (batches[batches.length - 1].length >= batchSize) { + batches.push([]); + } + + batches[batches.length - 1].push({auctionInstance, bidResponse, afterBidAdded}); + + if (!debouncing) { + debouncing = true; + batchFunc(() => { + batches.forEach(cache); + batches = [[]]; + debouncing = false; + }, batchTimeout); + } + } +}; + +const batchAndStore = batchingCache(); + +export const callPrebidCache = hook('async', function(auctionInstance, bidResponse, afterBidAdded, videoMediaType) { + batchAndStore(auctionInstance, bidResponse, afterBidAdded); }, 'callPrebidCache'); // Postprocess the bids so that all the universal properties exist, no matter which bidder they came from. diff --git a/test/spec/videoCache_spec.js b/test/spec/videoCache_spec.js index 5885dfb7cdf..a13028c966a 100644 --- a/test/spec/videoCache_spec.js +++ b/test/spec/videoCache_spec.js @@ -4,6 +4,7 @@ import { config } from 'src/config.js'; import { server } from 'test/mocks/xhr.js'; import {auctionManager} from '../../src/auctionManager.js'; import {AuctionIndex} from '../../src/auctionIndex.js'; +import { batchingCache } from '../../src/auction.js'; const should = chai.should(); @@ -297,6 +298,35 @@ describe('The video cache', function () { JSON.parse(request.requestBody).should.deep.equal(payload); }); + it('should wait the duration of the batchTimeout and pass the correct batchSize if batched requests are enabled in the config', () => { + const mockAfterBidAdded = function() {}; + let callback = null; + let mockTimeout = sinon.stub().callsFake((cb) => { callback = cb }); + + config.setConfig({ + cache: { + url: 'https://prebid.adnxs.com/pbc/v1/cache', + batchSize: 3, + batchTimeout: 20 + } + }); + + let stubCache = sinon.stub(); + const batchAndStore = batchingCache(mockTimeout, stubCache); + for (let i = 0; i < 3; i++) { + batchAndStore({}, {}, mockAfterBidAdded); + } + + sinon.assert.calledOnce(mockTimeout); + sinon.assert.calledWith(mockTimeout, sinon.match.any, 20); + + const expectedBatch = [{ afterBidAdded: mockAfterBidAdded, auctionInstance: { }, bidResponse: { } }, { afterBidAdded: mockAfterBidAdded, auctionInstance: { }, bidResponse: { } }, { afterBidAdded: mockAfterBidAdded, auctionInstance: { }, bidResponse: { } }]; + + callback(); + + sinon.assert.calledWith(stubCache, expectedBatch); + }); + function assertRequestMade(bid, expectedValue) { store([bid], function () { }); From fb790f7fda84ca8332116d9e3a356e60da735d99 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 25 Aug 2022 13:36:20 +0000 Subject: [PATCH 103/246] Prebid 7.12.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index a6ae1e5a3e8..6cd1eedebd1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.12.0-pre", + "version": "7.12.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index a9a4c120c48..85c913baf21 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.12.0-pre", + "version": "7.12.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 5a92113c4152b68bfb054bd472556c2e925cffc9 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 25 Aug 2022 13:36:20 +0000 Subject: [PATCH 104/246] Increment version to 7.13.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6cd1eedebd1..160f2fbfe5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.12.0", + "version": "7.13.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 85c913baf21..5f68825a89d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.12.0", + "version": "7.13.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From fab2dc6ec49d3fbad635fc062679ea48db9aaecf Mon Sep 17 00:00:00 2001 From: AcuityAdsIntegrations <72594990+AcuityAdsIntegrations@users.noreply.github.com> Date: Thu, 25 Aug 2022 21:48:12 +0300 Subject: [PATCH 105/246] AcuityAds Bid Adapter: initial adapter release (#8854) --- modules/acuityAdsBidAdapter.js | 207 +++++++++ modules/acuityAdsBidAdapter.md | 79 ++++ test/spec/modules/acuityAdsBidAdapter_spec.js | 398 ++++++++++++++++++ 3 files changed, 684 insertions(+) create mode 100644 modules/acuityAdsBidAdapter.js create mode 100644 modules/acuityAdsBidAdapter.md create mode 100644 test/spec/modules/acuityAdsBidAdapter_spec.js diff --git a/modules/acuityAdsBidAdapter.js b/modules/acuityAdsBidAdapter.js new file mode 100644 index 00000000000..f469fe48c60 --- /dev/null +++ b/modules/acuityAdsBidAdapter.js @@ -0,0 +1,207 @@ +import { isFn, deepAccess, logMessage, logError } from '../src/utils.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; + +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; + +const BIDDER_CODE = 'acuityads'; +const AD_URL = 'https://prebid.admanmedia.com/pbjs'; +const SYNC_URL = 'https://cs.admanmedia.com'; + +function isBidResponseValid(bid) { + if (!bid.requestId || !bid.cpm || !bid.creativeId || !bid.ttl || !bid.currency) { + return false; + } + + switch (bid.mediaType) { + case BANNER: + return Boolean(bid.width && bid.height && bid.ad); + case VIDEO: + return Boolean(bid.vastUrl || bid.vastXml); + case NATIVE: + return Boolean(bid.native && bid.native.impressionTrackers && bid.native.impressionTrackers.length); + default: + return false; + } +} + +function getPlacementReqData(bid) { + const { params, bidId, mediaTypes } = bid; + const schain = bid.schain || {}; + const { placementId } = params; + const bidfloor = getBidFloor(bid); + + const placement = { + bidId, + schain, + bidfloor + }; + + placement.placementId = placementId; + placement.type = 'publisher'; + + if (mediaTypes && mediaTypes[BANNER]) { + placement.adFormat = BANNER; + placement.sizes = mediaTypes[BANNER].sizes; + } else if (mediaTypes && mediaTypes[VIDEO]) { + placement.adFormat = VIDEO; + placement.playerSize = mediaTypes[VIDEO].playerSize; + placement.minduration = mediaTypes[VIDEO].minduration; + placement.maxduration = mediaTypes[VIDEO].maxduration; + placement.mimes = mediaTypes[VIDEO].mimes; + placement.protocols = mediaTypes[VIDEO].protocols; + placement.startdelay = mediaTypes[VIDEO].startdelay; + placement.placement = mediaTypes[VIDEO].placement; + placement.skip = mediaTypes[VIDEO].skip; + placement.skipafter = mediaTypes[VIDEO].skipafter; + placement.minbitrate = mediaTypes[VIDEO].minbitrate; + placement.maxbitrate = mediaTypes[VIDEO].maxbitrate; + placement.delivery = mediaTypes[VIDEO].delivery; + placement.playbackmethod = mediaTypes[VIDEO].playbackmethod; + placement.api = mediaTypes[VIDEO].api; + placement.linearity = mediaTypes[VIDEO].linearity; + } else if (mediaTypes && mediaTypes[NATIVE]) { + placement.native = mediaTypes[NATIVE]; + placement.adFormat = NATIVE; + } + + return placement; +} + +function getBidFloor(bid) { + if (!isFn(bid.getFloor)) { + return deepAccess(bid, 'params.bidfloor', 0); + } + + try { + const bidFloor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*', + }); + return bidFloor.floor; + } catch (err) { + logError(err); + return 0; + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid = {}) => { + const { params, bidId, mediaTypes } = bid; + let valid = Boolean(bidId && params && params.placementId); + + if (mediaTypes && mediaTypes[BANNER]) { + valid = valid && Boolean(mediaTypes[BANNER] && mediaTypes[BANNER].sizes); + } else if (mediaTypes && mediaTypes[VIDEO]) { + valid = valid && Boolean(mediaTypes[VIDEO] && mediaTypes[VIDEO].playerSize); + } else if (mediaTypes && mediaTypes[NATIVE]) { + valid = valid && Boolean(mediaTypes[NATIVE]); + } else { + valid = false; + } + return valid; + }, + + buildRequests: (validBidRequests = [], bidderRequest = {}) => { + // convert Native ORTB definition to old-style prebid native definition + validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); + + let deviceWidth = 0; + let deviceHeight = 0; + + let winLocation; + try { + const winTop = window.top; + deviceWidth = winTop.screen.width; + deviceHeight = winTop.screen.height; + winLocation = winTop.location; + } catch (e) { + logMessage(e); + winLocation = window.location; + } + + const refferUrl = bidderRequest.refererInfo && bidderRequest.refererInfo.page; + let refferLocation; + try { + refferLocation = refferUrl && new URL(refferUrl); + } catch (e) { + logMessage(e); + } + // TODO: does the fallback make sense here? + let location = refferLocation || winLocation; + const language = (navigator && navigator.language) ? navigator.language.split('-')[0] : ''; + const host = location.host; + const page = location.pathname; + const secure = location.protocol === 'https:' ? 1 : 0; + const placements = []; + const request = { + deviceWidth, + deviceHeight, + language, + secure, + host, + page, + placements, + coppa: config.getConfig('coppa') === true ? 1 : 0, + ccpa: bidderRequest.uspConsent || undefined, + gdpr: bidderRequest.gdprConsent || undefined, + tmax: config.getConfig('bidderTimeout') + }; + + const len = validBidRequests.length; + for (let i = 0; i < len; i++) { + const bid = validBidRequests[i]; + placements.push(getPlacementReqData(bid)); + } + + return { + method: 'POST', + url: AD_URL, + data: request + }; + }, + + interpretResponse: (serverResponse) => { + let response = []; + for (let i = 0; i < serverResponse.body.length; i++) { + let resItem = serverResponse.body[i]; + if (isBidResponseValid(resItem)) { + const advertiserDomains = resItem.adomain && resItem.adomain.length ? resItem.adomain : []; + resItem.meta = { ...resItem.meta, advertiserDomains }; + + response.push(resItem); + } + } + return response; + }, + + getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { + let syncType = syncOptions.iframeEnabled ? 'iframe' : 'image'; + let syncUrl = SYNC_URL + `/${syncType}?pbjs=1`; + if (gdprConsent && gdprConsent.consentString) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + syncUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + syncUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; + } + } + if (uspConsent && uspConsent.consentString) { + syncUrl += `&ccpa_consent=${uspConsent.consentString}`; + } + + const coppa = config.getConfig('coppa') ? 1 : 0; + syncUrl += `&coppa=${coppa}`; + + return [{ + type: syncType, + url: syncUrl + }]; + } +}; + +registerBidder(spec); diff --git a/modules/acuityAdsBidAdapter.md b/modules/acuityAdsBidAdapter.md new file mode 100644 index 00000000000..a19e0a6b0ba --- /dev/null +++ b/modules/acuityAdsBidAdapter.md @@ -0,0 +1,79 @@ +# Overview + +``` +Module Name: AcuityAds Bidder Adapter +Module Type: AcuityAds Bidder Adapter +Maintainer: sa-support@brightcom.com +``` + +# Description + +Connects to AcuityAds exchange for bids. +AcuityAds bid adapter supports Banner, Video (instream and outstream) and Native. + +# Test Parameters +``` + var adUnits = [ + // Will return static test banner + { + code: 'adunit1', + mediaTypes: { + banner: { + sizes: [ [300, 250], [320, 50] ], + } + }, + bids: [ + { + bidder: 'acuityads', + params: { + placementId: 'testBanner', + } + } + ] + }, + { + code: 'addunit2', + mediaTypes: { + video: { + playerSize: [ [640, 480] ], + context: 'instream', + minduration: 5, + maxduration: 60, + } + }, + bids: [ + { + bidder: 'acuityads', + params: { + placementId: 'testVideo', + } + } + ] + }, + { + code: 'addunit3', + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids: [ + { + bidder: 'acuityads', + params: { + placementId: 'testNative', + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/test/spec/modules/acuityAdsBidAdapter_spec.js b/test/spec/modules/acuityAdsBidAdapter_spec.js new file mode 100644 index 00000000000..18ea574c1ce --- /dev/null +++ b/test/spec/modules/acuityAdsBidAdapter_spec.js @@ -0,0 +1,398 @@ +import { expect } from 'chai'; +import { spec } from '../../../modules/acuityAdsBidAdapter'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; +import { getUniqueIdentifierStr } from '../../../src/utils.js'; + +const bidder = 'acuityads' + +describe('AcuityAdsBidAdapter', function () { + const bids = [ + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + placementId: 'testBanner', + } + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [VIDEO]: { + playerSize: [[300, 300]], + minduration: 5, + maxduration: 60 + } + }, + params: { + placementId: 'testVideo', + } + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [NATIVE]: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + } + }, + params: { + placementId: 'testNative', + } + } + ]; + + const invalidBid = { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + + } + } + + const bidderRequest = { + uspConsent: '1---', + gdprConsent: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', + refererInfo: { + referer: 'https://test.com' + } + }; + + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and key parameters present', function () { + expect(spec.isBidRequestValid(bids[0])).to.be.true; + }); + it('Should return false if at least one of parameters is not present', function () { + expect(spec.isBidRequestValid(invalidBid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests(bids, bidderRequest); + + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('https://prebid.admanmedia.com/pbjs'); + }); + + it('Returns general data valid', function () { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', + 'deviceHeight', + 'language', + 'secure', + 'host', + 'page', + 'placements', + 'coppa', + 'ccpa', + 'gdpr', + 'tmax' + ); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.language).to.be.a('string'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + expect(data.coppa).to.be.a('number'); + expect(data.gdpr).to.be.a('string'); + expect(data.ccpa).to.be.a('string'); + expect(data.tmax).to.be.a('number'); + expect(data.placements).to.have.lengthOf(3); + }); + + it('Returns valid placements', function () { + const { placements } = serverRequest.data; + for (let i = 0, len = placements.length; i < len; i++) { + const placement = placements[i]; + expect(placement.placementId).to.be.oneOf(['testBanner', 'testVideo', 'testNative']); + expect(placement.adFormat).to.be.oneOf([BANNER, VIDEO, NATIVE]); + expect(placement.bidId).to.be.a('string'); + expect(placement.schain).to.be.an('object'); + expect(placement.bidfloor).to.exist.and.to.equal(0); + expect(placement.type).to.exist.and.to.equal('publisher'); + + if (placement.adFormat === BANNER) { + expect(placement.sizes).to.be.an('array'); + } + switch (placement.adFormat) { + case BANNER: + expect(placement.sizes).to.be.an('array'); + break; + case VIDEO: + expect(placement.playerSize).to.be.an('array'); + expect(placement.minduration).to.be.an('number'); + expect(placement.maxduration).to.be.an('number'); + break; + case NATIVE: + expect(placement.native).to.be.an('object'); + break; + } + } + }); + + it('Returns data with gdprConsent and without uspConsent', function () { + delete bidderRequest.uspConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + let data = serverRequest.data; + expect(data.gdpr).to.exist; + expect(data.gdpr).to.be.a('string'); + expect(data.gdpr).to.equal(bidderRequest.gdprConsent); + expect(data.ccpa).to.not.exist; + delete bidderRequest.gdprConsent; + }); + + it('Returns data with uspConsent and without gdprConsent', function () { + bidderRequest.uspConsent = '1---'; + delete bidderRequest.gdprConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + let data = serverRequest.data; + expect(data.ccpa).to.exist; + expect(data.ccpa).to.be.a('string'); + expect(data.ccpa).to.equal(bidderRequest.uspConsent); + expect(data.gdpr).to.not.exist; + }); + + it('Returns empty data if no valid requests are passed', function () { + serverRequest = spec.buildRequests([], bidderRequest); + let data = serverRequest.data; + expect(data.placements).to.be.an('array').that.is.empty; + }); + }); + + describe('interpretResponse', function () { + it('Should interpret banner response', function () { + const banner = { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let bannerResponses = spec.interpretResponse(banner); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + let dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal(banner.body[0].requestId); + expect(dataItem.cpm).to.equal(banner.body[0].cpm); + expect(dataItem.width).to.equal(banner.body[0].width); + expect(dataItem.height).to.equal(banner.body[0].height); + expect(dataItem.ad).to.equal(banner.body[0].ad); + expect(dataItem.ttl).to.equal(banner.body[0].ttl); + expect(dataItem.creativeId).to.equal(banner.body[0].creativeId); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal(banner.body[0].currency); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret video response', function () { + const video = { + body: [{ + vastUrl: 'test.com', + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let videoResponses = spec.interpretResponse(video); + expect(videoResponses).to.be.an('array').that.is.not.empty; + + let dataItem = videoResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.5); + expect(dataItem.vastUrl).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret native response', function () { + const native = { + body: [{ + mediaType: 'native', + native: { + clickUrl: 'test.com', + title: 'Test', + image: 'test.com', + impressionTrackers: ['test.com'], + }, + ttl: 120, + cpm: 0.4, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let nativeResponses = spec.interpretResponse(native); + expect(nativeResponses).to.be.an('array').that.is.not.empty; + + let dataItem = nativeResponses[0]; + expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native', 'meta'); + expect(dataItem.native).to.have.keys('clickUrl', 'impressionTrackers', 'title', 'image') + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.native.clickUrl).to.equal('test.com'); + expect(dataItem.native.title).to.equal('Test'); + expect(dataItem.native.image).to.equal('test.com'); + expect(dataItem.native.impressionTrackers).to.be.an('array').that.is.not.empty; + expect(dataItem.native.impressionTrackers[0]).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should return an empty array if invalid banner response is passed', function () { + const invBanner = { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + + let serverResponses = spec.interpretResponse(invBanner); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid video response is passed', function () { + const invVideo = { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invVideo); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid native response is passed', function () { + const invNative = { + body: [{ + mediaType: 'native', + clickUrl: 'test.com', + title: 'Test', + impressionTrackers: ['test.com'], + ttl: 120, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + let serverResponses = spec.interpretResponse(invNative); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid response is passed', function () { + const invalid = { + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invalid); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + + describe('getUserSyncs', function() { + it('Should return array of objects with proper sync config , include GDPR', function() { + const syncData = spec.getUserSyncs({}, {}, { + consentString: 'ALL', + gdprApplies: true, + }, {}); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://cs.admanmedia.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') + }); + it('Should return array of objects with proper sync config , include CCPA', function() { + const syncData = spec.getUserSyncs({}, {}, {}, { + consentString: '1---' + }); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://cs.admanmedia.com/image?pbjs=1&ccpa_consent=1---&coppa=0') + }); + }); +}); From 2a162842da342b45edf0c633a741ee6b5fd5110b Mon Sep 17 00:00:00 2001 From: eknis Date: Fri, 26 Aug 2022 23:43:38 +0900 Subject: [PATCH 106/246] ImRtdProvider: add segment max (#8902) * imRtdProvider: add segment max param * imRtdProvider: refactoring * imRtdProvider: refactoring --- modules/imRtdProvider.js | 27 ++++++++++++++++++------- modules/imRtdProvider.md | 4 +++- test/spec/modules/imRtdProvider_spec.js | 15 +++++++++----- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/modules/imRtdProvider.js b/modules/imRtdProvider.js index 0ed239019cf..bc01896d062 100644 --- a/modules/imRtdProvider.js +++ b/modules/imRtdProvider.js @@ -37,28 +37,40 @@ function setImDataInCookie(value) { ); } +/** + * @param {Object} segments + * @param {Object} moduleConfig + */ +function getSegments(segments, moduleConfig) { + if (!segments) return; + const maxSegments = !Number.isNaN(moduleConfig.params.maxSegments) ? moduleConfig.params.maxSegments : 200; + return segments.slice(0, maxSegments); +} + /** * @param {string} bidderName */ export function getBidderFunction(bidderName) { const biddersFunction = { - pubmatic: function (bid, data) { + pubmatic: function (bid, data, moduleConfig) { if (data.im_segments && data.im_segments.length) { + const segments = getSegments(data.im_segments, moduleConfig); const dctr = deepAccess(bid, 'params.dctr'); deepSetValue( bid, 'params.dctr', - `${dctr ? dctr + '|' : ''}im_segments=${data.im_segments.join(',')}` + `${dctr ? dctr + '|' : ''}im_segments=${segments.join(',')}` ); } return bid }, - fluct: function (bid, data) { + fluct: function (bid, data, moduleConfig) { if (data.im_segments && data.im_segments.length) { + const segments = getSegments(data.im_segments, moduleConfig); deepSetValue( bid, 'params.kv.imsids', - data.im_segments + segments ); } return bid @@ -88,14 +100,15 @@ export function setRealTimeData(bidConfig, moduleConfig, data) { const utils = {deepSetValue, deepAccess, logInfo, logError, mergeDeep}; if (data.im_segments) { + const segments = getSegments(data.im_segments, moduleConfig); const ortb2 = bidConfig.ortb2Fragments?.global || {}; - deepSetValue(ortb2, 'user.ext.data.im_segments', data.im_segments); + deepSetValue(ortb2, 'user.ext.data.im_segments', segments); if (moduleConfig.params.setGptKeyValues || !moduleConfig.params.hasOwnProperty('setGptKeyValues')) { window.googletag = window.googletag || {cmd: []}; window.googletag.cmd = window.googletag.cmd || []; window.googletag.cmd.push(() => { - window.googletag.pubads().setTargeting('im_segments', data.im_segments); + window.googletag.pubads().setTargeting('im_segments', segments); }); } } @@ -107,7 +120,7 @@ export function setRealTimeData(bidConfig, moduleConfig, data) { if (overwriteFunction) { overwriteFunction(bid, data, utils, config); } else if (bidderFunction) { - bidderFunction(bid, data); + bidderFunction(bid, data, moduleConfig); } }) }); diff --git a/modules/imRtdProvider.md b/modules/imRtdProvider.md index 7ece2b996b4..8f31d3eb545 100644 --- a/modules/imRtdProvider.md +++ b/modules/imRtdProvider.md @@ -21,7 +21,8 @@ pbjs.setConfig( waitForIt: true, params: { cid: 5126, // Set your Intimate Merger Customer ID here for production - setGptKeyValues: true + setGptKeyValues: true, + maxSegments: 200 // maximum number is 200 } } ] @@ -39,3 +40,4 @@ pbjs.setConfig( | params | Required | Object | Details of module params. | | | params.cid | Required | Number | This is the Customer ID value obtained via Intimate Merger. | `5126` | | params.setGptKeyValues | Optional | Boolean | This is set targeting for GPT/GAM. Default setting is true. | `true` | +| params.maxSegments | Optional | Number | This is set maximum number of rtd segments at once. Default setting is 200. | `200` | diff --git a/test/spec/modules/imRtdProvider_spec.js b/test/spec/modules/imRtdProvider_spec.js index b580e5b6516..89328b91529 100644 --- a/test/spec/modules/imRtdProvider_spec.js +++ b/test/spec/modules/imRtdProvider_spec.js @@ -27,7 +27,8 @@ describe('imRtdProvider', function () { const moduleConfig = { params: { cid: 5126, - setGptKeyValues: true + setGptKeyValues: true, + maxSegments: 2 } } @@ -60,11 +61,11 @@ describe('imRtdProvider', function () { it(`should return bid with correct key data: ${bidderName}`, function () { const bid = {bidder: bidderName}; - expect(getBidderFunction(bidderName)(bid, {'im_segments': ['12345', '67890']})).to.equal(bid); + expect(getBidderFunction(bidderName)(bid, {'im_segments': ['12345', '67890']}, {params: {}})).to.equal(bid); }); it(`should return bid without data: ${bidderName}`, function () { const bid = {bidder: bidderName}; - expect(getBidderFunction(bidderName)(bid, '')).to.equal(bid); + expect(getBidderFunction(bidderName)(bid, '', {params: {}})).to.equal(bid); }); }); it(`should return null with unexpected bidder`, function () { @@ -73,7 +74,7 @@ describe('imRtdProvider', function () { describe('fluct bidder function', function () { it('should return a bid w/o im_segments if not any exists', function () { const bid = {bidder: 'fluct'}; - expect(getBidderFunction('fluct')(bid, '')).to.eql(bid); + expect(getBidderFunction('fluct')(bid, '', {params: {}})).to.eql(bid); }); it('should return a bid w/ im_segments if any exists', function () { const bid = { @@ -84,7 +85,11 @@ describe('imRtdProvider', function () { } } }; - expect(getBidderFunction('fluct')(bid, {im_segments: ['12345', '67890']})) + expect(getBidderFunction('fluct')( + bid, + {im_segments: ['12345', '67890', '09876']}, + {params: {maxSegments: 2}} + )) .to.eql( { bidder: 'fluct', From cc3b3db43d435e0f73d5c5d494df792ec4703b31 Mon Sep 17 00:00:00 2001 From: probably-kira Date: Fri, 26 Aug 2022 11:28:40 -0400 Subject: [PATCH 107/246] Lifestreet Bid adapter: initial release (#8340) * Lifestreet adapter with meta support added * Added tests * fix linting error * Fixed typo in tests * CR fix: import functions from utils instead of using * Co-authored-by: Chris Huie --- modules/lifestreetBidAdapter.js | 143 +++++++++++ .../spec/modules/lifestreetBidAdapter_spec.js | 232 ++++++++++++++++++ 2 files changed, 375 insertions(+) create mode 100644 modules/lifestreetBidAdapter.js create mode 100644 test/spec/modules/lifestreetBidAdapter_spec.js diff --git a/modules/lifestreetBidAdapter.js b/modules/lifestreetBidAdapter.js new file mode 100644 index 00000000000..6a8b783ce21 --- /dev/null +++ b/modules/lifestreetBidAdapter.js @@ -0,0 +1,143 @@ +import { isInteger } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'lifestreet'; +const ADAPTER_VERSION = '$prebid.version$'; + +const urlTemplate = template`https://ads.lfstmedia.com/gate/${'adapter'}/${'slot'}?adkey=${'adkey'}&ad_size=${'ad_size'}&__location=${'location'}&__referrer=${'referrer'}&__wn=${'wn'}&__sf=${'sf'}&__fif=${'fif'}&__if=${'if'}&__stamp=${'stamp'}&__pp=1&__hb=1&_prebid_json=1&__gz=1&deferred_format=vast_2_0,vast_3_0&__hbver=${'hbver'}`; + +/** + * A helper function for template to generate string from boolean + */ +function boolToString(value) { + return value ? '1' : '0'; +} + +/** + * A helper function to form URL from the template + */ +function template(strings, ...keys) { + return function(...values) { + let dict = values[values.length - 1] || {}; + let result = [strings[0]]; + keys.forEach(function(key, i) { + let value = isInteger(key) ? values[key] : dict[key]; + result.push(value, strings[i + 1]); + }); + return result.join(''); + }; +} + +/** + * Creates a bid requests for a given bid. + * + * @param {BidRequest} bid The bid params to use for formatting a request + */ +function formatBidRequest(bid, bidderRequest = {}) { + const {params} = bid; + const {referer} = (bidderRequest.refererInfo || {}); + let url = urlTemplate({ + adapter: 'prebid', + slot: params.slot, + adkey: params.adkey, + ad_size: params.ad_size, + location: referer, + referrer: referer, + wn: boolToString(/fb_http/i.test(window.name)), + sf: boolToString(window['sfAPI'] || window['$sf']), + fif: boolToString(window['inDapIF'] === true), + if: boolToString(window !== window.top), + stamp: new Date().getTime(), + hbver: ADAPTER_VERSION + }); + + if (bidderRequest.gdprConsent) { + if (bidderRequest.gdprConsent.gdprApplies !== undefined) { + const gdpr = '&__gdpr=' + (bidderRequest.gdprConsent.gdprApplies ? '1' : '0'); + url += gdpr; + } + if (bidderRequest.gdprConsent.consentString !== undefined) { + url += `&__consent=${bidderRequest.gdprConsent.consentString}`; + } + } + + // ccpa support + if (bidderRequest.uspConsent) { + url += `&__us_privacy=${bidderRequest.uspConsent}` + } + + return { + method: 'GET', + url: url, + bidId: bid.bidId + }; +} + +function isResponseValid(response) { + return !/^\s*\{\s*"advertisementAvailable"\s*:\s*false/i.test(response.content) && + response.content.indexOf('') === -1 && /* (typeof response.cpm !== 'undefined') && */ + response.status === 1; +} + +export const spec = { + code: BIDDER_CODE, + aliases: ['lsm'], + supportedMediaTypes: [BANNER, VIDEO], + + isBidRequestValid: (bid = {}) => { + const {params = {}} = bid; + return !!(params.slot && params.adkey && params.ad_size); + }, + + buildRequests: (validBidRequests, bidderRequest) => { + return validBidRequests.map(bid => { + return formatBidRequest(bid, bidderRequest) + }); + }, + + interpretResponse: (serverResponse, bidRequest) => { + const bidResponses = []; + let response = serverResponse.body; + if (!isResponseValid(response)) { + return bidResponses; + } + + const isVideo = response.content_type.indexOf('vast') > -1; + const mediaType = isVideo ? VIDEO : BANNER; + + const bidResponse = { + requestId: bidRequest.bidId, + cpm: response.cpm, + currency: response.currency ? response.currency : 'USD', + width: response.width, + height: response.height, + creativeId: response.creativeId, + netRevenue: response.netRevenue ? response.netRevenue : true, + ttl: response.ttl ? response.ttl : 86400, + mediaType, + meta: { + mediaType, + advertiserDomains: response.advertiserDomains + } + }; + + if (response.hasOwnProperty('dealId')) { + bidResponse.dealId = response.dealId; + } + if (isVideo) { + if (typeof response.vastUrl !== 'undefined') { + bidResponse.vastUrl = response.vastUrl; + } else { + bidResponse.vastXml = response.content; + } + } else { + bidResponse.ad = response.content; + } + + bidResponses.push(bidResponse); + return bidResponses; + } +}; + +registerBidder(spec); diff --git a/test/spec/modules/lifestreetBidAdapter_spec.js b/test/spec/modules/lifestreetBidAdapter_spec.js new file mode 100644 index 00000000000..d66727da644 --- /dev/null +++ b/test/spec/modules/lifestreetBidAdapter_spec.js @@ -0,0 +1,232 @@ +import { expect } from 'chai'; +import { BANNER, VIDEO } from 'src/mediaTypes.js'; +import { spec } from 'modules/lifestreetBidAdapter.js'; + +describe('lifestreetBidAdapter', function() { + let bidRequests; + let videoBidRequests; + let bidResponses; + let videoBidResponses; + beforeEach(function() { + bidRequests = [ + { + bidder: 'lifestreet', + params: { + slot: 'slot166704', + adkey: '78c', + ad_size: '160x600' + }, + mediaTypes: { + banner: { + sizes: [ + [160, 600], + [300, 600] + ] + } + }, + sizes: [ + [160, 600], + [300, 600] + ] + } + ]; + + bidResponses = { + body: { + cpm: 0.1, + netRevenue: true, + content_type: 'display_flash', + width: 160, + currency: 'USD', + ttl: 86400, + content: '