-
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
New Cox adapter #1228
New Cox adapter #1228
Changes from all commits
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 |
---|---|---|
|
@@ -68,6 +68,7 @@ | |
"trion", | ||
"prebidServer", | ||
"adsupply", | ||
"cox", | ||
{ | ||
"appnexus": { | ||
"alias": "brealtime" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
var bidfactory = require('../bidfactory.js'); | ||
var bidmanager = require('../bidmanager.js'); | ||
var adLoader = require('../adloader.js'); | ||
|
||
var CoxAdapter = function CoxAdapter() { | ||
var adZoneAttributeKeys = ['id', 'size', 'thirdPartyClickUrl'], | ||
otherKeys = ['siteId', 'wrapper', 'referrerUrl'], | ||
placementMap = {}, | ||
W = window; | ||
|
||
var COX_BIDDER_CODE = 'cox'; | ||
|
||
function _callBids(params) { | ||
var env = ''; | ||
|
||
// Create global cdsTag and CMT object (for the latter, only if needed ) | ||
W.cdsTag = {}; | ||
if (!W.CMT) W.CMT = _getCoxLite(); | ||
|
||
// Populate the tag with the info from prebid | ||
var bids = params.bids || [], | ||
tag = W.cdsTag, | ||
i, | ||
j; | ||
for (i = 0; i < bids.length; i++) { | ||
var bid = bids[i], | ||
cfg = bid.params || {}; | ||
|
||
if (cfg.id) { | ||
tag.zones = tag.zones || {}; | ||
var zone = {}; | ||
|
||
for (j = 0; j < adZoneAttributeKeys.length; j++) { | ||
if (cfg[adZoneAttributeKeys[j]]) zone[adZoneAttributeKeys[j]] = cfg[adZoneAttributeKeys[j]]; | ||
} | ||
for (j = 0; j < otherKeys.length; j++) { | ||
if (cfg[otherKeys[j]]) tag[otherKeys[j]] = cfg[otherKeys[j]]; | ||
} | ||
var adZoneKey = 'as' + cfg.id; | ||
tag.zones[adZoneKey] = zone; | ||
|
||
// Check for an environment setting | ||
if (cfg.env) env = cfg.env; | ||
|
||
// Update the placement map | ||
var xy = (cfg.size || '0x0').split('x'); | ||
placementMap[adZoneKey] = { | ||
p: bid.placementCode, | ||
w: xy[0], | ||
h: xy[1] | ||
}; | ||
} | ||
} | ||
if (tag.zones && Object.keys(tag.zones).length > 0) { | ||
tag.__callback__ = function (r) { | ||
tag.response = r; | ||
_notify(); | ||
}; | ||
adLoader.loadScript(W.CMT.Service.buildSrc(tag, env)); | ||
} | ||
} | ||
|
||
function _notify() { | ||
// Will execute in the context of a bid | ||
// function finalizeAd(price) { | ||
// this.ad = W.CMT.Service.setAuctionPrice(this.ad, price); | ||
// return this; | ||
// } | ||
|
||
for (var adZoneKey in placementMap) { | ||
var bid = W.CMT.Service.getBidTrue(adZoneKey), | ||
bidObj, | ||
data = placementMap[adZoneKey]; | ||
|
||
if (bid > 0) { | ||
bidObj = bidfactory.createBid(1); | ||
bidObj.cpm = bid; | ||
bidObj.ad = W.CMT.Service.getAd(adZoneKey); | ||
bidObj.width = data.w; | ||
bidObj.height = data.h; | ||
// bidObj.floor = W.CMT.Service.getSecondPrice(adZoneKey); | ||
// bidObj.finalizeAd = finalizeAd; | ||
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. More commented code for potential removal |
||
} else { | ||
bidObj = bidfactory.createBid(2); | ||
} | ||
bidObj.bidderCode = COX_BIDDER_CODE; | ||
bidmanager.addBidResponse(data.p, bidObj); | ||
} | ||
} | ||
|
||
function _getCoxLite() { | ||
var CMT = {}; | ||
|
||
CMT.Util = (function () { | ||
return { | ||
|
||
getRand: function getRand() { | ||
return Math.round(Math.random() * 100000000); | ||
}, | ||
|
||
encodeUriObject: function encodeUriObject(obj) { | ||
return encodeURIComponent(JSON.stringify(obj)); | ||
}, | ||
|
||
extractUrlInfo: function extractUrlInfo() { | ||
function f2(callback) { | ||
try { | ||
if (!W.location.ancestorOrigins) return; | ||
for (var i = 0, len = W.location.ancestorOrigins.length; len > i; i++) { | ||
callback.call(null, W.location.ancestorOrigins[i], i); | ||
} | ||
} catch (ignore) { } | ||
return []; | ||
} | ||
|
||
function f1(callback) { | ||
var oneWindow, | ||
infoArray = []; | ||
do { | ||
try { | ||
oneWindow = oneWindow ? oneWindow.parent : W; | ||
callback.call(null, oneWindow, infoArray); | ||
} catch (t) { | ||
infoArray.push({ | ||
referrer: null, | ||
location: null, | ||
isTop: !1 | ||
}); | ||
return infoArray; | ||
} | ||
} while (oneWindow !== W.top); | ||
return infoArray; | ||
} | ||
var allInfo = f1(function (oneWindow, infoArray) { | ||
try { | ||
infoArray.push({ referrer: oneWindow.document.referrer || null, location: oneWindow.location.href || null, isTop: oneWindow === W.top }); | ||
} catch (e) { | ||
infoArray.push({ referrer: null, location: null, isTop: oneWindow === W.top }); | ||
} | ||
}); | ||
f2(function (n, r) { | ||
allInfo[r].ancestor = n; | ||
}); | ||
for (var t = '', e = !1, i = allInfo.length - 1, l = allInfo.length - 1; l >= 0; l--) { | ||
t = allInfo[l].location; | ||
if (!t && l > 0) { | ||
t = allInfo[l - 1].referrer; | ||
if (!t) t = allInfo[l - 1].ancestor; | ||
if (t) { | ||
e = W.location.ancestorOrigins ? !0 : l === allInfo.length - 1 && allInfo[allInfo.length - 1].isTop; | ||
break; | ||
} | ||
} | ||
} return { url: t, isTop: e, depth: i }; | ||
}, | ||
|
||
srTestCapabilities: function srTestCapabilities() { | ||
var plugins = navigator.plugins, | ||
flashVer = -1, | ||
sf = 'Shockwave Flash'; | ||
|
||
if (plugins && plugins.length > 0) { | ||
if (plugins[sf + ' 2.0'] || plugins[sf]) { | ||
var swVer2 = plugins[sf + ' 2.0'] ? ' 2.0' : ''; | ||
var flashDescription = plugins[sf + swVer2].description; | ||
flashVer = flashDescription.split(' ')[2].split('.')[0]; | ||
} | ||
} | ||
if (flashVer > 4) return 15; else return 7; | ||
} | ||
|
||
}; | ||
}()); | ||
|
||
// Ad calling functionality | ||
CMT.Service = (function () { | ||
// Closure variables shared by the service functions | ||
var U = CMT.Util; | ||
|
||
return { | ||
|
||
buildSrc: function buildSrc(tag, env) { | ||
var src = (document.location.protocol === 'https:' ? 'https://' : 'http://') + (!env || env === 'PRD' ? '' : env === 'PPE' ? 'ppe-' : env === 'STG' ? 'staging-' : '') + 'ad.afy11.net/ad' + '?mode=11' + '&ct=' + U.srTestCapabilities() + '&nif=0' + '&sf=0' + '&sfd=0' + '&ynw=0' + '&rand=' + U.getRand() + '&hb=1' + '&rk1=' + U.getRand() + '&rk2=' + new Date().valueOf() / 1000; | ||
|
||
// Make sure we don't have a response object... | ||
delete tag.response; | ||
|
||
// Extracted url info... | ||
var urlInfo = U.extractUrlInfo(); | ||
tag.pageUrl = urlInfo.url; | ||
tag.puTop = urlInfo.isTop; | ||
|
||
// Attach the serialized tag to our string | ||
src += '&ab=' + U.encodeUriObject(tag); | ||
|
||
return src; | ||
}, | ||
|
||
getAd: function (zoneKey) { | ||
if (!zoneKey) return; | ||
|
||
return this._getData(zoneKey, 'ad') + (this._getResponse().tpCookieSync || ''); // ...also append cookie sync if present | ||
}, | ||
|
||
// getSecondPrice: function getSecondPrice(zoneKey) { | ||
// if (zoneKey.substring(0, 2) !== 'as') zoneKey = 'as' + zoneKey; | ||
// var bid = this.getBidTrue(zoneKey), | ||
// floor = this._getData(zoneKey, 'floor'); | ||
|
||
// // If no floor, just set it to 80% of the bid | ||
// if (!floor) floor = bid * 0.80; | ||
|
||
// // Adjust the floor if it's too high...it needs to always be lower | ||
// if (floor >= bid) { | ||
// floor = floor * 0.80; // Take off 20% to account for possible non-adjusted 2nd highest bid | ||
|
||
// // If it's still too high, just take 80% to 90% of the bid | ||
// if (floor >= bid) floor = bid * ((Math.random() * 10) + 80) / 100; | ||
// } | ||
// return Math.round(floor * 100) / 100; | ||
// }, | ||
|
||
// setAuctionPrice: function setAuctionPrice(ad, bid) { | ||
// return ad ? ad.replace('${AUCTION_PRICE}', bid) : ad; | ||
// }, | ||
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. Is this all required at some point? 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. We have a partner that utilizes the commented-out code. We'd rather keep it in there for completeness' sake. But if y'all would prefer it be removed, we'll be happy to do so. Please let me know. Thanks! |
||
|
||
getBidTrue: function getBidTrue(zoneKey) { | ||
return Math.round(this._getData(zoneKey, 'price') * 100) / 100; | ||
}, | ||
|
||
_getData: function (zoneKey, field) { | ||
var response = this._getResponse(), | ||
zoneResponseData = response.zones ? response.zones[zoneKey] : {}; | ||
|
||
return (zoneResponseData || {})[field] || null; | ||
}, | ||
|
||
_getResponse: function () { | ||
var tag = W.cdsTag; | ||
return (tag && tag.response) ? tag.response : {}; | ||
}, | ||
}; | ||
}()); | ||
|
||
return CMT; | ||
} | ||
|
||
// Export the callBids function, so that prebid.js can execute this function | ||
// when the page asks to send out bid requests. | ||
return { | ||
callBids: _callBids, | ||
}; | ||
}; | ||
|
||
module.exports = CoxAdapter; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import Adapter from 'src/adapters/cox'; | ||
import bidManager from 'src/bidmanager'; | ||
import adLoader from 'src/adloader'; | ||
import utils from 'src/utils'; | ||
import {expect} from 'chai'; | ||
|
||
describe('CoxAdapter', () => { | ||
let adapter; | ||
let loadScriptStub; | ||
let addBidResponseSpy; | ||
|
||
let emitScript = (script) => { | ||
let node = document.createElement('script'); | ||
node.type = 'text/javascript'; | ||
node.appendChild(document.createTextNode(script)); | ||
document.getElementsByTagName('head')[0].appendChild(node); | ||
}; | ||
|
||
beforeEach(() => { | ||
adapter = new Adapter(); | ||
addBidResponseSpy = sinon.spy(bidManager, 'addBidResponse'); | ||
}); | ||
|
||
afterEach(() => { | ||
loadScriptStub.restore(); | ||
addBidResponseSpy.restore(); | ||
}); | ||
|
||
describe('response handling', () => { | ||
const normalResponse = 'cdsTag.__callback__({"zones":{"as2000005991707":{"ad" : "<h1>FOO<\/h1>","uid" : "","price" : 1.51,"floor" : 0,}},"tpCookieSync":"<h1>FOOKIE<\/h1>"})'; | ||
const zeroPriceResponse = 'cdsTag.__callback__({"zones":{"as2000005991707":{"ad" : "<h1>DEFAULT FOO<\/h1>","uid" : "","price" : 0,"floor" : 0,}},"tpCookieSync":"<h1>FOOKIE<\/h1>"})'; | ||
const incompleteResponse = 'cdsTag.__callback__({"zones":{},"tpCookieSync":"<h1>FOOKIE<\/h1>"})'; | ||
|
||
const oneBidConfig = { | ||
bidderCode: 'cox', | ||
bids: [{ | ||
bidder: 'cox', | ||
placementCode: 'FOO456789', | ||
sizes: [300, 250], | ||
params: { size: '300x250', id: 2000005991707, siteId: 2000100948180, env: 'PROD' }, | ||
}] | ||
}; | ||
|
||
// ===== 1 | ||
it('should provide a correctly populated Bid given a valid response', () => { | ||
loadScriptStub = sinon.stub(adLoader, 'loadScript', () => { emitScript(normalResponse); }) | ||
|
||
adapter.callBids(oneBidConfig); | ||
|
||
let bid = addBidResponseSpy.args[0][1]; | ||
expect(bid.cpm).to.equal(1.51); | ||
expect(bid.ad).to.be.a('string'); | ||
expect(bid.bidderCode).to.equal('cox'); | ||
}); | ||
|
||
// ===== 2 | ||
it('should provide an empty Bid given a zero-price response', () => { | ||
loadScriptStub = sinon.stub(adLoader, 'loadScript', () => { emitScript(zeroPriceResponse); }) | ||
|
||
adapter.callBids(oneBidConfig); | ||
|
||
let bid = addBidResponseSpy.args[0][1]; | ||
expect(bid.cpm).to.not.be.ok | ||
expect(bid.ad).to.not.be.ok; | ||
}); | ||
|
||
// ===== 3 | ||
it('should provide an empty Bid given an incomplete response', () => { | ||
loadScriptStub = sinon.stub(adLoader, 'loadScript', () => { emitScript(incompleteResponse); }) | ||
|
||
adapter.callBids(oneBidConfig); | ||
|
||
let bid = addBidResponseSpy.args[0][1]; | ||
expect(bid.cpm).to.not.be.ok | ||
expect(bid.ad).to.not.be.ok; | ||
}); | ||
|
||
// ===== 4 | ||
it('should not provide a Bid given no response', () => { | ||
loadScriptStub = sinon.stub(adLoader, 'loadScript', () => { emitScript(''); }); | ||
|
||
adapter.callBids(oneBidConfig); | ||
|
||
expect(addBidResponseSpy.callCount).to.equal(0); | ||
}); | ||
}); | ||
|
||
describe('request generation', () => { | ||
const missingBidsConfig = { | ||
bidderCode: 'cox', | ||
bids: null, | ||
}; | ||
const missingParamsConfig = { | ||
bidderCode: 'cox', | ||
bids: [{ | ||
bidder: 'cox', | ||
placementCode: 'FOO456789', | ||
sizes: [300, 250], | ||
params: null, | ||
}] | ||
}; | ||
|
||
// ===== 5 | ||
it('should not make an ad call given missing bids in config', () => { | ||
loadScriptStub = sinon.stub(adLoader, 'loadScript'); | ||
|
||
adapter.callBids(missingBidsConfig); | ||
|
||
expect(loadScriptStub.callCount).to.equal(0); | ||
}); | ||
|
||
// ===== 6 | ||
it('should not make an ad call given missing params in config', () => { | ||
loadScriptStub = sinon.stub(adLoader, 'loadScript'); | ||
|
||
adapter.callBids(missingParamsConfig); | ||
|
||
expect(loadScriptStub.callCount).to.equal(0); | ||
}); | ||
}); | ||
}); |
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 don't need the code that is commented out here you may want to remove this