forked from prebid/Prebid.js
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b8810b3
commit 982a547
Showing
4 changed files
with
377 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
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,98 @@ | ||
|
||
import { submodule } from '../src/hook.js'; | ||
import { mergeDeep, logError, logMessage, deepSetValue, generateUUID } from '../src/utils.js'; | ||
import { getGlobal } from '../src/prebidGlobal.js'; | ||
|
||
const SUBMODULE_NAME = 'oneKey'; | ||
const prefixLog = 'OneKey.RTD-module' | ||
|
||
// Pre-init OneKey if it has not load yet. | ||
window.OneKey = window.OneKey || {}; | ||
window.OneKey.queue = window.OneKey.queue || []; | ||
|
||
/** | ||
* Generate the OneKey transmission and include it in the Bid Request. | ||
* | ||
* Modify the AdUnit object for each auction. | ||
* It’s called as part of the requestBids hook. | ||
* https://docs.prebid.org/dev-docs/add-rtd-submodule.html#getbidrequestdata | ||
* | ||
* @param {Object} reqBidsConfigObj | ||
* @param {function} callback | ||
* @param {Object} rtdConfig | ||
* @param {Object} userConsent | ||
*/ | ||
const getTransmissionInBidRequest = (reqBidsConfigObj, done, rtdConfig) => { | ||
const adUnits = reqBidsConfigObj.adUnits || getGlobal().adUnits; | ||
const transactionIds = adUnits.map(() => generateUUID()); | ||
|
||
logMessage(prefixLog, 'Queue seed generation.'); | ||
window.OneKey.queue.push(() => { | ||
logMessage(prefixLog, 'Generate a seed.'); | ||
window.OneKey.generateSeed(transactionIds) | ||
.then(onGetSeed(reqBidsConfigObj, rtdConfig, adUnits, transactionIds)) | ||
.catch((err) => { logError(SUBMODULE_NAME, err.message); }) | ||
.finally(done); | ||
}); | ||
} | ||
|
||
const onGetSeed = (reqBidsConfigObj, rtdConfig, adUnits, transactionIds) => { | ||
return (seed) => { | ||
if (!seed) { | ||
logMessage(prefixLog, 'No seed generated.'); | ||
return; | ||
} | ||
|
||
logMessage(prefixLog, 'Has retrieved a seed:', seed); | ||
addTransactionIdsToAdUnits(adUnits, transactionIds); | ||
addTransmissionToOrtb2(reqBidsConfigObj, rtdConfig, seed); | ||
}; | ||
}; | ||
|
||
const addTransactionIdsToAdUnits = (adUnits, transactionIds) => { | ||
adUnits.forEach((unit, index) => { | ||
deepSetValue(unit, `ortb2Imp.ext.data.paf.transaction_id`, transactionIds[index]); | ||
}); | ||
}; | ||
|
||
const addTransmissionToOrtb2 = (reqBidsConfigObj, rtdConfig, seed) => { | ||
const okOrtb2 = { | ||
ortb2: { | ||
user: { | ||
ext: { | ||
paf: { | ||
transmission: { | ||
seed | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
const shareSeedWithAllBidders = !rtdConfig.params || !rtdConfig.params.bidders; | ||
if (shareSeedWithAllBidders) { | ||
// Change global first party data with OneKey | ||
logMessage(prefixLog, 'set ortb2:', okOrtb2); | ||
mergeDeep(reqBidsConfigObj.ortb2Fragments.global, okOrtb2.ortb2); | ||
} else { | ||
// Change bidder-specific first party data with OneKey | ||
logMessage(prefixLog, `set ortb2 for: ${rtdConfig.params.bidders.join(',')}`, okOrtb2); | ||
rtdConfig.params.bidders.forEach(bidder => { | ||
mergeDeep(reqBidsConfigObj.ortb2Fragments.bidder, { [bidder]: okOrtb2.ortb2 }); | ||
}); | ||
} | ||
}; | ||
|
||
/** @type {RtdSubmodule} */ | ||
export const oneKeyDataSubmodule = { | ||
/** | ||
* used to link submodule with realTimeData | ||
* @type {string} | ||
*/ | ||
name: SUBMODULE_NAME, | ||
init: () => true, | ||
getBidRequestData: getTransmissionInBidRequest, | ||
}; | ||
|
||
submodule('realTimeData', oneKeyDataSubmodule); |
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,126 @@ | ||
## OneKey Real-time Data Submodule | ||
|
||
The OneKey real-time data module in Prebid has been built so that publishers | ||
can quickly and easily setup the OneKey Addressability Framework. | ||
This module is used along with the oneKeyIdSystem to pass OneKey data to your partners. | ||
Both modules are required. This module will pass transmission requests to your partners | ||
while the oneKeyIdSystem will pass the oneKeyData. | ||
|
||
Background information: | ||
- [prebid/addressability-framework](https://github.com/prebid/addressability-framework) | ||
- [prebid/paf-mvp-implementation](https://github.com/prebid/paf-mvp-implementation) | ||
|
||
### Publisher Usage | ||
|
||
The OneKey RTD module depends on paf-lib.js existing in the page. | ||
|
||
Compile the OneKey RTD module into your Prebid build: | ||
|
||
`gulp build --modules=userId,oneKeyIdSystem,rtdModule,oneKeyRtdProvider,appnexusBidAdapter` | ||
|
||
Add the OneKey RTD provider to your Prebid config. In this example we will configure | ||
a sample proxyHostName. See the "Parameter Descriptions" below for more detailed information | ||
of the configuration parameters. | ||
|
||
``` | ||
pbjs.setConfig( | ||
... | ||
realTimeData: { | ||
auctionDelay: 5000, | ||
dataProviders: [ | ||
{ | ||
name: "paf", | ||
waitForIt: true, | ||
params: { | ||
proxyHostName: "cmp.pafdemopublisher.com" | ||
} | ||
} | ||
] | ||
} | ||
... | ||
} | ||
``` | ||
|
||
### Parameter Descriptions for the OneKey Configuration Section | ||
|
||
| Name |Type | Description | Notes | | ||
| :------------ | :------------ | :------------ |:------------ | | ||
| name | String | Real time data module name | Always 'oneKey' | | ||
| waitForIt | Boolean | Required to ensure that the auction is delayed until prefetch is complete | Optional. Defaults to false | | ||
| params | Object | | | | ||
| params.proxyHostName | String | servername of the OneKey Proxy which will generate seeds. | Required | | ||
| params.bidders | Array | List of bidders to restrict the data to. | Optional | | ||
|
||
### Data for bidders | ||
|
||
The data will provided to the bidders using the `ortb2` object. You can find the | ||
format of the data at https://github.com/prebid/addressability-framework. | ||
The following is an example of the format of the data: | ||
|
||
```json | ||
"user": { | ||
"ext": { | ||
"paf": { | ||
"transmission": { | ||
"seed": { | ||
"version": "0.1", | ||
"transaction_ids": ["06df6992-691c-4342-bbb0-66d2a005d5b1", "d2cd0aa7-8810-478c-bd15-fb5bfa8138b8"], | ||
"publisher": "cmp.pafdemopublisher.com", | ||
"source": { | ||
"domain": "cmp.pafdemopublisher.com", | ||
"timestamp": 1649712888, | ||
"signature": "turzZlXh9IqD5Rjwh4vWR78pKLrVsmwQrGr6fgw8TPgQVJSC8K3HvkypTV7lm3UaCi+Zzjl+9sd7Hrv87gdI8w==" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
|
||
```json | ||
"ortb2Imp": { | ||
"ext": { | ||
"data": { | ||
"paf": { | ||
"transaction_id": "52d23fed-4f50-4c17-b07a-c458143e9d09" | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
### Bidder Responses | ||
|
||
Bidders who are part of the OneKey Addressability Framework and receive OneKey | ||
transmissions are required to return transmission responses as outlined in | ||
[prebid/addressability-framework](https://github.com/prebid/addressability-framework/blob/main/mvp-spec/ad-auction.md). Transmission responses should be appended to bids | ||
along with the releveant content_id using the meta.paf field. The paf-lib will | ||
be responsible for collecting all of the transmission responses. | ||
|
||
Below is an example of setting a transmission response: | ||
```javascript | ||
bid.meta.paf = { | ||
"content_id": "90141190-26fe-497c-acee-4d2b649c2112", | ||
"transmission": { | ||
"version": "0.1", | ||
"contents": [ | ||
{ | ||
"transaction_id": "f55a401d-e8bb-4de1-a3d2-fa95619393e8", | ||
"content_id": "90141190-26fe-497c-acee-4d2b649c2112" | ||
} | ||
], | ||
"status": "success", | ||
"details": "", | ||
"receiver": "dsp1.com", | ||
"source": { | ||
"domain": "dsp1.com", | ||
"timestamp": 1639589531, | ||
"signature": "d01c6e83f14b4f057c2a2a86d320e2454fc0c60df4645518d993b5f40019d24c" | ||
}, | ||
"children": [] | ||
} | ||
} | ||
``` | ||
|
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,152 @@ | ||
import {oneKeyDataSubmodule} from 'modules/oneKeyRtdProvider.js'; | ||
import {getAdUnits} from '../../fixtures/fixtures.js'; | ||
|
||
const defaultSeed = { | ||
version: '0.1', | ||
transaction_ids: [ | ||
'd566b02a-a6e2-4c87-98dc-f5623cd9e828', | ||
'f7ffe3cc-0d58-4ec4-b687-1d3d410a48fe' | ||
], | ||
publisher: 'cmp.pafdemopublisher.com', | ||
source: { | ||
domain: 'cmp.pafdemopublisher.com', | ||
timestamp: 1657116880, | ||
signature: '6OmdrSGwagPpugGFuQ4VGjzqYadHxWIXPaLItk0vA1lmi/EQyRvNF5seXStfwKWRnC7HZlOIGSjA6g7HAuofWw==' | ||
|
||
} | ||
}; | ||
|
||
const defaultOrb2WithTransmission = { | ||
user: { | ||
ext: { | ||
paf: { | ||
transmission: { | ||
seed: defaultSeed | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
|
||
const defaultRtdConfig = { | ||
params: { | ||
proxyHostName: 'host' | ||
} | ||
}; | ||
|
||
describe('oneKeyDataSubmodule', () => { | ||
var bidsConfig; | ||
beforeEach(() => { | ||
// Fresh bidsConfig because it can be altered | ||
// during the tests. | ||
bidsConfig = getReqBidsConfig(); | ||
setUpOneKey(); | ||
}); | ||
|
||
it('successfully instantiates', () => { | ||
expect(oneKeyDataSubmodule.init()).to.equal(true); | ||
}); | ||
|
||
it('call OneKey API once it is loaded', () => { | ||
const done = sinon.spy(); | ||
|
||
oneKeyDataSubmodule.getBidRequestData(bidsConfig, done, defaultRtdConfig); | ||
|
||
expect(bidsConfig).to.eql(getReqBidsConfig()); | ||
expect(done.callCount).to.equal(0); | ||
expect(window.OneKey.queue.length).to.equal(1); | ||
}); | ||
|
||
it('don\'t change anything without a seed', () => { | ||
window.OneKey.generateSeed = (_transactionIds) => { | ||
return Promise.resolve(undefined); | ||
}; | ||
|
||
// Act | ||
return new Promise(resolve => { | ||
oneKeyDataSubmodule.getBidRequestData(bidsConfig, resolve, defaultRtdConfig); | ||
executeOneKeyQueue(); | ||
}) | ||
|
||
// Assert | ||
.then(() => { | ||
expect(bidsConfig).to.eql(getReqBidsConfig()); | ||
}); | ||
}); | ||
|
||
[ // Test cases | ||
{ | ||
description: 'global orb2', | ||
rtdConfig: defaultRtdConfig, | ||
expectedFragment: { | ||
global: { | ||
...defaultOrb2WithTransmission | ||
}, | ||
bidder: {} | ||
} | ||
}, | ||
|
||
{ | ||
description: 'bidder-specific orb2', | ||
rtdConfig: { | ||
params: { | ||
proxyHostName: 'host', | ||
bidders: [ 'bidder42', 'bidder24' ] | ||
} | ||
}, | ||
expectedFragment: { | ||
global: { }, | ||
bidder: { | ||
bidder42: { | ||
...defaultOrb2WithTransmission | ||
}, | ||
bidder24: { | ||
...defaultOrb2WithTransmission | ||
} | ||
} | ||
} | ||
} | ||
].forEach(testCase => { | ||
it(`update adUnits with transaction-ids and transmission in ${testCase.description}`, () => { | ||
// Act | ||
return new Promise(resolve => { | ||
oneKeyDataSubmodule.getBidRequestData(bidsConfig, resolve, testCase.rtdConfig); | ||
executeOneKeyQueue(); | ||
}) | ||
|
||
// Assert | ||
.then(() => { | ||
// Verify transaction-ids without equality | ||
// because they are generated UUID. | ||
bidsConfig.adUnits.forEach((adUnit) => { | ||
expect(adUnit.ortb2Imp.ext.data.paf.transaction_id).to.not.be.undefined; | ||
}); | ||
expect(bidsConfig.ortb2Fragments).to.eql(testCase.expectedFragment); | ||
}); | ||
}); | ||
}); | ||
}); | ||
|
||
const getReqBidsConfig = () => { | ||
return { | ||
adUnits: getAdUnits(), | ||
ortb2Fragments: { | ||
global: {}, | ||
bidder: {} | ||
} | ||
} | ||
} | ||
|
||
const setUpOneKey = () => { | ||
window.OneKey.queue = []; | ||
OneKey.generateSeed = (_transactionIds) => { | ||
return Promise.resolve(defaultSeed); | ||
}; | ||
} | ||
|
||
const executeOneKeyQueue = () => { | ||
while (window.OneKey.queue.length > 0) { | ||
window.OneKey.queue[0](); | ||
window.OneKey.queue.shift(); | ||
} | ||
} |