-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
PAAPI: add top level auction example #11259
Changes from all commits
09ff42e
43eb284
2c302ae
0eafac1
25a8ef0
a24948a
4917330
acc2619
ab4a7a6
209d190
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 |
---|---|---|
@@ -0,0 +1,57 @@ | ||
function logPrefix(scope) { | ||
return [ | ||
`%c PAAPI %c ${scope} %c`, | ||
'color: green; background-color:yellow; border: 1px solid black', | ||
'color: blue; border:1px solid black', | ||
'', | ||
]; | ||
} | ||
|
||
function scoreAd( | ||
adMetadata, | ||
bid, | ||
auctionConfig, | ||
trustedScoringSignals, | ||
browserSignals, | ||
directFromSellerSignals | ||
) { | ||
console.group(...logPrefix('scoreAd'), 'Buyer:', browserSignals.interestGroupOwner); | ||
console.log('Context:', JSON.stringify({ | ||
adMetadata, | ||
bid, | ||
auctionConfig: { | ||
...auctionConfig, | ||
componentAuctions: '[omitted]' | ||
}, | ||
trustedScoringSignals, | ||
browserSignals, | ||
directFromSellerSignals | ||
}, ' ', ' ')); | ||
|
||
const result = { | ||
desirability: bid, | ||
allowComponentAuction: true, | ||
}; | ||
const {bidfloor, bidfloorcur} = auctionConfig.auctionSignals?.prebid || {}; | ||
if (bidfloor) { | ||
if (browserSignals.bidCurrency !== '???' && browserSignals.bidCurrency !== bidfloorcur) { | ||
console.log(`Floor currency (${bidfloorcur}) does not match bid currency (${browserSignals.bidCurrency}), and currency conversion is not yet implemented. Rejecting bid.`); | ||
result.desirability = -1; | ||
} else if (bid < bidfloor) { | ||
console.log(`Bid (${bid}) lower than contextual winner/floor (${bidfloor}). Rejecting bid.`); | ||
result.desirability = -1; | ||
result.rejectReason = 'bid-below-auction-floor'; | ||
} | ||
} | ||
console.log('Result:', result); | ||
console.groupEnd(); | ||
return result; | ||
} | ||
|
||
function reportResult(auctionConfig, browserSignals) { | ||
console.group(...logPrefix('reportResult')); | ||
console.log('Context', JSON.stringify({auctionConfig, browserSignals}, ' ', ' ')); | ||
console.groupEnd(); | ||
sendReportTo(`${auctionConfig.seller}/report/win?${Object.entries(browserSignals).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join('&')}`); | ||
return {}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
<html> | ||
<head> | ||
<script async src="../../../../build/dev/prebid.js"></script> | ||
<script> | ||
// intercept navigator.runAdAuction and print parameters to console | ||
(() => { | ||
var originalRunAdAuction = navigator.runAdAuction; | ||
navigator.runAdAuction = function (...args) { | ||
console.log('%c runAdAuction', 'background: cyan; border: 2px; border-radius: 3px', ...args); | ||
return originalRunAdAuction.apply(navigator, args); | ||
}; | ||
})(); | ||
</script> | ||
|
||
<script async src="https://securepubads.g.doubleclick.net/tag/js/gpt.js"></script> | ||
|
||
<script> | ||
var FAILSAFE_TIMEOUT = 3300; | ||
var PREBID_TIMEOUT = 3000; | ||
var adUnits = [{ | ||
code: 'div-1', | ||
mediaTypes: { | ||
banner: { | ||
sizes: [[300, 250]], | ||
} | ||
}, | ||
ortb2Imp: { | ||
ext: { | ||
ae: 1 | ||
} | ||
}, | ||
bids: [ | ||
{ | ||
bidder: 'openx', | ||
params: { | ||
unit: '538703464', | ||
response_template_name: 'test_banner_ad', | ||
test: true, | ||
delDomain: 'sademo-d.openx.net' | ||
} | ||
}, | ||
|
||
], | ||
} | ||
] | ||
; | ||
|
||
var pbjs = pbjs || {}; | ||
pbjs.que = pbjs.que || []; | ||
|
||
var googletag = googletag || {}; | ||
googletag.cmd = googletag.cmd || []; | ||
googletag.cmd.push(function () { | ||
googletag.pubads().disableInitialLoad(); | ||
}); | ||
|
||
pbjs.que.push(function () { | ||
pbjs.setConfig({ | ||
debug: true, | ||
paapi: { | ||
enabled: true, | ||
gpt: { | ||
autoconfig: false | ||
} | ||
}, | ||
debugging: { | ||
enabled: true, | ||
intercept: [ | ||
{ | ||
when: { | ||
bidder: 'openx', | ||
}, | ||
then: { | ||
cpm: 0.1 | ||
}, | ||
paapi() { | ||
return [ | ||
{ | ||
'seller': 'https://privacysandbox.openx.net', | ||
'decisionLogicURL': 'https://privacysandbox.openx.net/fledge/decision-logic-component.js', | ||
'sellerSignals': { | ||
'floor': 0.01, | ||
'currency': 'USD', | ||
'auctionTimestamp': new Date().getTime(), | ||
'publisherId': '537143056', | ||
'adUnitId': '538703464' | ||
}, | ||
'interestGroupBuyers': [ | ||
'https://privacysandbox.openx.net' | ||
], | ||
'perBuyerSignals': { | ||
'https://privacysandbox.openx.net': { | ||
'bid': 1.5 | ||
} | ||
}, | ||
'sellerCurrency': 'USD' | ||
} | ||
]; | ||
} | ||
} | ||
] | ||
}, | ||
}); | ||
|
||
pbjs.addAdUnits(adUnits); | ||
pbjs.requestBids({ | ||
bidsBackHandler: sendAdserverRequest, | ||
timeout: PREBID_TIMEOUT | ||
}); | ||
}); | ||
|
||
function raa() { | ||
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. does it make sense to have some of this code in a submodule, which might end up being extremely compact on initial commit, but would provide the convenient logical abstraction that a user would pick the PAAPI submodule associated with their choice of TLS? 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. I think so yes. A pub makes 2 decisions.
|
||
return Promise.all( | ||
Object.entries(pbjs.getPAAPIConfig()) | ||
.map(([adUnitCode, auctionConfig]) => { | ||
return navigator.runAdAuction({ | ||
seller: window.location.origin, | ||
decisionLogicURL: new URL('decisionLogic.js', window.location).toString(), | ||
resolveToConfig: false, | ||
...auctionConfig | ||
}).then(urn => { | ||
if (urn) { | ||
// if we have a paapi winner, replace the adunit div | ||
// with an iframe that renders it | ||
const iframe = document.createElement('iframe'); | ||
Object.assign(iframe, { | ||
src: urn, | ||
frameBorder: 0, | ||
scrolling: 'no', | ||
}, auctionConfig.requestedSize); | ||
const div = document.getElementById(adUnitCode); | ||
div.parentElement.insertBefore(iframe, div); | ||
div.remove(); | ||
return true; | ||
} | ||
}); | ||
}) | ||
).then(won => won.every(el => el)); | ||
} | ||
|
||
function sendAdserverRequest() { | ||
if (pbjs.adserverRequestSent) return; | ||
pbjs.adserverRequestSent = true; | ||
raa().then((allPaapi) => { | ||
if (!allPaapi) { | ||
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. this calls gam if paapi doesn't fill, correct? But PAAPI will always fill if we give it the best contextual bid from prebid as its best alternative? Is the below code basically saying 'Let's compete Prebid vs PAAPI, and if neither provide any ad call gam?' 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. I don't think we can give it the best contextual bid. Some bidders may decide to provide "extra" bids for it, but those don't necessarily contain all, or even the same, contextual bids, and Prebid can't control them. 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. That's Anyway, I assume that deleting the 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, this is not a great way to render. I am not sure yet what the render API will look like:
|
||
googletag.cmd.push(function () { | ||
pbjs.que.push(function () { | ||
pbjs.setTargetingForGPTAsync(); | ||
googletag.pubads().refresh(); | ||
}); | ||
}); | ||
} | ||
}); | ||
} | ||
|
||
setTimeout(function () { | ||
sendAdserverRequest(); | ||
}, FAILSAFE_TIMEOUT); | ||
|
||
googletag.cmd.push(function () { | ||
googletag.defineSlot('/19968336/header-bid-tag-0', [[300, 250], [300, 600]], 'div-1').addService(googletag.pubads()); | ||
|
||
googletag.pubads().enableSingleRequest(); | ||
googletag.enableServices(); | ||
}); | ||
</script> | ||
</head> | ||
|
||
<body> | ||
<h2>Standalone PAAPI Prebid.js Example</h2> | ||
<p>Start local server with:</p> | ||
<code>gulp serve-fast --https</code> | ||
<p>Chrome flags:</p> | ||
<code>--enable-features=CookieDeprecationFacilitatedTesting:label/treatment_1.2/force_eligible/true | ||
--privacy-sandbox-enrollment-overrides=https://localhost:9999</code> | ||
<p>Join interest group at <a href="https://privacysandbox.openx.net/fledge/advertiser">https://privacysandbox.openx.net/fledge/advertiser</a> | ||
</p> | ||
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. Can make it fully local with navigator.joinAdInterestGroup({
name: 'prebid-test',
owner: window.location.origin,
biddingLogicURL: (new URL('buyer.js', window.location)).toString(),
ads: [{
renderURL: (new URL('ad.html', window.location)).toString(),
}],
adComponents: []
}, 600); and a short buyer js 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. My intention was to make this as real-world as possible but I couldn't get any adapter that would both work and agree to be in here. Mocking openx' response was a compromise, hopefully temporary. 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. I was not sure if the demo needed to be self-contained. If so, the buyer can be local too, at a small cost of being slightly confusing to share a domain with seller. It's fine either way. Mocking the openx adapter response is also fine - I was not aware this is possible so thanks for showing me how to do it :) |
||
<h5>Div-1</h5> | ||
<div id='div-1' style='min-width: 300px; min-height: 250px;'> | ||
<script type='text/javascript'> | ||
googletag.cmd.push(function () { | ||
googletag.display('div-1'); | ||
}); | ||
</script> | ||
</div> | ||
|
||
</body> | ||
</html> |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
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.
reportResult
should still be defined even if it does not do anything, to avoidCould be something like this
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.
that would replace it with an error about the report call failing :) I am already worried about people copying this demo into production so I'd rather avoid it; the plan is to work with @patmmccann to publish an actual reference implementation separately
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.
Is this about the event url showing up as an error in the Network tab ? It's still useful to look at parameters, but if unwanted can comment out sendReportTo or make it conditional on something like sellerSignals.reportURL