-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WURFL RTD submodule: initial version (#11840)
* WURFL Rtd Provider: initial version * WURFL Rtd Provider: import fetch method from ajax.js module * WURFL Rtd Provider: unit tests * WURFL Rtd Provider: list wurflRtdProvider in the .submodules.json file * WURFL Rtd Provider: remove wurfl from adloader.js * WURFL Rtd Provider: update to use loadExternalScript * WURFL Rtd Provider: update to use sendBeacon from ajax.js
- Loading branch information
Showing
6 changed files
with
713 additions
and
1 deletion.
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,106 @@ | ||
<html> | ||
|
||
<head> | ||
<meta http-equiv="delegate-ch" content="sec-ch-ua https://prebid.wurflcloud.com; sec-ch-ua-bitness https://prebid.wurflcloud.com; sec-ch-ua-arch https://prebid.wurflcloud.com; sec-ch-ua-model https://prebid.wurflcloud.com; sec-ch-ua-platform https://prebid.wurflcloud.com; sec-ch-ua-platform-version https://prebid.wurflcloud.com; sec-ch-ua-full-version https://prebid.wurflcloud.com; sec-ch-ua-full-version-list https://prebid.wurflcloud.com; sec-ch-ua-mobile https://prebid.wurflcloud.com"> | ||
<script async src="../../build/dev/prebid.js"></script> | ||
<script async src="https://www.googletagservices.com/tag/js/gpt.js"></script> | ||
<script> | ||
var FAILSAFE_TIMEOUT = 3300; | ||
var PREBID_TIMEOUT = 2000; | ||
|
||
var adUnits = [ | ||
{ | ||
code: 'div-gpt-ad-1460505748561-0', | ||
mediaTypes: { | ||
banner: { | ||
sizes: [[300, 250]], | ||
} | ||
}, | ||
sizes: [ | ||
[300, 250], | ||
[728, 90] | ||
], | ||
bids: [ | ||
{ | ||
bidder: 'appnexus', | ||
params: { | ||
placementId: 13144370 | ||
} | ||
}, | ||
] | ||
|
||
}]; | ||
|
||
var pbjs = pbjs || {}; | ||
pbjs.que = pbjs.que || []; | ||
</script> | ||
|
||
<script> | ||
var googletag = googletag || {}; | ||
googletag.cmd = googletag.cmd || []; | ||
googletag.cmd.push(function () { | ||
googletag.pubads().disableInitialLoad(); | ||
}); | ||
|
||
pbjs.que.push(function () { | ||
// configure the WURFL RTD module | ||
pbjs.setConfig({ | ||
debug: true, // enabled for testing purposes | ||
realTimeData: { | ||
auctionDelay: 2000, | ||
dataProviders: [ | ||
// WURFL RTD module configuration | ||
{ | ||
name: 'wurfl', | ||
waitForIt: true | ||
}, | ||
] | ||
} | ||
}); | ||
|
||
pbjs.addAdUnits(adUnits); | ||
|
||
pbjs.requestBids({ | ||
bidsBackHandler: sendAdserverRequest, | ||
timeout: PREBID_TIMEOUT | ||
}); | ||
}); | ||
|
||
function sendAdserverRequest() { | ||
if (pbjs.adserverRequestSent) return; | ||
pbjs.adserverRequestSent = true; | ||
googletag.cmd.push(function () { | ||
pbjs.que.push(function () { | ||
pbjs.setTargetingForGPTAsync(); | ||
googletag.pubads().refresh(); | ||
}); | ||
}); | ||
} | ||
|
||
setTimeout(function () { | ||
sendAdserverRequest(); | ||
}, FAILSAFE_TIMEOUT); | ||
|
||
</script> | ||
|
||
<script> | ||
googletag.cmd.push(function () { | ||
googletag.defineSlot('/19968336/header-bid-tag-0', [[300, 250], [300, 600]], 'div-gpt-ad-1460505748561-0').addService(googletag.pubads()); | ||
|
||
googletag.pubads().enableSingleRequest(); | ||
googletag.enableServices(); | ||
}); | ||
</script> | ||
</head> | ||
|
||
<body> | ||
<h2>Prebid.js Test</h2> | ||
<h5>Div-1</h5> | ||
<div id='div-gpt-ad-1460505748561-0'> | ||
<script type='text/javascript'> | ||
googletag.cmd.push(function () { googletag.display('div-gpt-ad-1460505748561-0'); }); | ||
</script> | ||
</div> | ||
</body> | ||
|
||
</html> |
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
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,213 @@ | ||
import { submodule } from '../src/hook.js'; | ||
import { fetch, sendBeacon } from '../src/ajax.js'; | ||
import { loadExternalScript } from '../src/adloader.js'; | ||
import { | ||
mergeDeep, | ||
prefixLog, | ||
} from '../src/utils.js'; | ||
|
||
// Constants | ||
const REAL_TIME_MODULE = 'realTimeData'; | ||
const MODULE_NAME = 'wurfl'; | ||
|
||
// WURFL_JS_HOST is the host for the WURFL service endpoints | ||
const WURFL_JS_HOST = 'https://prebid.wurflcloud.com'; | ||
// WURFL_JS_ENDPOINT_PATH is the path for the WURFL.js endpoint used to load WURFL data | ||
const WURFL_JS_ENDPOINT_PATH = '/wurfl.js'; | ||
// STATS_ENDPOINT_PATH is the path for the stats endpoint used to send analytics data | ||
const STATS_ENDPOINT_PATH = '/v1/prebid/stats'; | ||
|
||
const logger = prefixLog('[WURFL RTD Submodule]'); | ||
|
||
// enrichedBidders holds a list of prebid bidder names, of bidders which have been | ||
// injected with WURFL data | ||
const enrichedBidders = new Set(); | ||
|
||
/** | ||
* init initializes the WURFL RTD submodule | ||
* @param {Object} config Configuration for WURFL RTD submodule | ||
* @param {Object} userConsent User consent data | ||
*/ | ||
const init = (config, userConsent) => { | ||
logger.logMessage('initialized'); | ||
return true; | ||
} | ||
|
||
/** | ||
* getBidRequestData enriches the OpenRTB 2.0 device data with WURFL data | ||
* @param {Object} reqBidsConfigObj Bid request configuration object | ||
* @param {Function} callback Called on completion | ||
* @param {Object} config Configuration for WURFL RTD submodule | ||
* @param {Object} userConsent User consent data | ||
*/ | ||
const getBidRequestData = (reqBidsConfigObj, callback, config, userConsent) => { | ||
const altHost = config.params?.altHost ?? null; | ||
const isDebug = config.params?.debug ?? false; | ||
|
||
const bidders = new Set(); | ||
reqBidsConfigObj.adUnits.forEach(adUnit => { | ||
adUnit.bids.forEach(bid => { | ||
bidders.add(bid.bidder); | ||
}); | ||
}); | ||
|
||
let host = WURFL_JS_HOST; | ||
if (altHost) { | ||
host = altHost; | ||
} | ||
|
||
const url = new URL(host); | ||
url.pathname = WURFL_JS_ENDPOINT_PATH; | ||
|
||
if (isDebug) { | ||
url.searchParams.set('debug', 'true') | ||
} | ||
|
||
url.searchParams.set('mode', 'prebid') | ||
logger.logMessage('url', url.toString()); | ||
|
||
try { | ||
loadExternalScript(url.toString(), MODULE_NAME, () => { | ||
logger.logMessage('script injected'); | ||
window.WURFLPromises.complete.then((res) => { | ||
logger.logMessage('received data', res); | ||
if (!res.wurfl_pbjs) { | ||
logger.logError('invalid WURFL.js for Prebid response'); | ||
} else { | ||
enrichBidderRequests(reqBidsConfigObj, bidders, res); | ||
} | ||
callback(); | ||
}); | ||
}); | ||
} catch (err) { | ||
logger.logError(err); | ||
callback(); | ||
} | ||
} | ||
|
||
/** | ||
* enrichBidderRequests enriches the OpenRTB 2.0 device data with WURFL data for Business Edition | ||
* @param {Object} reqBidsConfigObj Bid request configuration object | ||
* @param {Array} bidders List of bidders | ||
* @param {Object} wjsResponse WURFL.js response | ||
*/ | ||
function enrichBidderRequests(reqBidsConfigObj, bidders, wjsResponse) { | ||
const authBidders = wjsResponse.wurfl_pbjs?.authorized_bidders ?? {}; | ||
const caps = wjsResponse.wurfl_pbjs?.caps ?? []; | ||
|
||
bidders.forEach((bidderCode) => { | ||
if (bidderCode in authBidders) { | ||
// inject WURFL data | ||
enrichedBidders.add(bidderCode); | ||
const data = bidderData(wjsResponse.WURFL, caps, authBidders[bidderCode]); | ||
logger.logMessage(`injecting data for ${bidderCode}: `, data); | ||
enrichBidderRequest(reqBidsConfigObj, bidderCode, data); | ||
return; | ||
} | ||
// inject WURFL low entropy data | ||
const data = lowEntropyData(wjsResponse.WURFL, wjsResponse.wurfl_pbjs?.low_entropy_caps); | ||
logger.logMessage(`injecting low entropy data for ${bidderCode}: `, data); | ||
enrichBidderRequest(reqBidsConfigObj, bidderCode, data); | ||
}); | ||
} | ||
|
||
/** | ||
* bidderData returns the WURFL data for a bidder | ||
* @param {Object} wurflData WURFL data | ||
* @param {Array} caps Capability list | ||
* @param {Array} filter Filter list | ||
* @returns {Object} Bidder data | ||
*/ | ||
export const bidderData = (wurflData, caps, filter) => { | ||
const data = {}; | ||
caps.forEach((cap, index) => { | ||
if (!filter.includes(index)) { | ||
return; | ||
} | ||
if (cap in wurflData) { | ||
data[cap] = wurflData[cap]; | ||
} | ||
}); | ||
return data; | ||
} | ||
|
||
/** | ||
* lowEntropyData returns the WURFL low entropy data | ||
* @param {Object} wurflData WURFL data | ||
* @param {Array} lowEntropyCaps Low entropy capability list | ||
* @returns {Object} Bidder data | ||
*/ | ||
export const lowEntropyData = (wurflData, lowEntropyCaps) => { | ||
const data = {}; | ||
lowEntropyCaps.forEach((cap, _) => { | ||
let value = wurflData[cap]; | ||
if (cap == 'complete_device_name') { | ||
value = value.replace(/Apple (iP(hone|ad|od)).*/, 'Apple iP$2'); | ||
} | ||
data[cap] = value; | ||
}); | ||
return data; | ||
} | ||
|
||
/** | ||
* enrichBidderRequest enriches the bidder request with WURFL data | ||
* @param {Object} reqBidsConfigObj Bid request configuration object | ||
* @param {String} bidderCode Bidder code | ||
* @param {Object} wurflData WURFL data | ||
*/ | ||
export const enrichBidderRequest = (reqBidsConfigObj, bidderCode, wurflData) => { | ||
const ortb2data = { | ||
'device': { | ||
'ext': { | ||
'wurfl': wurflData, | ||
} | ||
}, | ||
}; | ||
mergeDeep(reqBidsConfigObj.ortb2Fragments.bidder, { [bidderCode]: ortb2data }); | ||
} | ||
|
||
/** | ||
* onAuctionEndEvent is called when the auction ends | ||
* @param {Object} auctionDetails Auction details | ||
* @param {Object} config Configuration for WURFL RTD submodule | ||
* @param {Object} userConsent User consent data | ||
*/ | ||
function onAuctionEndEvent(auctionDetails, config, userConsent) { | ||
const altHost = config.params?.altHost ?? null; | ||
|
||
let host = WURFL_JS_HOST; | ||
if (altHost) { | ||
host = altHost; | ||
} | ||
|
||
const url = new URL(host); | ||
url.pathname = STATS_ENDPOINT_PATH; | ||
|
||
if (enrichedBidders.size === 0) { | ||
return; | ||
} | ||
|
||
var payload = JSON.stringify({ bidders: [...enrichedBidders] }); | ||
const sentBeacon = sendBeacon(url.toString(), payload); | ||
if (sentBeacon) { | ||
return; | ||
} | ||
|
||
fetch(url.toString(), { | ||
method: 'POST', | ||
body: payload, | ||
mode: 'no-cors', | ||
keepalive: true | ||
}); | ||
} | ||
|
||
// The WURFL submodule | ||
export const wurflSubmodule = { | ||
name: MODULE_NAME, | ||
init, | ||
getBidRequestData, | ||
onAuctionEndEvent, | ||
} | ||
|
||
// Register the WURFL submodule as submodule of realTimeData | ||
submodule(REAL_TIME_MODULE, wurflSubmodule); |
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,67 @@ | ||
# WURFL Real-time Data Submodule | ||
|
||
## Overview | ||
|
||
Module Name: WURFL Rtd Provider | ||
Module Type: Rtd Provider | ||
Maintainer: [email protected] | ||
|
||
## Description | ||
|
||
The WURFL RTD module enriches the OpenRTB 2.0 device data with [WURFL data](https://www.scientiamobile.com/wurfl-js-business-edition-at-the-intersection-of-javascript-and-enterprise/). | ||
The module sets the WURFL data in `device.ext.wurfl` and all the bidder adapters will always receive the low entry capabilites like `is_mobile`, `complete_device_name` and `form_factor`. | ||
|
||
For a more detailed analysis bidders can subscribe to detect iPhone and iPad models and receive additional [WURFL device capabilities](https://www.scientiamobile.com/capabilities/?products%5B%5D=wurfl-js). | ||
|
||
## User-Agent Client Hints | ||
|
||
WURFL.js is fully compatible with Chromium's User-Agent Client Hints (UA-CH) initiative. If User-Agent Client Hints are absent in the HTTP headers that WURFL.js receives, the service will automatically fall back to using the User-Agent Client Hints' JS API to fetch [high entropy client hint values](https://wicg.github.io/ua-client-hints/#getHighEntropyValues) from the client device. However, we recommend that you explicitly opt-in/advertise support for User-Agent Client Hints on your website and delegate them to the WURFL.js service for the fastest detection experience. Our documentation regarding implementing User-Agent Client Hint support [is available here](https://docs.scientiamobile.com/guides/implementing-useragent-clienthints). | ||
|
||
## Usage | ||
|
||
### Build | ||
``` | ||
gulp build --modules="wurflRtdProvider,appnexusBidAdapter,..." | ||
``` | ||
|
||
### Configuration | ||
|
||
Use `setConfig` to instruct Prebid.js to initilize the WURFL RTD module, as specified below. | ||
|
||
This module is configured as part of the `realTimeData.dataProviders` | ||
|
||
```javascript | ||
var TIMEOUT = 1000; | ||
pbjs.setConfig({ | ||
realTimeData: { | ||
auctionDelay: TIMEOUT, | ||
dataProviders: [{ | ||
name: 'wurfl', | ||
waitForIt: true, | ||
params: { | ||
debug: false | ||
} | ||
}] | ||
} | ||
}); | ||
``` | ||
|
||
### Parameters | ||
|
||
| Name | Type | Description | Default | | ||
| :------------------------ | :------------ | :--------------------------------------------------------------- |:----------------- | | ||
| name | String | Real time data module name | Always 'wurfl' | | ||
| waitForIt | Boolean | Should be `true` if there's an `auctionDelay` defined (optional) | `false` | | ||
| params | Object | | | | ||
| params.altHost | String | Alternate host to connect to WURFL.js | | | ||
| params.debug | Boolean | Enable debug | `false` | | ||
|
||
## Testing | ||
|
||
To view an example of how the WURFL RTD module works : | ||
|
||
`gulp serve --modules=wurflRtdProvider,appnexusBidAdapter` | ||
|
||
and then point your browser at: | ||
|
||
`http://localhost:9999/integrationExamples/gpt/wurflRtdProvider_example.html` |
Oops, something went wrong.