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

Timmedia/timadapter 2.0 #3497

Merged
merged 8 commits into from
Feb 4, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
177 changes: 177 additions & 0 deletions modules/timBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import * as utils from 'src/utils';
import {registerBidder} from 'src/adapters/bidderFactory';
import * as bidfactory from '../src/bidfactory';
var CONSTANTS = require('src/constants.json');
const BIDDER_CODE = 'tim';

function parseBidRequest(bidRequest) {
let params = bidRequest.url.split('?')[1];
var obj = {};
var pairs = params.split('&');
try {
for (var i in pairs) {
var split = pairs[i].split('=');
obj[decodeURIComponent(split[0])] = decodeURIComponent(split[1]);
}
} catch (e) {
utils.logError(e);
}

return JSON.parse(obj.br);
}

function formatAdMarkup(bid) {
var adm = bid.adm;
if ('nurl' in bid) {
adm += createTrackPixelHtml(bid.nurl);
}
return `<!DOCTYPE html><html><head><title></title><body style='margin:0px;padding:0px;'>${adm}</body></head>`;
}

function createTrackPixelHtml(url) {
if (!url) {
return '';
}
let img = '<div style="position:absolute;left:0px;top:0px;visibility:hidden;">';
img += '<img src="' + url + '"></div>';
return img;
}

export const spec = {
code: BIDDER_CODE,
aliases: ['timmedia'],

isBidRequestValid: function(bid) {
if (bid.params && bid.params.publisherid && bid.params.placementCode) {
return true;
} if (!bid.params) {
utils.logError('bid not valid: params were not provided');
} else if (!bid.params.publisherid) {
utils.logError('bid not valid: publisherid was not provided');
} else if (!bid.params.placementCode) {
utils.logError('bid not valid: placementCode was not provided');
} return false;
},

buildRequests: function(validBidRequests, bidderRequest) {
var requests = [];
for (var i = 0; i < validBidRequests.length; i++) {
requests.push(this.createRTBRequestURL(validBidRequests[i]));
}
return requests;
},

createRTBRequestURL: function(bidReq) {
// build bid request object
var domain = window.location.host;
var page = window.location.href;
var publisherid = bidReq.params.publisherid;
var bidFloor = bidReq.params.bidfloor;
var placementCode = bidReq.params.placementCode;

var adW = bidReq.mediaTypes.banner.sizes[0][0];
var adH = bidReq.mediaTypes.banner.sizes[0][1];

// build bid request with impressions
var bidRequest = {
id: utils.getUniqueIdentifierStr(),
imp: [{
id: bidReq.bidId,
banner: {
w: adW,
h: adH
},
tagid: placementCode,
bidfloor: bidFloor
}],
site: {
domain: domain,
page: page,
publisher: {
id: publisherid
}
},
device: {
'language': this.getLanguage(),
'w': adW,
'h': adH,
'js': 1,
'ua': navigator.userAgent
}
};
if (!bidFloor) {
delete bidRequest.imp['bidfloor'];
}

bidRequest.bidId = bidReq.bidId;
var url = '//hb.timmedia-hb.com/api/v2/services/prebid/' + publisherid + '/' + placementCode + '?' + 'br=' + encodeURIComponent(JSON.stringify(bidRequest));
return {
method: 'GET',
url: url,
data: '',
options: {withCredentials: false}
};
},

interpretResponse: function(serverResponse, bidRequest) {
bidRequest = parseBidRequest(bidRequest);
var bidResp = serverResponse.body;
const bidResponses = [];
if ((!bidResp || !bidResp.id) ||
(!bidResp.seatbid || bidResp.seatbid.length === 0 || !bidResp.seatbid[0].bid || bidResp.seatbid[0].bid.length === 0)) {
return [];
}
bidResp.seatbid[0].bid.forEach(function (bidderBid) {
var responseCPM;
var placementCode = '';
if (bidRequest) {
var bidResponse = bidfactory.createBid(1);
placementCode = bidRequest.placementCode;
bidRequest.status = CONSTANTS.STATUS.GOOD;
responseCPM = parseFloat(bidderBid.price);
if (responseCPM === 0) {
var bid = bidfactory.createBid(2);
bid.bidderCode = BIDDER_CODE;
bidResponses.push(bid);
return bidResponses;
}
bidResponse.placementCode = placementCode;
bidResponse.size = bidRequest.sizes;
bidResponse.creativeId = bidderBid.id;
bidResponse.bidderCode = BIDDER_CODE;
bidResponse.cpm = responseCPM;
bidResponse.ad = formatAdMarkup(bidderBid);
bidResponse.width = parseInt(bidderBid.w);
bidResponse.height = parseInt(bidderBid.h);
bidResponse.currency = bidResp.cur;
bidResponse.netRevenue = true;
bidResponse.requestId = bidRequest.bidId;
bidResponse.ttl = 180;
bidResponses.push(bidResponse);
}
});
return bidResponses;
},
getLanguage: function() {
const language = navigator.language ? 'language' : 'userLanguage';
return navigator[language].split('-')[0];
},

getUserSyncs: function(syncOptions, serverResponses) {
const syncs = []
return syncs;
},

onTimeout: function(data) {
// Bidder specifc code
},

onBidWon: function(bid) {
// Bidder specific code
},

onSetTargeting: function(bid) {
// Bidder specific code
},
}
registerBidder(spec);
26 changes: 26 additions & 0 deletions modules/timBidAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Overview

```
Module Name: tim Bidder Adapter
Module Type: Bidder Adapter
Maintainer: boris@thetimmedia.com
```

# Description

Module that connects to tim's demand sources

# Test Parameters
```
var adUnits = [{
"code":"99",
"sizes":[[300,250]],
"bids":[{"bidder":"tim",
"params":{
"placementCode":"testPlacementCode",
"publisherid":"testpublisherid"
}
}]
}]
```

152 changes: 152 additions & 0 deletions test/spec/modules/timBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { expect } from 'chai';
import { spec } from 'modules/timBidAdapter';

describe('timAdapterTests', function () {
describe('bidRequestValidity', function () {
it('bidRequest with publisherid and placementCode params', function () {
expect(spec.isBidRequestValid({
bidder: 'tim',
params: {
publisherid: 'testid',
placementCode: 'testplacement'
}
})).to.equal(true);
});

it('bidRequest with only publisherid', function () {
expect(spec.isBidRequestValid({
bidder: 'tim',
params: {
publisherid: 'testid'
}
})).to.equal(false);
});

it('bidRequest with only placementCode', function () {
expect(spec.isBidRequestValid({
bidder: 'tim',
params: {
placementCode: 'testplacement'
}
})).to.equal(false);
});

it('bidRequest without params', function () {
expect(spec.isBidRequestValid({
bidder: 'tim',
})).to.equal(false);
});
});

describe('buildRequests', function () {
const validBidRequests = [{
'bidder': 'tim',
'params': {'placementCode': 'placementCode', 'publisherid': 'testpublisherid'},
'mediaTypes': {'banner': {'sizes': [[300, 250]]}},
'adUnitCode': 'adUnitCode',
'transactionId': 'transactionId',
'sizes': [[300, 250]],
'bidId': 'bidId',
'bidderRequestId': 'bidderRequestId',
'auctionId': 'auctionId',
'src': 'client',
'bidRequestsCount': 1
}];

it('bidRequest method', function () {
const requests = spec.buildRequests(validBidRequests);
expect(requests[0].method).to.equal('GET');
});

it('bidRequest url', function () {
const requests = spec.buildRequests(validBidRequests);
expect(requests[0].url).to.exist;
});

it('bidRequest data', function () {
const requests = spec.buildRequests(validBidRequests);
expect(requests[0].data).to.exist;
});

it('bidRequest options', function () {
const requests = spec.buildRequests(validBidRequests);
expect(requests[0].options).to.exist;
});
});

describe('interpretResponse', function () {
const bidRequest = {
'method': 'GET',
'url': '//bidder.url/api/prebid/testpublisherid/header-bid-tag-0?br=%7B%22id%22%3A%223a3ac0d7fc2548%22%2C%22imp%22%3A%5B%7B%22id%22%3A%22251b8a6d3aac3e%22%2C%22banner%22%3A%7B%22w%22%3A300%2C%22h%22%3A250%7D%2C%22tagid%22%3A%22header-bid-tag-0%22%7D%5D%2C%22site%22%3A%7B%22domain%22%3A%22www.chinatimes.com%22%2C%22page%22%3A%22http%3A%2F%2Fwww.chinatimes.com%2Fa%22%2C%22publisher%22%3A%7B%22id%22%3A%22testpublisherid%22%7D%7D%2C%22device%22%3A%7B%22language%22%3A%22en%22%2C%22w%22%3A300%2C%22h%22%3A250%2C%22js%22%3A1%2C%22ua%22%3A%22Mozilla%2F5.0%20(Windows%20NT%2010.0%3B%20Win64%3B%20x64)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F71.0.3578.98%20Safari%2F537.36%22%7D%2C%22bidId%22%3A%22251b8a6d3aac3e%22%7D',
'data': '',
'options': {'withCredentials': false}
};

const serverResponse = {
'body': {
'id': 'id',
'seatbid': []
},
'headers': {}
};

it('check empty array response', function () {
const result = spec.interpretResponse(serverResponse, bidRequest);
expect(result).to.deep.equal([]);
});

const validBidRequest = {
'method': 'GET',
'url': '//bidder.url/api/v2/services/prebid/testpublisherid/placementCodeTest?br=%7B%22id%22%3A%2248640869bd9db94%22%2C%22imp%22%3A%5B%7B%22id%22%3A%224746fcaa11197f3%22%2C%22banner%22%3A%7B%22w%22%3A300%2C%22h%22%3A250%7D%2C%22tagid%22%3A%22placementCodeTest%22%7D%5D%2C%22site%22%3A%7B%22domain%22%3A%22mediamart.tv%22%2C%22page%22%3A%22http%3A%2F%2Fmediamart.tv%2Fsas%2Ftests%2FDesktop%2Fcaesar%2Fdfptest.html%22%2C%22publisher%22%3A%7B%22id%22%3A%22testpublisherid%22%7D%7D%2C%22device%22%3A%7B%22language%22%3A%22en%22%2C%22w%22%3A300%2C%22h%22%3A250%2C%22js%22%3A1%2C%22ua%22%3A%22Mozilla%2F5.0%20(Windows%20NT%2010.0%3B%20Win64%3B%20x64)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F71.0.3578.98%20Safari%2F537.36%22%7D%2C%22bidId%22%3A%224746fcaa11197f3%22%7D',
'data': '',
'options': {'withCredentials': false}
};
const validServerResponse = {
'body': {'id': 'id',
'seatbid': [
{'bid': [{'id': 'id',
'impid': 'impid',
'price': 3,
'nurl': 'https://bidder.url/api/v1/?price=${AUCTION_PRICE}&bidcur=USD&bidid=bidid=true',
'adm': '<script type=\"text/javascript\" src=\"https://domain.com/Player.php"></script>',
'adomain': [''],
'cid': '1',
'crid': '700',
'w': 300,
'h': 250
}]}],
'bidid': 'bidid',
'cur': 'USD'
},
'headers': {}
};
it('required keys', function () {
const result = spec.interpretResponse(validServerResponse, validBidRequest);

let requiredKeys = [
'requestId',
'creativeId',
'adId',
'cpm',
'width',
'height',
'currency',
'netRevenue',
'ttl',
'ad'
];

let resultKeys = Object.keys(result[0]);
requiredKeys.forEach(function(key) {
expect(resultKeys.indexOf(key) !== -1).to.equal(true);
});
})
});

describe('getUserSyncs', function () {
it('check empty response getUserSyncs', function () {
const result = spec.getUserSyncs('', '');
expect(result).to.deep.equal([]);
});
});
});