Skip to content

Commit

Permalink
Add Sharethrough adapter (prebid#865)
Browse files Browse the repository at this point in the history
* Rz/submit to prebid (prebid#4)

Added Sharethrough Adapter

* fix warnings

* added beacons

* made compatible with chrome 37. other minor changes

* win beacon fired in analytics adapter

* specs for new analytics adapter

* add try catch blocks. misc refactor

* removed test page

* remove debugger

* refactor analytics adapter

* removed test endpoint

* analytics url parameter is empty

* removed bidwon listener on adapter

* removed analytics from package.json
  • Loading branch information
rizhang authored and Will Alexander committed Mar 6, 2017
1 parent 76fb18e commit 744c347
Show file tree
Hide file tree
Showing 5 changed files with 468 additions and 0 deletions.
1 change: 1 addition & 0 deletions adapters.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"underdogmedia",
"memeglobal",
"centro",
"sharethrough",
"roxot",
"vertoz",
"widespace",
Expand Down
63 changes: 63 additions & 0 deletions src/adapters/analytics/sharethrough_analytics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import adapter from 'AnalyticsAdapter';
const utils = require('../../utils');

const emptyUrl = '';
const analyticsType = 'endpoint';
const STR_BIDDER_CODE = "sharethrough";
const STR_VERSION = "0.1.0";

export default utils.extend(adapter(
{
emptyUrl,
analyticsType
}
),
{
STR_BEACON_HOST: document.location.protocol + "//b.sharethrough.com/butler?",
placementCodeSet: {},

track({ eventType, args }) {
if(eventType === 'bidRequested' && args.bidderCode === 'sharethrough') {
var bids = args.bids;
var keys = Object.keys(bids);
for(var i = 0; i < keys.length; i++) {
this.placementCodeSet[bids[keys[i]].placementCode] = args.bids[keys[i]];
}
}

if(eventType === 'bidWon') {
this.bidWon(args);
}
},

bidWon(args) {
const curBidderCode = args.bidderCode;

if(curBidderCode !== STR_BIDDER_CODE && (args.adUnitCode in this.placementCodeSet)) {
let strBid = this.placementCodeSet[args.adUnitCode];
this.fireLoseBeacon(curBidderCode, args.cpm, strBid.adserverRequestId, "headerBidLose");
}
},

fireLoseBeacon(winningBidderCode, winningCPM, arid, type) {
let loseBeaconUrl = this.STR_BEACON_HOST;
loseBeaconUrl = utils.tryAppendQueryString(loseBeaconUrl, "winnerBidderCode", winningBidderCode);
loseBeaconUrl = utils.tryAppendQueryString(loseBeaconUrl, "winnerCpm", winningCPM);
loseBeaconUrl = utils.tryAppendQueryString(loseBeaconUrl, "arid", arid);
loseBeaconUrl = utils.tryAppendQueryString(loseBeaconUrl, "type", type);
loseBeaconUrl = this.appendEnvFields(loseBeaconUrl);

this.fireBeacon(loseBeaconUrl);
},
appendEnvFields(url) {
url = utils.tryAppendQueryString(url, 'hbVersion', '$prebid.version$');
url = utils.tryAppendQueryString(url, 'strVersion', STR_VERSION);
url = utils.tryAppendQueryString(url, 'hbSource', 'prebid');

return url;
},
fireBeacon(theUrl) {
const img = new Image();
img.src = theUrl;
}
});
127 changes: 127 additions & 0 deletions src/adapters/sharethrough.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
var utils = require('../utils.js');
var bidmanager = require('../bidmanager.js');
var bidfactory = require('../bidfactory.js');

const STR_BIDDER_CODE = "sharethrough";
const STR_VERSION = "0.1.0"; //Need to check analytics too for version

var SharethroughAdapter = function SharethroughAdapter() {

const str = {};
str.STR_BTLR_HOST = document.location.protocol + "//btlr.sharethrough.com";
str.STR_BEACON_HOST = document.location.protocol + "//b.sharethrough.com/butler?";
str.placementCodeSet = {};

function _callBids(params) {
const bids = params.bids;

addEventListener("message", _receiveMessage, false);

// cycle through bids
for (let i = 0; i < bids.length; i += 1) {
const bidRequest = bids[i];
str.placementCodeSet[bidRequest.placementCode] = bidRequest;
const scriptUrl = _buildSharethroughCall(bidRequest);
str.loadIFrame(scriptUrl);
}
}

function _buildSharethroughCall(bid) {
const testPkey = 'test';
const pkey = utils.getBidIdParameter('pkey', bid.params);

let host = str.STR_BTLR_HOST;

let url = host + "/header-bid/v1?";
url = utils.tryAppendQueryString(url, 'bidId', bid.bidId);

if(pkey !== testPkey) {
url = utils.tryAppendQueryString(url, 'placement_key', pkey);
url = utils.tryAppendQueryString(url, 'ijson', '$$PREBID_GLOBAL$$.strcallback');
url = appendEnvFields(url);
} else {
url = url.substring(0, url.length - 1);
}

return url;
}

str.loadIFrame = function(url) {
const iframe = document.createElement("iframe");
iframe.src = url;
iframe.style.cssText = 'display:none;';

document.body.appendChild(iframe);
};

function _receiveMessage(event) {
if(event.origin === str.STR_BTLR_HOST) {
try {
$$PREBID_GLOBAL$$.strcallback(JSON.parse(event.data).response);
} catch(e) {
console.log(e);
}
}
}

$$PREBID_GLOBAL$$.strcallback = function(bidResponse) {
const bidId = bidResponse.bidId;
const bidObj = utils.getBidRequest(bidId);
try {
const bid = bidfactory.createBid(1, bidObj);
bid.bidderCode = STR_BIDDER_CODE;
bid.cpm = bidResponse.creatives[0].cpm;
const size = bidObj.sizes[0];
bid.width = size[0];
bid.height = size[1];
bid.adserverRequestId = bidResponse.adserverRequestId;
str.placementCodeSet[bidObj.placementCode].adserverRequestId = bidResponse.adserverRequestId;

bid.pkey = utils.getBidIdParameter('pkey', bidObj.params);

const windowLocation = `str_response_${bidId}`;
const bidJsonString = JSON.stringify(bidResponse);
bid.ad = `<div data-str-native-key="${bid.pkey}" data-stx-response-name='${windowLocation}'>
</div>
<script>var ${windowLocation} = ${bidJsonString}</script>
<script src="//native.sharethrough.com/assets/sfp-set-targeting.js"></script>
<script type='text/javascript'>
(function() {
var sfp_js = document.createElement('script');
sfp_js.src = "//native.sharethrough.com/assets/sfp.js";
sfp_js.type = 'text/javascript';
sfp_js.charset = 'utf-8';
try {
window.top.document.getElementsByTagName('body')[0].appendChild(sfp_js);
} catch (e) {
console.log(e);
}
})();
</script>`;
bidmanager.addBidResponse(bidObj.placementCode, bid);
} catch (e) {
_handleInvalidBid(bidObj);
}
};

function _handleInvalidBid(bidObj) {
const bid = bidfactory.createBid(2, bidObj);
bidmanager.addBidResponse(bidObj.placementCode, bid);
}

function appendEnvFields(url) {
url = utils.tryAppendQueryString(url, 'hbVersion', '$prebid.version$');
url = utils.tryAppendQueryString(url, 'strVersion', STR_VERSION);
url = utils.tryAppendQueryString(url, 'hbSource', 'prebid');

return url;
}

return {
callBids: _callBids,
str : str,
};
};

module.exports = SharethroughAdapter;

178 changes: 178 additions & 0 deletions test/spec/adapters/sharethrough_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import { expect } from 'chai';
import Adapter from '../../../src/adapters/sharethrough';
import bidManager from '../../../src/bidmanager';

describe('sharethrough adapter', () => {

let adapter;
let sandbox;
let bidsRequestedOriginal;

const bidderRequest = {
bidderCode: 'sharethrough',
bids: [
{
bidder: 'sharethrough',
bidId: 'bidId1',
sizes: [[600,300]],
placementCode: 'foo',
params: {
pkey: 'aaaa1111'
}
},
{
bidder: 'sharethrough',
bidId: 'bidId2',
sizes: [[700,400]],
placementCode: 'bar',
params: {
pkey: 'bbbb2222'
}
}
]
};

beforeEach(() => {
adapter = new Adapter();
sandbox = sinon.sandbox.create();
bidsRequestedOriginal = pbjs._bidsRequested;
pbjs._bidsRequested = [];
});

afterEach(() => {
sandbox.restore();

pbjs._bidsRequested = bidsRequestedOriginal;
});

describe('callBids', () => {

let firstBidUrl;
let secondBidUrl;

beforeEach(() => {
sandbox.spy(adapter.str, 'loadIFrame');
});

it('should call loadIFrame on the adloader for each bid', () => {

adapter.callBids(bidderRequest);

firstBidUrl = adapter.str.loadIFrame.firstCall.args[0];
secondBidUrl = adapter.str.loadIFrame.secondCall.args[0];

sinon.assert.calledTwice(adapter.str.loadIFrame);

expect(firstBidUrl).to.contain(adapter.str.STR_BTLR_HOST + '/header-bid/v1?bidId=bidId1&placement_key=aaaa1111&ijson=pbjs.strcallback&hbVersion=%24prebid.version%24&strVersion=0.1.0&hbSource=prebid&');
expect(secondBidUrl).to.contain(adapter.str.STR_BTLR_HOST + '/header-bid/v1?bidId=bidId2&placement_key=bbbb2222&ijson=pbjs.strcallback&hbVersion=%24prebid.version%24&strVersion=0.1.0&hbSource=prebid&');
});

});

describe('strcallback', () => {

it('should exist and be a function', () => {
let shit = sandbox.stub(pbjs, 'strcallback');
expect(pbjs.strcallback).to.exist.and.to.be.a('function');
});

});

describe('bid requests', () => {

let firstBid;
let secondBid;

beforeEach(() => {
sandbox.stub(bidManager, 'addBidResponse');

pbjs._bidsRequested.push(bidderRequest);
adapter.str.placementCodeSet['foo'] = {};
adapter.str.placementCodeSet['bar'] = {};
// respond

let bidderReponse1 = {
"adserverRequestId": "40b6afd5-6134-4fbb-850a-bb8972a46994",
"bidId": "bidId1",
"creatives": [
{
"cpm": 12.34,
"auctionWinId": "b2882d5e-bf8b-44da-a91c-0c11287b8051",
"version": 1
}
],
"stxUserId": ""
};

let bidderReponse2 = {
"adserverRequestId": "40b6afd5-6134-4fbb-850a-bb8972a46994",
"bidId": "bidId2",
"creatives": [
{
"cpm": 12.35,
"auctionWinId": "b2882d5e-bf8b-44da-a91c-0c11287b8051",
"version": 1
}
],
"stxUserId": ""
};

pbjs.strcallback(bidderReponse1);
pbjs.strcallback(bidderReponse2);

firstBid = bidManager.addBidResponse.firstCall.args[1];
secondBid = bidManager.addBidResponse.secondCall.args[1];
});

it('should add a bid object for each bid', () => {
sinon.assert.calledTwice(bidManager.addBidResponse);
});

it('should pass the correct placement code as first param', () => {
let firstPlacementCode = bidManager.addBidResponse.firstCall.args[0];
let secondPlacementCode = bidManager.addBidResponse.secondCall.args[0];

expect(firstPlacementCode).to.eql('foo');
expect(secondPlacementCode).to.eql('bar');
});

it('should include the bid request bidId as the adId', () => {
expect(firstBid).to.have.property('adId', 'bidId1');
expect(secondBid).to.have.property('adId', 'bidId2');
});

it('should have a good statusCode', () => {
expect(firstBid.getStatusCode()).to.eql(1);
expect(secondBid.getStatusCode()).to.eql(1);
});

it('should add the CPM to the bid object', () => {
expect(firstBid).to.have.property('cpm', 12.34);
expect(secondBid).to.have.property('cpm', 12.35);
});

it('should add the bidder code to the bid object', () => {
expect(firstBid).to.have.property('bidderCode', 'sharethrough');
expect(secondBid).to.have.property('bidderCode', 'sharethrough');
});

it('should include the ad on the bid object', () => {
expect(firstBid).to.have.property('ad');
expect(secondBid).to.have.property('ad');
});

it('should include the size on the bid object', () => {
expect(firstBid).to.have.property('width', 600);
expect(firstBid).to.have.property('height', 300);
expect(secondBid).to.have.property('width', 700);
expect(secondBid).to.have.property('height', 400);
});

it('should include the pkey', () => {
expect(firstBid).to.have.property('pkey', 'aaaa1111');
expect(secondBid).to.have.property('pkey', 'bbbb2222');
});

});

});
Loading

0 comments on commit 744c347

Please sign in to comment.