Skip to content

Commit

Permalink
Add Twenga adapter (#936)
Browse files Browse the repository at this point in the history
  • Loading branch information
twenga-rtb authored and matthewlane committed Feb 7, 2017
1 parent 2f4bb60 commit fcf50e7
Show file tree
Hide file tree
Showing 5 changed files with 274 additions and 2 deletions.
1 change: 1 addition & 0 deletions adapters.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"sovrn",
"springserve",
"triplelift",
"twenga",
"yieldbot",
"nginad",
"brightcom",
Expand Down
151 changes: 151 additions & 0 deletions src/adapters/twenga.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { getBidRequest } from '../utils.js';

var CONSTANTS = require('../constants.json');
var utils = require('../utils.js');
var adloader = require('../adloader.js');
var bidmanager = require('../bidmanager.js');
var bidfactory = require('../bidfactory.js');
var Adapter = require('./adapter.js');

var TwengaAdapter;
TwengaAdapter = function TwengaAdapter() {
var baseAdapter = Adapter.createNew('twenga');

baseAdapter.callBids = function (params) {
for (var i = 0; i < params.bids.length; i++) {
var bidRequest = params.bids[i];
var callbackId = bidRequest.bidId;
adloader.loadScript(buildBidCall(bidRequest, callbackId));
}
};

function buildBidCall(bid, callbackId) {

var bidUrl = '//rtb.t.c4tw.net/Bid?';
bidUrl = utils.tryAppendQueryString(bidUrl, 's', 'h');
bidUrl = utils.tryAppendQueryString(bidUrl, 'callback', '$$PREBID_GLOBAL$$.handleTwCB');
bidUrl = utils.tryAppendQueryString(bidUrl, 'callback_uid', callbackId);
bidUrl = utils.tryAppendQueryString(bidUrl, 'referrer', utils.getTopWindowUrl());
if (bid.params) {
for (var key in bid.params) {
var value = bid.params[key];
switch (key) {
case 'placementId': key = 'id'; break;
case 'siteId': key = 'sid'; break;
case 'publisherId': key = 'pid'; break;
case 'currency': key = 'cur'; break;
case 'bidFloor': key = 'min'; break;
case 'country': key = 'gz'; break;
}
bidUrl = utils.tryAppendQueryString(bidUrl, key, value);
}
}

var sizes = utils.parseSizesInput(bid.sizes);
if (sizes.length > 0) {
bidUrl = utils.tryAppendQueryString(bidUrl, 'size', sizes.join(','));
}

bidUrl += 'ta=1';

// @if NODE_ENV='debug'
utils.logMessage('bid request built: ' + bidUrl);

// @endif

//append a timer here to track latency
bid.startTime = new Date().getTime();

return bidUrl;
}

//expose the callback to the global object:
$$PREBID_GLOBAL$$.handleTwCB = function (bidResponseObj) {

var bidCode;

if (bidResponseObj && bidResponseObj.callback_uid) {

var responseCPM;
var id = bidResponseObj.callback_uid;
var placementCode = '';
var bidObj = getBidRequest(id);
if (bidObj) {

bidCode = bidObj.bidder;

placementCode = bidObj.placementCode;

bidObj.status = CONSTANTS.STATUS.GOOD;
}

// @if NODE_ENV='debug'
utils.logMessage('JSONP callback function called for ad ID: ' + id);

// @endif
var bid = [];
if (bidResponseObj.result &&
bidResponseObj.result.cpm &&
bidResponseObj.result.cpm !== 0 &&
bidResponseObj.result.ad) {

var result = bidResponseObj.result;

responseCPM = parseInt(result.cpm, 10);

//CPM response from /Bid is dollar/cent multiplied by 10000
//in order to avoid using floats
//switch CPM to "dollar/cent"
responseCPM = responseCPM / 10000;

var ad = result.ad.replace('%%WP%%', result.cpm);

//store bid response
//bid status is good (indicating 1)
bid = bidfactory.createBid(1, bidObj);
bid.creative_id = result.creative_id;
bid.bidderCode = bidCode;
bid.cpm = responseCPM;
if (ad && (ad.lastIndexOf('http', 0) === 0 || ad.lastIndexOf('//', 0) === 0))
bid.adUrl = ad;
else
bid.ad = ad;
bid.width = result.width;
bid.height = result.height;

bidmanager.addBidResponse(placementCode, bid);

} else {
//no response data
// @if NODE_ENV='debug'
utils.logMessage('No prebid response from Twenga for placement code ' + placementCode);

// @endif
//indicate that there is no bid for this placement
bid = bidfactory.createBid(2, bidObj);
bid.bidderCode = bidCode;
bidmanager.addBidResponse(placementCode, bid);
}

} else {
//no response data
// @if NODE_ENV='debug'
utils.logMessage('No prebid response for placement %%PLACEMENT%%');

// @endif
}
};

return {
callBids: baseAdapter.callBids,
setBidderCode: baseAdapter.setBidderCode,
createNew: TwengaAdapter.createNew,
buildBidCall: buildBidCall
};
};

TwengaAdapter.createNew = function () {
return new TwengaAdapter();
};

module.exports = TwengaAdapter;
2 changes: 1 addition & 1 deletion test/fixtures/allAdapters.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

120 changes: 120 additions & 0 deletions test/spec/adapters/twenga_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
describe("twenga adapter tests", function () {

var urlParse = require("url-parse");
var querystringify = require("querystringify");
var adapter = require("src/adapters/twenga");
var adLoader = require("src/adloader");
var expect = require("chai").expect;
var bidmanager = require("src/bidmanager");
var CONSTANTS = require('src/constants.json');

var DEFAULT_PARAMS = {
bidderCode: "twenga",
bids: [{
bidId: "tw_abcd1234",
sizes: [[300, 250], [300, 200]],
bidder: "twenga",
params: {
placementId: "test",
siteId: 1234,
publisherId: 5678,
currency: "USD",
bidFloor: 0.5,
country: "DE"
},
requestId: "tw_efgh5678",
placementCode: "tw_42"
}]
};

var BID_RESPONSE = {
result: {
cpm: 10000,
width: 300,
height: 250,
ad: "//rtb.t.c4tw.net",
creative_id: "test"
},
callback_uid: "tw_abcd1234"
};

it("sets url parameters", function () {
var stubLoadScript = sinon.stub(adLoader, "loadScript");

adapter().callBids(DEFAULT_PARAMS);

var bidUrl = stubLoadScript.getCall(0).args[0];
var parsedBidUrl = urlParse(bidUrl);
var parsedBidUrlQueryString = querystringify.parse(parsedBidUrl.query);

expect(parsedBidUrl.hostname).to.equal("rtb.t.c4tw.net");
expect(parsedBidUrl.pathname).to.equal("/Bid");

expect(parsedBidUrlQueryString).to.have.property("s").and.to.equal("h");
expect(parsedBidUrlQueryString).to.have.property("callback").and.to.equal("$$PREBID_GLOBAL$$.handleTwCB");
expect(parsedBidUrlQueryString).to.have.property("callback_uid").and.to.equal("tw_abcd1234");
expect(parsedBidUrlQueryString).to.have.property("id").and.to.equal("test");

stubLoadScript.restore();
});

var stringToFunction = function (s) {
var scope = global;
var scopeSplit = s.split('.');
for (var i = 0; i < scopeSplit.length - 1; i++) {
scope = scope[scopeSplit[i]];
if (scope == undefined) return;
}
return scope[scopeSplit[scopeSplit.length - 1]];
};

it("creates an empty bid response if no bids", function() {
var stubLoadScript = sinon.stub(adLoader, "loadScript", function(url) {
var bidUrl = stubLoadScript.getCall(0).args[0];
var parsedBidUrl = urlParse(bidUrl);
var parsedBidUrlQueryString = querystringify.parse(parsedBidUrl.query);

var callback = stringToFunction(parsedBidUrlQueryString.callback);
expect(callback).to.exist.and.to.be.a('function');
callback(undefined);
});
var stubAddBidResponse = sinon.stub(bidmanager, "addBidResponse");

adapter.createNew().callBids(DEFAULT_PARAMS);

expect(stubAddBidResponse.getCall(0)).to.be.null;

stubAddBidResponse.restore();
stubLoadScript.restore();
});

it("creates a bid response if bid is returned", function() {
var stubLoadScript = sinon.stub(adLoader, "loadScript", function(url) {
var bidUrl = stubLoadScript.getCall(0).args[0];
var parsedBidUrl = urlParse(bidUrl);
var parsedBidUrlQueryString = querystringify.parse(parsedBidUrl.query);

$$PREBID_GLOBAL$$._bidsRequested.
push({ bidderCode: DEFAULT_PARAMS.bidderCode,
bids: [{ bidId: parsedBidUrlQueryString.callback_uid,
placementCode: DEFAULT_PARAMS.bids[0].placementCode }]});

var callback = stringToFunction(parsedBidUrlQueryString.callback);
expect(callback).to.exist.and.to.be.a('function');
callback(BID_RESPONSE);
});
var stubAddBidResponse = sinon.stub(bidmanager, "addBidResponse");

adapter.createNew().callBids(DEFAULT_PARAMS);

var bidResponseAd = stubAddBidResponse.getCall(0).args[1];

expect(bidResponseAd).to.have.property("cpm").and.to.equal(BID_RESPONSE.result.cpm / 10000);
expect(bidResponseAd).to.have.property("adUrl").and.to.equal(BID_RESPONSE.result.ad);
expect(bidResponseAd).to.have.property("width").and.to.equal(BID_RESPONSE.result.width);
expect(bidResponseAd).to.have.property("height").and.to.equal(BID_RESPONSE.result.height);

stubAddBidResponse.restore();
stubLoadScript.restore();
});
});
2 changes: 1 addition & 1 deletion test/spec/loaders/adapterLoader_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const allAdapters = require('../../fixtures/allAdapters');
const expect = require('chai').expect;
require('../../../loaders/adapterLoader');

const defaultAdapters = ["aardvark","adblade","adbutler","adequant","adform","admedia","aol","appnexus","appnexusAst","getintent","hiromedia","indexExchange","kruxlink","komoona","openx","piximedia","pubmatic","pulsepoint","rubicon","sonobi","sovrn","springserve","triplelift","yieldbot","nginad","brightcom","wideorbit","jcm","underdogmedia","memeglobal","centro","roxot",{"appnexus":{"alias":"brealtime"}},{"appnexus":{"alias":"pagescience"}},{"appnexus":{"alias":"defymedia"}},{"appnexusAst":{"supportedMediaTypes":["video"]}}];
const defaultAdapters = ["aardvark","adblade","adbutler","adequant","adform","admedia","aol","appnexus","appnexusAst","getintent","hiromedia","indexExchange","kruxlink","komoona","openx","piximedia","pubmatic","pulsepoint","rubicon","sonobi","sovrn","springserve","triplelift","twenga","yieldbot","nginad","brightcom","wideorbit","jcm","underdogmedia","memeglobal","centro","roxot",{"appnexus":{"alias":"brealtime"}},{"appnexus":{"alias":"pagescience"}},{"appnexus":{"alias":"defymedia"}},{"appnexusAst":{"supportedMediaTypes":["video"]}}];

const input = `/** INSERT ADAPTERS - DO NOT EDIT OR REMOVE */
/** END INSERT ADAPTERS */`;
Expand Down

0 comments on commit fcf50e7

Please sign in to comment.