-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GreenbidsAnalyticsAdapter and GreenbidsRtdProvider: Rework greenbids sampling and improve transparency #10792
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,24 @@ | ||
# Overview | ||
#### Registration | ||
|
||
``` | ||
Module Name: Greenbids Analytics Adapter | ||
Module Type: Analytics Adapter | ||
Maintainer: jb@greenbids.ai | ||
``` | ||
The Greenbids Analytics adapter requires setup and approval from the | ||
Greenbids team. Please reach out to our team for more information [greenbids.ai](https://greenbids.ai). | ||
|
||
# Description | ||
#### Analytics Options | ||
|
||
Analytics adapter for Greenbids | ||
{: .table .table-bordered .table-striped } | ||
| Name | Scope | Description | Example | Type | | ||
|-------------|---------|--------------------|-----------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|------------------| | ||
| pbuid | required | The Greenbids Publisher ID | greenbids-publisher-1 | string | | ||
| greenbidsSampling | optional | sampling factor [0-1] (a value of 0.1 will filter 90% of the traffic) | 1.0 | float | | ||
|
||
# Test Parameters | ||
### Example Configuration | ||
|
||
``` | ||
{ | ||
provider: 'greenbids', | ||
options: { | ||
pbuid: "PBUID_FROM_GREENBIDS" | ||
sampling: 1.0 | ||
} | ||
} | ||
``` | ||
```javascript | ||
pbjs.enableAnalytics({ | ||
provider: 'greenbids', | ||
options: { | ||
pbuid: "greenbids-publisher-1" // please contact Greenbids to get a pbuid for yourself | ||
greenbidsSampling: 1.0 | ||
} | ||
}); | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,13 @@ | ||
import { logError } from '../src/utils.js'; | ||
import { logError, deepClone, generateUUID, deepSetValue } from '../src/utils.js'; | ||
import { ajax } from '../src/ajax.js'; | ||
import { submodule } from '../src/hook.js'; | ||
import * as events from '../src/events.js'; | ||
import CONSTANTS from '../src/constants.json'; | ||
|
||
const MODULE_NAME = 'greenbidsRtdProvider'; | ||
const MODULE_VERSION = '1.0.0'; | ||
const MODULE_VERSION = '2.0.0'; | ||
const ENDPOINT = 'https://t.greenbids.ai'; | ||
|
||
const auctionInfo = {}; | ||
const rtdOptions = {}; | ||
|
||
function init(moduleConfig) { | ||
|
@@ -16,22 +17,28 @@ function init(moduleConfig) { | |
return false; | ||
} else { | ||
rtdOptions.pbuid = params?.pbuid; | ||
rtdOptions.targetTPR = params?.targetTPR || 0.99; | ||
rtdOptions.timeout = params?.timeout || 200; | ||
return true; | ||
} | ||
} | ||
|
||
function onAuctionInitEvent(auctionDetails) { | ||
auctionInfo.auctionId = auctionDetails.auctionId; | ||
/* Emitting one billing event per auction */ | ||
events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { | ||
type: 'auction', | ||
billingId: generateUUID(), | ||
auctionId: auctionDetails.auctionId, | ||
vendor: MODULE_NAME | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So the plan is to emit a billing event once per auction no matter what right, even if the adapter did not do something? I am not aware of if there is a scenario where that would happen, like if the greenbids endpoint returns empty or errors. Just checking. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I think billing for each auction makes the most sense because it correlates 1 to 1 with our infra cost, but I updated this to check that the |
||
} | ||
|
||
function getBidRequestData(reqBidsConfigObj, callback, config, userConsent) { | ||
let promise = createPromise(reqBidsConfigObj); | ||
let greenbidsId = generateUUID(); | ||
let promise = createPromise(reqBidsConfigObj, greenbidsId); | ||
promise.then(callback); | ||
} | ||
|
||
function createPromise(reqBidsConfigObj) { | ||
function createPromise(reqBidsConfigObj, greenbidsId) { | ||
return new Promise((resolve) => { | ||
const timeoutId = setTimeout(() => { | ||
resolve(reqBidsConfigObj); | ||
|
@@ -40,32 +47,43 @@ function createPromise(reqBidsConfigObj) { | |
ENDPOINT, | ||
{ | ||
success: (response) => { | ||
processSuccessResponse(response, timeoutId, reqBidsConfigObj); | ||
processSuccessResponse(response, timeoutId, reqBidsConfigObj, greenbidsId); | ||
resolve(reqBidsConfigObj); | ||
}, | ||
error: () => { | ||
clearTimeout(timeoutId); | ||
resolve(reqBidsConfigObj); | ||
}, | ||
}, | ||
createPayload(reqBidsConfigObj), | ||
{ contentType: 'application/json' } | ||
createPayload(reqBidsConfigObj, greenbidsId), | ||
{ | ||
contentType: 'application/json', | ||
customHeaders: { | ||
'Greenbids-Pbuid': rtdOptions.pbuid | ||
} | ||
} | ||
); | ||
}); | ||
} | ||
|
||
function processSuccessResponse(response, timeoutId, reqBidsConfigObj) { | ||
function processSuccessResponse(response, timeoutId, reqBidsConfigObj, greenbidsId) { | ||
clearTimeout(timeoutId); | ||
const responseAdUnits = JSON.parse(response); | ||
|
||
updateAdUnitsBasedOnResponse(reqBidsConfigObj.adUnits, responseAdUnits); | ||
updateAdUnitsBasedOnResponse(reqBidsConfigObj.adUnits, responseAdUnits, greenbidsId); | ||
} | ||
|
||
function updateAdUnitsBasedOnResponse(adUnits, responseAdUnits) { | ||
function updateAdUnitsBasedOnResponse(adUnits, responseAdUnits, greenbidsId) { | ||
adUnits.forEach((adUnit) => { | ||
const matchingAdUnit = findMatchingAdUnit(responseAdUnits, adUnit.code); | ||
if (matchingAdUnit) { | ||
removeFalseBidders(adUnit, matchingAdUnit); | ||
deepSetValue(adUnit, 'ortb2Imp.ext.greenbids', { | ||
greenbidsId: greenbidsId, | ||
keptInAuction: matchingAdUnit.bidders, | ||
isExploration: matchingAdUnit.isExploration | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice, could end up being very valuable information for analytics |
||
if (!matchingAdUnit.isExploration) { | ||
removeFalseBidders(adUnit, matchingAdUnit); | ||
} | ||
} | ||
}); | ||
} | ||
|
@@ -85,14 +103,24 @@ function getFalseBidders(bidders) { | |
.map(([bidder]) => bidder); | ||
} | ||
|
||
function createPayload(reqBidsConfigObj) { | ||
function stripAdUnits(adUnits) { | ||
const stripedAdUnits = deepClone(adUnits); | ||
return stripedAdUnits.map(adUnit => { | ||
adUnit.bids = adUnit.bids.map(bid => { | ||
return { bidder: bid.bidder }; | ||
}); | ||
return adUnit; | ||
}); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like this gets sent to your server, and you just only want bidder name. Obviously I have no idea what your server is doing, but you could just return bids as array of strings instead of array of objects with one Also, there may be quite a lot of other stuff on the adUnit, do you want all of that in your strippedAdUnits also? Like ORTB2Imp stuff, mediaTypes, etc? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting point for the array of strings structure I think we'll keep it that way for the moment with further optimizations coming down the line (like returning only the bidders to filter form the server for instance and not every bidder involved). the |
||
|
||
function createPayload(reqBidsConfigObj, greenbidsId) { | ||
return JSON.stringify({ | ||
auctionId: auctionInfo.auctionId, | ||
version: MODULE_VERSION, | ||
...rtdOptions, | ||
referrer: window.location.href, | ||
prebid: '$prebid.version$', | ||
rtdOptions: rtdOptions, | ||
adUnits: reqBidsConfigObj.adUnits, | ||
greenbidsId: greenbidsId, | ||
adUnits: stripAdUnits(reqBidsConfigObj.adUnits), | ||
}); | ||
} | ||
|
||
|
@@ -105,6 +133,7 @@ export const greenbidsSubmodule = { | |
findMatchingAdUnit: findMatchingAdUnit, | ||
removeFalseBidders: removeFalseBidders, | ||
getFalseBidders: getFalseBidders, | ||
stripAdUnits: stripAdUnits, | ||
}; | ||
|
||
submodule('realTimeData', greenbidsSubmodule); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IF you are only looking for greenbids vendor you prob do not need to set that local
vendor
varAlso, this seems to be setting a module wide
billingId
Which is emitted by Greenbids at
aucitonInit
But it seems you do not send the message to your server until
auctionEnd
Prebid supports running multiple auctions at once, so it is possible this data will not match up together.
If it matters to your server and stuff, you may want to have this function
handleBillable
save to a map ofauctionId => billingId
Then in your
createBidMessage
you grab thebillingId
used for the auction your are sending.Something like:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great feedback, it actually applied to more than
billingId
in the code,greenbidsId
andisSampled
were globally defined as well and not compatible with concurrent auctions. They have now been included in thecachedAuctions
object which fixes this. Thanks for noticing !