-
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
Sovrn 1.0 compliance #1796
Sovrn 1.0 compliance #1796
Changes from 18 commits
1a04f1f
bf9478b
75337df
62d965a
60b2be6
97b89cc
f69f45b
60f4d1e
0338ce7
a02635b
5af81a8
9bd1397
07b4dc6
dbfae1e
1c4016f
a497aa8
a5689a1
d261720
17ada25
72a6cf2
9680417
5638b98
bb55cea
fb34ecb
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,156 +1,83 @@ | ||
var CONSTANTS = require('src/constants.json'); | ||
var utils = require('src/utils.js'); | ||
var bidfactory = require('src/bidfactory.js'); | ||
var bidmanager = require('src/bidmanager.js'); | ||
var adloader = require('src/adloader'); | ||
var adaptermanager = require('src/adaptermanager'); | ||
|
||
/** | ||
* Adapter for requesting bids from Sovrn | ||
*/ | ||
var SovrnAdapter = function SovrnAdapter() { | ||
var sovrnUrl = 'ap.lijit.com/rtb/bid'; | ||
|
||
function _callBids(params) { | ||
var sovrnBids = params.bids || []; | ||
|
||
_requestBids(sovrnBids); | ||
} | ||
|
||
function _requestBids(bidReqs) { | ||
// build bid request object | ||
var domain = window.location.host; | ||
var page = window.location.pathname + location.search + location.hash; | ||
|
||
var sovrnImps = []; | ||
|
||
// build impression array for sovrn | ||
import * as utils from 'src/utils'; | ||
import { registerBidder } from 'src/adapters/bidderFactory'; | ||
import { BANNER } from 'src/mediaTypes'; | ||
import { REPO_AND_VERSION } from 'src/constants'; | ||
|
||
export const spec = { | ||
code: 'sovrn', | ||
supportedMediaTypes: [BANNER], | ||
|
||
/** | ||
* Check if the bid is a valid zone ID in either number or string form | ||
* @param {object} bid the Sovrn bid to validate | ||
* @return boolean for whether or not a bid is valid | ||
*/ | ||
isBidRequestValid: function(bid) { | ||
return !!(bid.params.tagid && !isNaN(parseFloat(bid.params.tagid)) && isFinite(bid.params.tagid)); | ||
}, | ||
|
||
/** | ||
* Format the bid request object for our endpoint | ||
* @param {BidRequest[]} bidRequests Array of Sovrn bidders | ||
* @return object of parameters for Prebid AJAX request | ||
*/ | ||
buildRequests: function(bidReqs) { | ||
let sovrnImps = []; | ||
utils._each(bidReqs, function (bid) { | ||
var tagId = utils.getBidIdParameter('tagid', bid.params); | ||
var bidFloor = utils.getBidIdParameter('bidfloor', bid.params); | ||
var adW = 0; | ||
var adH = 0; | ||
|
||
// sovrn supports only one size per tagid, so we just take the first size if there are more | ||
// if we are a 2 item array of 2 numbers, we must be a SingleSize array | ||
var bidSizes = Array.isArray(bid.params.sizes) ? bid.params.sizes : bid.sizes; | ||
var sizeArrayLength = bidSizes.length; | ||
if (sizeArrayLength === 2 && typeof bidSizes[0] === 'number' && typeof bidSizes[1] === 'number') { | ||
adW = bidSizes[0]; | ||
adH = bidSizes[1]; | ||
} else { | ||
adW = bidSizes[0][0]; | ||
adH = bidSizes[0][1]; | ||
} | ||
|
||
var imp = | ||
{ | ||
id: bid.bidId, | ||
banner: { | ||
w: adW, | ||
h: adH | ||
}, | ||
tagid: tagId, | ||
bidfloor: bidFloor | ||
}; | ||
sovrnImps.push(imp); | ||
sovrnImps.push({ | ||
id: bid.bidId, | ||
banner: { w: 1, h: 1 }, | ||
tagid: utils.getBidIdParameter('tagid', bid.params), | ||
bidfloor: utils.getBidIdParameter('bidfloor', bid.params) | ||
}); | ||
}); | ||
|
||
// build bid request with impressions | ||
var sovrnBidReq = { | ||
const sovrnBidReq = { | ||
id: utils.getUniqueIdentifierStr(), | ||
imp: sovrnImps, | ||
site: { | ||
domain: domain, | ||
page: page | ||
domain: window.location.host, | ||
page: window.location.pathname + location.search + location.hash | ||
} | ||
}; | ||
|
||
var scriptUrl = '//' + sovrnUrl + '?callback=window.$$PREBID_GLOBAL$$.sovrnResponse' + | ||
'&src=' + CONSTANTS.REPO_AND_VERSION + | ||
'&br=' + encodeURIComponent(JSON.stringify(sovrnBidReq)); | ||
adloader.loadScript(scriptUrl); | ||
} | ||
|
||
function addBlankBidResponses(impidsWithBidBack) { | ||
var missing = utils.getBidderRequestAllAdUnits('sovrn'); | ||
if (missing) { | ||
missing = missing.bids.filter(bid => impidsWithBidBack.indexOf(bid.bidId) < 0); | ||
} else { | ||
missing = []; | ||
} | ||
|
||
missing.forEach(function (bidRequest) { | ||
// Add a no-bid response for this bid request. | ||
var bid = {}; | ||
bid = bidfactory.createBid(2, bidRequest); | ||
bid.bidderCode = 'sovrn'; | ||
bidmanager.addBidResponse(bidRequest.placementCode, bid); | ||
}); | ||
} | ||
|
||
// expose the callback to the global object: | ||
$$PREBID_GLOBAL$$.sovrnResponse = function (sovrnResponseObj) { | ||
var impidsWithBidBack = []; | ||
|
||
// valid response object from sovrn | ||
if (sovrnResponseObj && sovrnResponseObj.id && sovrnResponseObj.seatbid && sovrnResponseObj.seatbid.length !== 0 && | ||
sovrnResponseObj.seatbid[0].bid && sovrnResponseObj.seatbid[0].bid.length !== 0) { | ||
sovrnResponseObj.seatbid[0].bid.forEach(function (sovrnBid) { | ||
var responseCPM; | ||
var placementCode = ''; | ||
var id = sovrnBid.impid; | ||
var bid = {}; | ||
|
||
var bidObj = utils.getBidRequest(id); | ||
|
||
if (bidObj) { | ||
placementCode = bidObj.placementCode; | ||
bidObj.status = CONSTANTS.STATUS.GOOD; | ||
|
||
responseCPM = parseFloat(sovrnBid.price); | ||
|
||
if (responseCPM !== 0) { | ||
sovrnBid.placementCode = placementCode; | ||
sovrnBid.size = bidObj.sizes; | ||
var responseAd = sovrnBid.adm; | ||
|
||
// build impression url from response | ||
var responseNurl = '<img src="' + sovrnBid.nurl + '">'; | ||
|
||
// store bid response | ||
// bid status is good (indicating 1) | ||
bid = bidfactory.createBid(1, bidObj); | ||
bid.creative_id = sovrnBid.id; | ||
bid.bidderCode = 'sovrn'; | ||
bid.cpm = responseCPM; | ||
|
||
// set ad content + impression url | ||
// sovrn returns <script> block, so use bid.ad, not bid.adurl | ||
bid.ad = decodeURIComponent(responseAd + responseNurl); | ||
|
||
// Set width and height from response now | ||
bid.width = parseInt(sovrnBid.w); | ||
bid.height = parseInt(sovrnBid.h); | ||
|
||
if (sovrnBid.dealid) { | ||
bid.dealId = sovrnBid.dealid; | ||
} | ||
|
||
bidmanager.addBidResponse(placementCode, bid); | ||
impidsWithBidBack.push(id); | ||
} | ||
} | ||
return { | ||
method: 'POST', | ||
url: `//ap.lijit.com/rtb/bid?src=${REPO_AND_VERSION}`, | ||
data: JSON.stringify(sovrnBidReq), | ||
options: {contentType: 'text/plain'} | ||
}; | ||
}, | ||
|
||
/** | ||
* Format Sovrn responses as Prebid bid responses | ||
* @param {id, seatbid} sovrnResponse A successful response from Sovrn. | ||
* @return {Bid[]} An array of formatted bids. | ||
*/ | ||
interpretResponse: function({id, seatbid}) { | ||
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. #1748 changed the first argument of {
body: responseBody,
headers: {
get: function(header) { /* returns a header from the HTTP response */ }
}
} so destructuring like {body: {id, seatbid}} or however you'd prefer to grab 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. Thanks @matthewlane for the info here. Just so I am on the same page, destructuring as you have suggested will lead to the same functionality as what we currently have implemented? I am just making sure that we do not have to change the bid response's JSON structure on our end. 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 right, updating in this way will give the same functionality as before #1748 was merged to master, and there are no changes required to the JSON structure sent from your endpoint. For a (non-destructuring) example, appnexusAst was updated here to pick the response data off the Let me know if you have more questions, and I'll verify the change works whenever the PR is updated |
||
let sovrnBidResponses = []; | ||
if (id && | ||
seatbid && | ||
seatbid.length > 0 && | ||
seatbid[0].bid && | ||
seatbid[0].bid.length > 0) { | ||
seatbid[0].bid.map(sovrnBid => { | ||
sovrnBidResponses.push({ | ||
requestId: sovrnBid.impid, | ||
bidderCode: spec.code, | ||
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.
|
||
cpm: parseFloat(sovrnBid.price), | ||
width: parseInt(sovrnBid.w), | ||
height: parseInt(sovrnBid.h), | ||
creativeId: sovrnBid.id, | ||
dealId: sovrnBid.dealId || null, | ||
currency: 'USD', | ||
netRevenue: true, | ||
mediaType: BANNER, | ||
ad: decodeURIComponent(`${sovrnBid.adm}<img src="${sovrnBid.nurl}">`), | ||
ttl: 60000 | ||
}); | ||
}); | ||
} | ||
addBlankBidResponses(impidsWithBidBack); | ||
}; | ||
|
||
return { | ||
callBids: _callBids | ||
}; | ||
return sovrnBidResponses; | ||
} | ||
}; | ||
|
||
adaptermanager.registerBidAdapter(new SovrnAdapter(), 'sovrn'); | ||
|
||
module.exports = SovrnAdapter; | ||
registerBidder(spec); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# Overview | ||
|
||
``` | ||
Module Name: Sovrn Bid Adapter | ||
Module Type: Bidder Adapter | ||
Maintainer: trand@sovrn.com | ||
``` | ||
|
||
# Description | ||
|
||
Sovrn's adapter integration to the Prebid library. Posts plain-text JSON to the /rtb/bid endpoint. | ||
|
||
# Test Parameters | ||
|
||
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 line and line 46 should mark the code with ``` |
||
var adUnits = [ | ||
{ | ||
code: 'test-leaderboard', | ||
sizes: [[728, 90]], | ||
bids: [{ | ||
bidder: 'sovrn', | ||
params: { | ||
tagid: '403370', | ||
bidfloor: 0.01 | ||
} | ||
}] | ||
}, { | ||
code: 'test-banner', | ||
sizes: [[300, 250]], | ||
bids: [{ | ||
bidder: 'sovrn', | ||
params: { | ||
tagid: '403401' | ||
} | ||
}] | ||
}, { | ||
code: 'test-sidebar', | ||
size: [[160, 600]], | ||
bids: [{ | ||
bidder: 'sovrn', | ||
params: { | ||
tagid: '531000' | ||
} | ||
}] | ||
} | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -66,6 +66,7 @@ import { logWarn, logError, parseQueryStringParameters, delayExecution } from 's | |
* @property {('GET'|'POST')} method The type of request which this is. | ||
* @property {string} url The endpoint for the request. For example, "//bids.example.com". | ||
* @property {string|object} data Data to be sent in the request. | ||
* @property {object} options Content-Type set in the header of the bid request, overrides default 'text/plain'. | ||
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. Proposed changes to core modules are welcome but should be made in a separate PR. Also calls from 1.0 bidders will have Edit: oh this is already in master via #1681? GitHub shows these files as changed for some reason 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. yeah sorry I couldn't figure out why that was happening either. |
||
* If this is a GET request, they'll become query params. If it's a POST request, they'll be added to the body. | ||
* Strings will be added as-is. Objects will be unpacked into query params based on key/value mappings, or | ||
* JSON-serialized into the Request body. | ||
|
@@ -233,10 +234,10 @@ export function newBidder(spec) { | |
error: onFailure | ||
}, | ||
undefined, | ||
{ | ||
Object.assign({ | ||
method: 'GET', | ||
withCredentials: true | ||
} | ||
}, request.options) | ||
); | ||
break; | ||
case 'POST': | ||
|
@@ -247,11 +248,11 @@ export function newBidder(spec) { | |
error: onFailure | ||
}, | ||
typeof request.data === 'string' ? request.data : JSON.stringify(request.data), | ||
{ | ||
Object.assign({ | ||
method: 'POST', | ||
contentType: 'text/plain', | ||
withCredentials: true | ||
} | ||
}, request.options) | ||
); | ||
break; | ||
default: | ||
|
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.
@tedrand Is this hardcoded as 1x1 because Sovrn knows the size based on the ID? It seems like the bid.sizes and bid.params.sizes properties will be totally ignored by the adapter. Is that correct?
@mkendall07 If that’s the case, then https://github.com/prebid/Prebid.js/blob/master/modules/sovrnBidAdapter.md should be updated to omit the sizes fields, right?