forked from prebid/Prebid.js
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Oxxion Rtd Module: initial module release (prebid#9102)
* add oxxionRtdProvider new module * + * add tests * Update oxxionRtdProvider.md * Update oxxionRtdProvider.js * Update oxxionRtdProvider_spec.js * + * change test * change tests * change tests * change tests * change tests * requests changes * requests changes * requested changes
- Loading branch information
1 parent
b744a9d
commit 0a267dd
Showing
3 changed files
with
309 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import { submodule } from '../src/hook.js' | ||
import { deepAccess, logInfo } from '../src/utils.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 = []; | ||
|
||
/** @type {RtdSubmodule} */ | ||
export const oxxionSubmodule = { | ||
name: 'oxxionRtd', | ||
init: init, | ||
onAuctionEndEvent: onAuctionEnd, | ||
getBidRequestData: getAdUnits, | ||
}; | ||
|
||
function init(config, userConsent) { | ||
if (!config.params || !config.params.domain || !config.params.contexts || !Array.isArray(config.params.contexts) || config.params.contexts.length == 0) { | ||
return false | ||
} | ||
return true; | ||
} | ||
|
||
function getAdUnits(reqBidsConfigObj, callback, config, userConsent) { | ||
const reqAdUnits = reqBidsConfigObj.adUnits; | ||
if (Array.isArray(reqAdUnits)) { | ||
reqAdUnits.forEach(adunit => { | ||
if (config.params.contexts.includes(deepAccess(adunit, 'mediaTypes.video.context'))) { | ||
allAdUnits.push(adunit); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
function insertVideoTracking(bidResponse, config, maxCpm) { | ||
if (bidResponse.mediaType === 'video') { | ||
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 | ||
} | ||
// 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 = Math.round(100000 * (data.cpm - maxCpm)) / 100000; | ||
return trackingImpUrl + 'cpmIncrement=' + cpmIncrement + '&context=' + context; | ||
} | ||
|
||
function onAuctionEnd(auctionDetails, config, userConsent) { | ||
const transactionsToCheck = {} | ||
auctionDetails.adUnits.forEach(adunit => { | ||
if (config.params.contexts.includes(deepAccess(adunit, 'mediaTypes.video.context'))) { | ||
transactionsToCheck[adunit.transactionId] = {'bids': {}, 'maxCpm': 0.0, 'secondMaxCpm': 0.0}; | ||
} | ||
}); | ||
for (const key in auctionDetails.bidsReceived) { | ||
if (auctionDetails.bidsReceived[key].transactionId in transactionsToCheck) { | ||
transactionsToCheck[auctionDetails.bidsReceived[key].transactionId]['bids'][auctionDetails.bidsReceived[key].adId] = {'key': key, 'cpm': auctionDetails.bidsReceived[key].cpm}; | ||
if (auctionDetails.bidsReceived[key].cpm > transactionsToCheck[auctionDetails.bidsReceived[key].transactionId]['maxCpm']) { | ||
transactionsToCheck[auctionDetails.bidsReceived[key].transactionId]['secondMaxCpm'] = transactionsToCheck[auctionDetails.bidsReceived[key].transactionId]['maxCpm']; | ||
transactionsToCheck[auctionDetails.bidsReceived[key].transactionId]['maxCpm'] = auctionDetails.bidsReceived[key].cpm; | ||
} else if (auctionDetails.bidsReceived[key].cpm > transactionsToCheck[auctionDetails.bidsReceived[key].transactionId]['secondMaxCpm']) { | ||
transactionsToCheck[auctionDetails.bidsReceived[key].transactionId]['secondMaxCpm'] = auctionDetails.bidsReceived[key].cpm; | ||
} | ||
} | ||
}; | ||
Object.keys(transactionsToCheck).forEach(transaction => { | ||
Object.keys(transactionsToCheck[transaction]['bids']).forEach(bid => { | ||
insertVideoTracking(auctionDetails.bidsReceived[transactionsToCheck[transaction]['bids'][bid].key], config, transactionsToCheck[transaction].secondMaxCpm); | ||
}); | ||
}); | ||
} | ||
|
||
submodule('realTimeData', oxxionSubmodule); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Overview | ||
|
||
Module Name: Oxxion Rtd Provider | ||
Module Type: Rtd Provider | ||
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. | ||
|
||
# Integration | ||
|
||
Make sure to have the following modules listed while building prebid : `rtdModule,oxxionRtdProvider` | ||
`rtbModule` is required to activate real-time-data submodules. | ||
For example : | ||
``` | ||
gulp build --modules=schain,priceFloors,currency,consentManagement,appnexusBidAdapter,rubiconBidAdapter,rtdModule,oxxionRtdProvider | ||
``` | ||
|
||
Then add the oxxion Rtd module to your prebid configuration : | ||
``` | ||
pbjs.setConfig( | ||
... | ||
realTimeData: { | ||
auctionDelay: 200, | ||
dataProviders: [ | ||
{ | ||
name: "oxxionRtd", | ||
waitForIt: true, | ||
params: { | ||
domain: "test.endpoint", | ||
contexts: ["instream"], | ||
} | ||
} | ||
] | ||
} | ||
... | ||
) | ||
``` | ||
|
||
# setConfig Parameters | ||
|
||
| Name | Type | Description | | ||
|:---------------------------------|:---------|:------------------------------------------------------------------------------------------------------------| | ||
| domain | String | This string identifies yourself in Oxxion's systems and is provided to you by your Oxxion representative. | | ||
| contexts | Array | Array defining which video contexts to add tracking events into. Values can be instream and/or outstream. | | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
import {oxxionSubmodule} from 'modules/oxxionRtdProvider.js'; | ||
import 'src/prebid.js'; | ||
|
||
const utils = require('src/utils.js'); | ||
|
||
const moduleConfig = { | ||
params: { | ||
domain: 'test.endpoint', | ||
contexts: ['instream', 'outstream'] | ||
} | ||
}; | ||
|
||
let request = { | ||
'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', | ||
'timestamp': 1647424261187, | ||
'auctionEnd': 1647424261714, | ||
'auctionStatus': 'completed', | ||
'adUnits': [ | ||
{ | ||
'code': 'msq_tag_200124_banner', | ||
'mediaTypes': { 'banner': { 'sizes': [[300, 600]] } }, | ||
'bids': [{'bidder': 'appnexus', 'params': {'placementId': 123456}}], | ||
'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40' | ||
}, | ||
{ | ||
'code': 'msq_tag_200125_video', | ||
'mediaTypes': { 'video': { 'context': 'instream' }, playerSize: [640, 480], mimes: ['video/mp4'] }, | ||
'bids': [ | ||
{'bidder': 'mediasquare', 'params': {'code': 'publishername_atf_desktop_rg_video', 'owner': 'test'}}, | ||
{'bidder': 'appnexusAst', 'params': {'placementId': 345678}}, | ||
], | ||
'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b41' | ||
}, | ||
] | ||
}; | ||
|
||
let bids = [{ | ||
'bidderCode': 'mediasquare', | ||
'width': 640, | ||
'height': 480, | ||
'statusMessage': 'Bid available', | ||
'adId': '3647626fdbe68a', | ||
'requestId': '2d891705d2125b', | ||
'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b41', | ||
'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', | ||
'mediaType': 'video', | ||
'source': 'client', | ||
'cpm': 0.9723, | ||
'creativeId': 'freewheel|AdswizzAd71819', | ||
'currency': 'USD', | ||
'netRevenue': true, | ||
'ttl': 2000, | ||
'mediasquare': { | ||
'bidder': 'freewheel', | ||
'code': 'test/publishername_atf_desktop_rg_video', | ||
'hasConsent': true | ||
}, | ||
'meta': { | ||
'advertiserDomains': [ | ||
'unknown' | ||
] | ||
}, | ||
'vastUrl': 'https://some.vast-url.com', | ||
'vastXml': '<VAST version="3.0"><Ad><Wrapper><VASTAdTagURI><![CDATA[https://pbs-front.mediasquare.fr/cache?uuid=4de68767e8f2f9974fd4addd5a9d135a]]></VASTAdTagURI><Impression><![CDATA[https://pbs-front.mediasquare.fr/winning?adUnitCode=&auctionId=ZltsQnBqbcyC6aalwAdD4irnXmrl3E&bidder=freewheel&code=test/publishername_atf_desktop_rg_video&cpm=0.9723&creativeId=AdswizzAd71819¤cy=USD&hasConsent=true&mediaType=video&pbjs=&requestId=2d891705d2125b&size=video&timeToRespond=unknown]]></Impression></Wrapper></Ad></VAST>', | ||
'adapterCode': 'mediasquare', | ||
'originalCpm': 0.9723, | ||
'originalCurrency': 'USD', | ||
'responseTimestamp': 1665505150740, | ||
'requestTimestamp': 1665505150594, | ||
'bidder': 'mediasquare', | ||
'adUnitCode': 'msq_tag_200125_video', | ||
'timeToRespond': 146, | ||
'size': '640x480', | ||
}, { | ||
'bidderCode': 'appnexusAst', | ||
'width': 640, | ||
'height': 480, | ||
'statusMessage': 'Bid available', | ||
'adId': '4b2e1581c0ca1a', | ||
'requestId': '2d891705d2125b', | ||
'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b41', | ||
'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', | ||
'mediaType': 'video', | ||
'source': 'client', | ||
'cpm': 1.9723, | ||
'creativeId': '159080650', | ||
'currency': 'USD', | ||
'netRevenue': true, | ||
'ttl': 2000, | ||
'vastUrl': 'https://some.vast-url.com', | ||
'vastXml': '<VAST version="3.0"><Ad><InLine><AdSystem>Adnxs</AdSystem><AdTitle>Title</AdTitle><Creatives/></InLine></Ad></VAST>', | ||
'adapterCode': 'mediasquare', | ||
'originalCpm': 1.9723, | ||
'originalCurrency': 'USD', | ||
'responseTimestamp': 1665505150740, | ||
'requestTimestamp': 1665505150594, | ||
'bidder': 'appnexusAst', | ||
'adUnitCode': 'msq_tag_200125_video', | ||
'timeToRespond': 146, | ||
'size': '640x480', | ||
'vastImpUrl': 'https://some.tracking-url.com' | ||
}, | ||
]; | ||
|
||
describe('oxxionRtdProvider', () => { | ||
describe('Oxxion RTD sub module', () => { | ||
it('should init, return true, and set the params', () => { | ||
expect(oxxionSubmodule.init(moduleConfig)).to.equal(true); | ||
}); | ||
}); | ||
|
||
describe('Oxxion RTD sub module', () => { | ||
let auctionEnd = request; | ||
auctionEnd.bidsReceived = bids; | ||
it('call everything', function() { | ||
oxxionSubmodule.getBidRequestData(request, null, moduleConfig); | ||
oxxionSubmodule.onAuctionEndEvent(auctionEnd, moduleConfig); | ||
}); | ||
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=1')); | ||
}); | ||
}); | ||
}); |