From 6c7356a1b9013ba13a5ee2afbd4b0b7778621e7f Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Fri, 29 Sep 2023 14:05:48 +0200 Subject: [PATCH] Oxxion Rtd Module: cleaning and change in sampling (#10539) * remove video stuffs * update sampling * listen for 'ova' parameter * update sampling --- modules/oxxionAnalyticsAdapter.js | 2 +- modules/oxxionRtdProvider.js | 103 ++++---------------- modules/oxxionRtdProvider.md | 9 +- test/spec/modules/oxxionRtdProvider_spec.js | 95 ------------------ 4 files changed, 21 insertions(+), 188 deletions(-) diff --git a/modules/oxxionAnalyticsAdapter.js b/modules/oxxionAnalyticsAdapter.js index cc69443d8bf..d4dcd33be88 100644 --- a/modules/oxxionAnalyticsAdapter.js +++ b/modules/oxxionAnalyticsAdapter.js @@ -23,7 +23,7 @@ let auctionEnd = {} let initOptions = {} let mode = {}; let endpoint = 'https://default' -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', 'adId']; +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', 'adId', 'ova']; function getAdapterNameForAlias(aliasName) { return adapterManager.aliasRegistry[aliasName] || aliasName; diff --git a/modules/oxxionRtdProvider.js b/modules/oxxionRtdProvider.js index c6f8b9a902b..c979a10d00c 100644 --- a/modules/oxxionRtdProvider.js +++ b/modules/oxxionRtdProvider.js @@ -1,12 +1,10 @@ import { submodule } from '../src/hook.js' -import { deepAccess, logInfo, logError } from '../src/utils.js' +import { logInfo, logError } from '../src/utils.js' import { ajax } from '../src/ajax.js'; import adapterManager from '../src/adapterManager.js'; -const oxxionRtdSearchFor = [ 'adUnitCode', 'auctionId', 'bidder', 'bidderCode', 'bidId', 'cpm', 'creativeId', 'currency', 'width', 'height', 'mediaType', 'netRevenue', 'originalCpm', 'originalCurrency', 'requestId', 'size', 'source', 'status', 'timeToRespond', 'transactionId', 'ttl', 'sizes', 'mediaTypes', 'src', 'userId', 'labelAny', 'adId' ]; const LOG_PREFIX = 'oxxionRtdProvider submodule: '; -const allAdUnits = []; const bidderAliasRegistry = adapterManager.aliasRegistry || {}; /** @type {RtdSubmodule} */ @@ -14,14 +12,12 @@ export const oxxionSubmodule = { name: 'oxxionRtd', init: init, getBidRequestData: getAdUnits, - onBidResponseEvent: insertVideoTracking, getRequestsList: getRequestsList, getFilteredAdUnitsOnBidRates: getFilteredAdUnitsOnBidRates, }; function init(config, userConsent) { if (!config.params || !config.params.domain) { return false } - if (config.params.contexts && Array.isArray(config.params.contexts) && config.params.contexts.length > 0) { return true; } if (typeof config.params.threshold != 'undefined' && typeof config.params.samplingRate == 'number') { return true } return false; } @@ -53,81 +49,6 @@ function getAdUnits(reqBidsConfigObj, callback, config, userConsent) { if (typeof callback == 'function') { callback(); } }).catch(error => logError(LOG_PREFIX, 'bidInterestError', error)); } - if (config.params.contexts && Array.isArray(config.params.contexts) && config.params.contexts.length > 0) { - const reqAdUnits = reqBidsConfigObj.adUnits; - if (Array.isArray(reqAdUnits)) { - reqAdUnits.forEach(adunit => { - if (config.params.contexts.includes(deepAccess(adunit, 'mediaTypes.video.context'))) { - allAdUnits.push(adunit); - } - }); - } - if (!(typeof config.params.threshold != 'undefined' && typeof config.params.samplingRate == 'number') && typeof callback == 'function') { - callback(); - } - } -} - -function insertVideoTracking(bidResponse, config, userConsent) { - // this should only be do for video bids - if (bidResponse.mediaType === 'video') { - let maxCpm = 0; - const trackingUrl = getImpUrl(config, bidResponse, maxCpm); - if (!trackingUrl) { - return; - } - // Vast Impression URL - if (bidResponse.vastUrl) { - bidResponse.vastImpUrl = bidResponse.vastImpUrl - ? trackingUrl + '&url=' + encodeURI(bidResponse.vastImpUrl) - : trackingUrl; - logInfo(LOG_PREFIX + 'insert into vastImpUrl for adId ' + bidResponse.adId); - } - // Vast XML document - if (bidResponse.vastXml !== undefined) { - const doc = new DOMParser().parseFromString(bidResponse.vastXml, 'text/xml'); - const wrappers = doc.querySelectorAll('VAST Ad Wrapper, VAST Ad InLine'); - let hasAltered = false; - if (wrappers.length) { - wrappers.forEach(wrapper => { - const impression = doc.createElement('Impression'); - impression.appendChild(doc.createCDATASection(trackingUrl)); - wrapper.appendChild(impression) - }); - bidResponse.vastXml = new XMLSerializer().serializeToString(doc); - hasAltered = true; - } - if (hasAltered) { - logInfo(LOG_PREFIX + 'insert into vastXml for adId ' + bidResponse.adId); - } - } - } -} - -function getImpUrl(config, data, maxCpm) { - const adUnitCode = data.adUnitCode; - const adUnits = allAdUnits.find(adunit => adunit.code === adUnitCode && - 'mediaTypes' in adunit && - 'video' in adunit.mediaTypes && - typeof adunit.mediaTypes.video.context === 'string'); - const context = adUnits !== undefined - ? adUnits.mediaTypes.video.context - : 'unknown'; - if (!config.params.contexts.includes(context)) { - return false; - } - let trackingImpUrl = 'https://' + config.params.domain + '.oxxion.io/analytics/vast_imp?'; - trackingImpUrl += oxxionRtdSearchFor.reduce((acc, param) => { - switch (typeof data[param]) { - case 'string': - case 'number': - acc += param + '=' + data[param] + '&' - break; - } - return acc; - }, ''); - const cpmIncrement = 0.0; - return trackingImpUrl + 'cpmIncrement=' + cpmIncrement + '&context=' + context; } function getPromisifiedAjax (url, data = {}, options = {}) { @@ -146,22 +67,27 @@ function getPromisifiedAjax (url, data = {}, options = {}) { function getFilteredAdUnitsOnBidRates (bidsRateInterests, adUnits, params, useSampling) { const { threshold, samplingRate } = params; + const sampling = getRandomNumber(100) < samplingRate && useSampling; const filteredBids = []; // Separate bidsRateInterests in two groups against threshold & samplingRate - const { interestingBidsRates, uninterestingBidsRates } = bidsRateInterests.reduce((acc, interestingBid) => { + const { interestingBidsRates, uninterestingBidsRates, sampledBidsRates } = bidsRateInterests.reduce((acc, interestingBid) => { const isBidRateUpper = typeof threshold == 'number' ? interestingBid.rate === true || interestingBid.rate > threshold : interestingBid.suggestion; - const isBidInteresting = isBidRateUpper || (getRandomNumber(100) < samplingRate && useSampling); + const isBidInteresting = isBidRateUpper || sampling; const key = isBidInteresting ? 'interestingBidsRates' : 'uninterestingBidsRates'; acc[key].push(interestingBid); + if (!isBidRateUpper && sampling) { + acc['sampledBidsRates'].push(interestingBid); + } return acc; }, { interestingBidsRates: [], - uninterestingBidsRates: [] // Do something with later + uninterestingBidsRates: [], // Do something with later + sampledBidsRates: [] }); logInfo(LOG_PREFIX, 'getFilteredAdUnitsOnBidRates()', interestingBidsRates, uninterestingBidsRates); // Filter bids and adUnits against interesting bids rates const newAdUnits = adUnits.filter(({ bids = [] }, adUnitIndex) => { - adUnits[adUnitIndex].bids = bids.filter(bid => { + adUnits[adUnitIndex].bids = bids.filter((bid, bidIndex) => { if (!params.bidders || params.bidders.includes(bid.bidder)) { const index = interestingBidsRates.findIndex(({ id }) => id === bid._id); if (index == -1) { @@ -173,10 +99,19 @@ function getFilteredAdUnitsOnBidRates (bidsRateInterests, adUnits, params, useSa delete tmpBid.floorData; } filteredBids.push(tmpBid); + adUnits[adUnitIndex].bids[bidIndex]['ova'] = 'filtered'; + } else { + if (sampledBidsRates.findIndex(({ id }) => id === bid._id) == -1) { + adUnits[adUnitIndex].bids[bidIndex]['ova'] = 'cleared'; + } else { + adUnits[adUnitIndex].bids[bidIndex]['ova'] = 'sampled'; + logInfo(LOG_PREFIX + ' sampled ! '); + } } delete bid._id; return index !== -1; } else { + adUnits[adUnitIndex].bids[bidIndex]['ova'] = 'protected'; return true; } }); diff --git a/modules/oxxionRtdProvider.md b/modules/oxxionRtdProvider.md index 14b4abec5c2..bfdbfae1fa9 100644 --- a/modules/oxxionRtdProvider.md +++ b/modules/oxxionRtdProvider.md @@ -7,7 +7,7 @@ Maintainer: tech@oxxion.io # Oxxion Real-Time-Data submodule Oxxion helps you to understand how your prebid stack performs. -This Rtd module is to use in order to improve video events tracking and/or to filter bidder requested. +This Rtd module purpose is to filter bidders requested. # Integration @@ -30,7 +30,6 @@ pbjs.setConfig( waitForIt: true, params: { domain: "test.endpoint", - contexts: ["instream"], threshold: false, samplingRate: 10, } @@ -47,12 +46,6 @@ pbjs.setConfig( |:---------------------------------|:---------|:------------------------------------------------------------------------------------------------------------| | domain | String | This string identifies yourself in Oxxion's systems and is provided to you by your Oxxion representative. | -# setConfig Parameters for Video Tracking - -| Name | Type | Description | -|:---------------------------------|:---------|:------------------------------------------------------------------------------------------------------------| -| contexts | Array | Array defining which video contexts to add tracking events into. Values can be instream and/or outstream. | - # setConfig Parameters for bidder filtering | Name | Type | Description | diff --git a/test/spec/modules/oxxionRtdProvider_spec.js b/test/spec/modules/oxxionRtdProvider_spec.js index 7bccf2319a4..2a8024f3565 100644 --- a/test/spec/modules/oxxionRtdProvider_spec.js +++ b/test/spec/modules/oxxionRtdProvider_spec.js @@ -113,77 +113,6 @@ let bids = [{ }, ]; -let originalBidderRequests = [{ - 'bidderCode': 'rubicon', - 'auctionId': 'dd42b870-2072-4b71-8ab7-e7789b14c5ce', - 'bidderRequestId': '16c2bceb2e891a', - 'bids': [ - { - 'bidder': 'rubicon', - 'params': { - 'accountId': 1234, - 'siteId': 2345, - 'zoneId': 3456 - }, - 'auctionId': 'dd42b870-2072-4b71-8ab7-e7789b14c5ce', - 'mediaTypes': {'banner': {'sizes': [[970, 250]]}}, - 'adUnitCode': 'adunit1', - 'transactionId': '8f20b49c-5e47-4bb5-a7d5-0b816cf527f3', - 'bidId': '2d9920072ab028', - 'bidderRequestId': '16c2bceb2e891a', - }, - { - 'bidder': 'rubicon', - 'params': { - 'accountId': 1234, - 'siteId': 2345, - 'zoneId': 4567 - }, - 'auctionId': 'dd42b870-2072-4b71-8ab7-e7789b14c5ce', - 'mediaTypes': {'banner': {'sizes': [[300, 250]]}}, - 'adUnitCode': 'adunit2', - 'transactionId': '4161f09e-7870-4486-b2a6-b4158a327bc4', - 'bidId': '331c3d708f4864', - 'bidderRequestId': '16c2bceb2e891a', - 'src': 'client', - } - ], - 'auctionStart': 1683383333809, - 'timeout': 3000, - 'gdprConsent': { - 'consentString': 'consent_hash', - 'gdprApplies': true, - 'apiVersion': 2 - } -}, -{ - 'bidderCode': 'appnexusAst', - 'auctionId': 'dd42b870-2072-4b71-8ab7-e7789b14c5ce', - 'bidderRequestId': '4d83b8c60d45e7', - 'bids': [ - { - 'bidder': 'appnexusAst', - 'params': { - 'placementId': 10471298 - }, - 'auctionId': 'dd42b870-2072-4b71-8ab7-e7789b14c5ce', - 'mediaTypes': {'banner': {'sizes': [[300, 250]]}}, - 'adUnitCode': 'adunit2', - 'transactionId': '4161f09e-7870-4486-b2a6-b4158a327bc4', - 'bidId': '5b7cd5abc6aea3', - 'bidderRequestId': '4d83b8c60d45e7', - } - ], - 'auctionStart': 1683383333809, - 'timeout': 3000, - 'gdprConsent': { - 'consentString': 'consent_hash', - 'gdprApplies': true, - 'apiVersion': 2 - } -} -]; - let bidInterests = [ {'id': 0, 'rate': 50.0, 'suggestion': true}, {'id': 1, 'rate': 12.0, 'suggestion': false}, @@ -212,8 +141,6 @@ describe('oxxionRtdProvider', () => { auctionEnd.bidsReceived = bids; it('call everything', function() { oxxionSubmodule.getBidRequestData(request, null, moduleConfig); - oxxionSubmodule.onBidResponseEvent(auctionEnd.bidsReceived[0], moduleConfig); - oxxionSubmodule.onBidResponseEvent(auctionEnd.bidsReceived[1], moduleConfig); }); it('check bid filtering', function() { let requestsList = oxxionSubmodule.getRequestsList(request); @@ -229,27 +156,5 @@ describe('oxxionRtdProvider', () => { expect(filteredBiddderRequests[1]).to.have.property('bids'); expect(filteredBiddderRequests[1].bids.length).to.equal(1); }); - it('check vastImpUrl', function() { - expect(auctionEnd.bidsReceived[0]).to.have.property('vastImpUrl'); - let expectVastImpUrl = 'https://' + moduleConfig.params.domain + '.oxxion.io/analytics/vast_imp?'; - expect(auctionEnd.bidsReceived[1].vastImpUrl).to.contain(expectVastImpUrl); - expect(auctionEnd.bidsReceived[1].vastImpUrl).to.contain(encodeURI('https://some.tracking-url.com')); - }); - it('check vastXml', function() { - expect(auctionEnd.bidsReceived[0]).to.have.property('vastXml'); - let vastWrapper = new DOMParser().parseFromString(auctionEnd.bidsReceived[0].vastXml, 'text/xml'); - let impressions = vastWrapper.querySelectorAll('VAST Ad Wrapper Impression'); - expect(impressions.length).to.equal(2); - expect(auctionEnd.bidsReceived[1]).to.have.property('vastXml'); - expect(auctionEnd.bidsReceived[1].adId).to.equal('4b2e1581c0ca1a'); - let vastInline = new DOMParser().parseFromString(auctionEnd.bidsReceived[1].vastXml, 'text/xml'); - let inline = vastInline.querySelectorAll('VAST Ad InLine'); - expect(inline).to.have.lengthOf(1); - let inlineImpressions = vastInline.querySelectorAll('VAST Ad InLine Impression'); - expect(inlineImpressions).to.have.lengthOf.above(0); - }); - it('check cpmIncrement', function() { - expect(auctionEnd.bidsReceived[1].vastImpUrl).to.contain(encodeURI('cpmIncrement=0')); - }); }); });