Skip to content
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

Added adapter for Conversant Media #708

Merged
merged 6 commits into from
Nov 2, 2016
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions adapters.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"aol",
"appnexus",
"appnexusAst",
"conversant",
"getintent",
"hiromedia",
"indexExchange",
Expand Down
230 changes: 230 additions & 0 deletions src/adapters/conversant.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
'use strict';
var VERSION = '2.0.0',
CONSTANTS = require('../constants.json'),
utils = require('../utils.js'),
bidfactory = require('../bidfactory.js'),
bidmanager = require('../bidmanager.js'),
adloader = require('../adloader');

/**
* Adapter for requesting bids from Conversant
*/
var ConversantAdapter = function () {
var w = window,
n = navigator;

// production endpoint
var conversantUrl = '//media.msg.dotomi.com/s2s/header?callback=$$PREBID_GLOBAL$$.conversantResponse';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@msahagu2 can you request an Access-Control-Allow-Origin: * header for this endpoint?

screen shot 2016-11-23 at 12 14 53 am

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know what ad sizes are requested? We're currently working on a fix for the headers of our 204 responses

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am told by T. Rex at Conversant that the site_id numeric value must be passed as a string (buh?) so can you catch this case in the adapter @msahagu2


// SSAPI returns JSONP with window.pbjs.conversantResponse as the cb
var appendScript = function (code) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.className = 'cnvr-response';

try {
script.appendChild(document.createTextNode(code));
document.getElementsByTagName('head')[0].appendChild(script);
} catch (e) {
script.text = code;
document.getElementsByTagName('head')[0].appendChild(script);
}
};

var httpPOSTAsync = function (url, data) {
var xmlHttp = new w.XMLHttpRequest();

xmlHttp.onload = function () {
appendScript(xmlHttp.responseText);
};
xmlHttp.open('POST', url, true); // true for asynchronous
xmlHttp.withCredentials = true;
xmlHttp.send(data);
};

var getDNT = function () {
return n.doNotTrack === '1' || w.doNotTrack === '1' || n.msDoNotTrack === '1' || n.doNotTrack === 'yes';
};

var getDevice = function () {
return {
h: screen.height,
w: screen.width,
dnt: getDNT() ? 1 : 0,
language: n.language.split('-')[0],
make: n.vendor ? n.vendor : '',
ua: n.userAgent
};
};

var callBids = function (params) {
var conversantBids = params.bids || [];
requestBids(conversantBids);
};

var requestBids = function (bidReqs) {
// build bid request object
var page = location.pathname + location.search + location.hash,
siteId = '',
conversantImps = [],
conversantBidReqs,
secure = 0;

//build impression array for conversant
utils._each(bidReqs, function (bid) {
var bidfloor = utils.getBidIdParamater('bidloor', bid.params),
sizeArrayLength = bid.sizes.length,
adW = 0,
adH = 0,
imp;

secure = utils.getBidIdParamater('secure', bid.params) ? 1 : secure;
siteId = utils.getBidIdParamater('site_id', bid.params);

if (sizeArrayLength === 2 && typeof bid.sizes[0] === 'number' && typeof bid.sizes[1] === 'number') {
adW = bid.sizes[0];
adH = bid.sizes[1];
} else {
adW = bid.sizes[0][0];
adH = bid.sizes[0][1];
}

imp = {
id: bid.bidId,
banner: {
w: adW,
h: adH
},
secure: secure,
bidfloor: bidfloor ? bidfloor : 0,
displaymanager: 'Prebid.js',
displaymanagerver: VERSION
};

conversantImps.push(imp);
});

conversantBidReqs = {
'id': utils.getUniqueIdentifierStr(),
'imp': conversantImps,

'site': {
'id': siteId,
'mobile': document.querySelector('meta[name="viewport"][content*="width=device-width"]') !== null ? 1 : 0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the presence of this meta tag a reliable way to detect mobile clients? Our responsive site at washingtonian.com includes this meta tag for everyone.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mobile field is there to determine if the site's layout can be optimized when on mobile clients. Not necessarily that they are mobile

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thank you @msahagu2! 👍

'page': page
},

'device': getDevice(),
'at': 1
};

var url = secure ? 'https:' + conversantUrl : location.protocol + conversantUrl;
httpPOSTAsync(url, JSON.stringify(conversantBidReqs));
};

var addEmptyBidResponses = function (placementsWithBidsBack) {
var allConversantBidRequests = $$PREBID_GLOBAL$$._bidsRequested.find(bidSet => bidSet.bidderCode === 'conversant');

if (allConversantBidRequests && allConversantBidRequests.bids){
utils._each(allConversantBidRequests.bids, function (conversantBid) {
if (!utils.contains(placementsWithBidsBack, conversantBid.placementCode)) {
// Add a no-bid response for this placement.
var bid = bidfactory.createBid(2, conversantBid);
bid.bidderCode = 'conversant';
bidmanager.addBidResponse(conversantBid.placementCode, bid);
}
});
}
};

var parseSeatbid = function (bidResponse) {
var placementsWithBidsBack = [];
utils._each(bidResponse.bid, function (conversantBid) {
var responseCPM,
placementCode = '',
id = conversantBid.impid,
bid = {},
responseAd,
responseNurl,
sizeArrayLength;

// Bid request we sent Conversant
var bidRequested = $$PREBID_GLOBAL$$._bidsRequested.find(bidSet => bidSet.bidderCode === 'conversant').bids.find(bid => bid.bidId === id);

if (bidRequested) {
placementCode = bidRequested.placementCode;
placementsWithBidsBack.push(placementCode);
bidRequested.status = CONSTANTS.STATUS.GOOD;

responseCPM = parseFloat(conversantBid.price);

if (responseCPM !== 0.0) {
conversantBid.placementCode = placementCode;
conversantBid.size = bidRequested.sizes;
responseAd = conversantBid.adm || '';
responseNurl = conversantBid.nurl || '';

// Our bid!
bid = bidfactory.createBid(1);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pass the bid object as 2nd argument to sync bid request and response IDs. See #509

bid.creative_id = conversantBid.id || '';
bid.bidderCode = 'conversant';

bid.cpm = responseCPM;

// Track impression image onto returned html
bid.ad = responseAd + '<img src=\"' + responseNurl + '\" />';

sizeArrayLength = bidRequested.sizes.length;
if (sizeArrayLength === 2 && typeof bidRequested.sizes[0] === 'number' && typeof bidRequested.sizes[1] === 'number') {
bid.width = bidRequested.sizes[0];
bid.height = bidRequested.sizes[1];
} else {
bid.width = bidRequested.sizes[0][0];
bid.height = bidRequested.sizes[0][1];
}

bidmanager.addBidResponse(placementCode, bid);
} else {
//0 price bid
//indicate that there is no bid for this placement
bid = bidfactory.createBid(2);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pass the bid object as 2nd argument to sync bid request and response IDs. See #509

bid.bidderCode = 'conversant';
bidmanager.addBidResponse(placementCode, bid);
}
}
});
addEmptyBidResponses(placementsWithBidsBack);
};

// Register our callback to the global object:
$$PREBID_GLOBAL$$.conversantResponse = function (conversantResponseObj, path) {
// valid object?
if (conversantResponseObj && conversantResponseObj.id) {
if (conversantResponseObj.seatbid && conversantResponseObj.seatbid.length > 0 && conversantResponseObj.seatbid[0].bid && conversantResponseObj.seatbid[0].bid.length > 0) {
utils._each(conversantResponseObj.seatbid, parseSeatbid);
} else {
//no response data for any placements
addEmptyBidResponses([]);
}
} else {
//no response data for any placements
addEmptyBidResponses([]);
}
// for debugging purposes
if (path){
adloader.loadScript(path, function () {
var allConversantBidRequests = $$PREBID_GLOBAL$$._bidsRequested.find(bidSet => bidSet.bidderCode === 'conversant');

if ($$PREBID_GLOBAL$$.conversantDebugResponse){
$$PREBID_GLOBAL$$.conversantDebugResponse(allConversantBidRequests);
}
});
}
}; // conversantResponse

return {
callBids: callBids
};
};

module.exports = ConversantAdapter;
Loading