forked from prebid/Prebid.js
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request prebid#9 from pm-harshad-mane/PubMatic_One
PubMatic one
- Loading branch information
Showing
2 changed files
with
395 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
import * as utils from 'src/utils'; | ||
import { registerBidder } from 'src/adapters/bidderFactory'; | ||
const constants = require('src/constants.json'); | ||
|
||
const BIDDER_CODE = 'pubmatic'; | ||
const ENDPOINT = '//openbid.pubmatic.com/translator?source=prebid-client'; | ||
const USYNCURL = '//ads.pubmatic.com/AdServer/js/showad.js#PIX&kdntuid=1&p='; | ||
const CURRENCY = 'USD'; | ||
const AUCTION_TYPE = 2; //todo ?? is auction type correct ? second price auction | ||
//todo: now what is significance of value ? | ||
const CUSTOM_PARAMS = { | ||
'kadpageurl': 'kadpageurl', | ||
'gender': 'gender', // User gender | ||
'yob': 'yob', // User year of birth | ||
'dctr': 'dctr', // Custom Targeting //todo : remove ???? | ||
'lat': 'lat', // User location - Latitude | ||
'lon': 'lon', // User Location - Longitude | ||
'wiid': 'wiid', // OpenWrap Wrapper Impression ID | ||
'profId': 'profId', // OpenWrap Legacy: Profile ID | ||
'verId': 'verId' // OpenWrap Legacy: version ID | ||
}; | ||
|
||
let publisherId = 0; | ||
|
||
function _processPmZoneId(zoneId) { | ||
if (utils.isStr(zoneId)) { | ||
return zoneId.split(',').slice(0, 50).join(); | ||
} else { | ||
utils.logWarn('PubMatic: Ignoring param key: pmzoneid, expects string-value, found ' + typeof zoneId); | ||
return undefined; | ||
} | ||
} | ||
|
||
function _processFloor(floor){ | ||
if (utils.isStr(floor)) { | ||
return parseFloat(floor) || undefined; | ||
} else { | ||
utils.logWarn('PubMatic: Ignoring param key: kadfloor, expects string-value, found ' + typeof floor); | ||
return undefined; | ||
} | ||
} | ||
|
||
function _cleanSlot(slotName) { | ||
if (utils.isStr(slotName)) { | ||
return slotName.replace(/^\s+/g, '').replace(/\s+$/g, ''); | ||
} | ||
return ''; | ||
} | ||
|
||
function _parseAdSlot(bid){ | ||
|
||
bid.params.adUnit = ''; | ||
bid.params.adUnitIndex = '0'; | ||
bid.params.width = 0; | ||
bid.params.height = 0; | ||
|
||
bid.params.adSlot = _cleanSlot(bid.params.adSlot); | ||
|
||
var slot = bid.params.adSlot; | ||
var splits = slot.split(':'); | ||
|
||
slot = splits[0]; | ||
if(splits.length == 2){ | ||
bid.params.adUnitIndex = splits[1]; | ||
} | ||
splits = slot.split('@'); | ||
if(splits.length != 2){ | ||
return; | ||
} | ||
bid.params.adUnit = splits[0]; | ||
splits = splits[1].split('x'); | ||
if(splits.length != 2){ | ||
return; | ||
} | ||
bid.params.width = parseInt(splits[0]); | ||
bid.params.height = parseInt(splits[1]); | ||
} | ||
|
||
function _initConf() { | ||
var conf = {}; | ||
var currTime = new Date(); | ||
conf.sec = window.location.protocol === 'https:' ? 1 : 0; | ||
conf.wp = 'PreBid';//todo : do we need to send this ? | ||
conf.wv = constants.REPO_AND_VERSION; | ||
try { | ||
conf.pageURL = window.top.location.href; | ||
conf.hostname = window.top.location.hostname; | ||
conf.refurl = window.top.document.referrer; | ||
} catch (e) { | ||
conf.pageURL = window.location.href; | ||
conf.hostname = window.location.hostname; | ||
conf.refurl = window.document.referrer; | ||
} | ||
return conf; | ||
} | ||
|
||
function _handleCustomParams(params, conf) { | ||
// istanbul ignore else | ||
if (!conf.kadpageurl) { | ||
conf.kadpageurl = conf.pageURL; | ||
} | ||
|
||
var key, value, entry; | ||
for (key in CUSTOM_PARAMS) { | ||
// istanbul ignore else | ||
if (CUSTOM_PARAMS.hasOwnProperty(key)) { | ||
value = params[key]; | ||
// istanbul ignore else | ||
if (value) { | ||
entry = CUSTOM_PARAMS[key]; | ||
|
||
if (typeof entry === 'object') { | ||
value = entry.m(value, conf); | ||
key = entry.n; | ||
} else { | ||
key = CUSTOM_PARAMS[key]; | ||
} | ||
|
||
if (utils.isStr(value)) { | ||
conf[key] = value; | ||
} else { | ||
utils.logWarn('PubMatic: Ignoring param key: ' + CUSTOM_PARAMS[key] + ', expects string-value, found ' + typeof value); | ||
} | ||
} | ||
} | ||
} | ||
return conf; | ||
} | ||
|
||
function _createOrtbTemplate(conf){ | ||
return { | ||
id : '' + new Date().getTime(), | ||
at: AUCTION_TYPE, | ||
cur: [CURRENCY], | ||
imp: [], | ||
site: { | ||
domain: conf.hostname, | ||
page: conf.pageURL, | ||
publisher: {} | ||
}, | ||
device: { | ||
ua: navigator.userAgent, | ||
js: 1, | ||
dnt: (navigator.doNotTrack == "yes" || navigator.doNotTrack == "1" || navigator.msDoNotTrack == "1") ? 1 : 0, | ||
h: screen.height, | ||
w: screen.width, | ||
language: navigator.language | ||
}, | ||
user: {}, | ||
ext: {} | ||
}; | ||
} | ||
|
||
function _createImpressionObject(bid, conf){ | ||
return { | ||
id: bid.bidId, | ||
tagid: bid.params.adUnit, | ||
bidfloor: _processFloor(bid.params.kadfloor), | ||
secure: conf.sec, | ||
banner: { | ||
pos: 0, | ||
w: bid.params.width, | ||
h: bid.params.height, | ||
topframe: 1, //todo: may need to change for postbid : check with open bid | ||
}, | ||
ext: { | ||
pmZoneId: _processPmZoneId(bid.params.pmzoneid) | ||
} | ||
}; | ||
} | ||
|
||
export const spec = { | ||
code: BIDDER_CODE, | ||
|
||
/** | ||
* Determines whether or not the given bid request is valid. Valid bid request must have placementId and hbid | ||
* | ||
* @param {BidRequest} bid The bid params to validate. | ||
* @return boolean True if this is a valid bid, and false otherwise. | ||
*/ | ||
isBidRequestValid: bid => { | ||
return !!(bid && bid.params && bid.params.publisherId && bid.params.adSlot); | ||
}, | ||
|
||
/** | ||
* Make a server request from the list of BidRequests. | ||
* | ||
* @param {validBidRequests[]} - an array of bids | ||
* @return ServerRequest Info describing the request to the server. | ||
*/ | ||
buildRequests: validBidRequests => { | ||
var conf = _initConf(); | ||
var payload = _createOrtbTemplate(conf); | ||
validBidRequests.forEach(bid => { | ||
_parseAdSlot(bid); | ||
if(! (bid.params.adSlot && bid.params.adUnit && bid.params.adUnitIndex && bid.params.width && bid.params.height)){ | ||
utils.logWarn('PubMatic: Skipping the non-standard adslot:', bid.params.adSlot, bid); | ||
return; | ||
} | ||
conf.pubId = conf.pubId || bid.params.publisherId; | ||
conf = _handleCustomParams(bid.params, conf); | ||
conf.transactionId = bid.transactionId; | ||
payload.imp.push(_createImpressionObject(bid, conf)); | ||
}); | ||
|
||
if(payload.imp.length == 0){ | ||
return; | ||
} | ||
|
||
payload.site.publisher.id = conf.pubId; | ||
publisherId = conf.pubId; | ||
payload.ext.wrapper = {}; | ||
payload.ext.wrapper.profile = conf.profId || undefined; | ||
payload.ext.wrapper.version = conf.verId || undefined; | ||
payload.ext.wrapper.wiid = conf.wiid || undefined; | ||
payload.ext.wrapper.wv = conf.wv || undefined; | ||
payload.ext.wrapper.transactionId = conf.transactionId; | ||
payload.user.gender = conf.gender || undefined; | ||
payload.user.lat = conf.lat || undefined; | ||
payload.user.lon = conf.lon || undefined; | ||
payload.user.yob = conf.yob || undefined; | ||
payload.site.page = conf.kadpageurl || payload.site.page; | ||
return { | ||
method: 'POST', | ||
url: ENDPOINT, | ||
data: JSON.stringify(payload) | ||
}; | ||
}, | ||
|
||
/** | ||
* Unpack the response from the server into a list of bids. | ||
* | ||
* @param {*} response A successful response from the server. | ||
* @return {Bid[]} An array of bids which were nested inside the server. | ||
*/ | ||
interpretResponse: (response, request) => { | ||
const bidResponses = []; | ||
try { | ||
if (response.body && response.body.seatbid && response.body.seatbid[0] && response.body.seatbid[0].bid) { | ||
response.body.seatbid[0].bid.forEach(bid => { | ||
let newBid = { | ||
requestId: bid.impid, | ||
cpm: bid.price, // Can we round to min precision ? | ||
width: bid.w, | ||
height: bid.h, | ||
creativeId: bid.crid || bid.id, | ||
dealId: bid.dealid, | ||
currency: CURRENCY, | ||
netRevenue: true, | ||
ttl: 300, | ||
referrer: utils.getTopWindowUrl(), | ||
ad: bid.adm | ||
}; | ||
bidResponses.push(newBid); | ||
}); | ||
} | ||
} catch (error) { | ||
utils.logError(error); | ||
} | ||
return bidResponses; | ||
}, | ||
|
||
/** | ||
* Register User Sync. | ||
*/ | ||
getUserSyncs: syncOptions => { | ||
if (syncOptions.iframeEnabled) { | ||
return [{ | ||
type: 'iframe', | ||
url: USYNCURL + publisherId | ||
}]; | ||
} | ||
} | ||
}; | ||
|
||
registerBidder(spec); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import {expect} from 'chai'; | ||
import {spec} from 'modules/pubmaticBidAdapter'; | ||
const constants = require('src/constants.json'); | ||
|
||
describe('PubMatic adapter', () => { | ||
let bidRequests; | ||
|
||
beforeEach(() => { | ||
bidRequests = [ | ||
{ | ||
bidder: 'pubmatic', | ||
params: { | ||
publisherId: '301', | ||
adSlot: '/15671365/DMDemo@300x250:0', | ||
kadfloor: '1.2', | ||
pmzoneid: 'aabc, ddef', | ||
kadpageurl: 'www.publisher.com', | ||
yob: '1986', | ||
gender: 'M', | ||
lat: '12.3', | ||
lon: '23.7', | ||
wiid: '1234567890', | ||
profId: '100', | ||
verId: '200' | ||
}, | ||
placementCode: '/19968336/header-bid-tag-1', | ||
sizes: [[300, 250], [300, 600]], | ||
bidId: '23acc48ad47af5', | ||
requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', | ||
bidderRequestId: '1c56ad30b9b8ca8', | ||
transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' | ||
} | ||
]; | ||
}); | ||
|
||
describe('implementation', () => { | ||
|
||
describe('Bid validations', () => { | ||
|
||
it('valid bid case', () => { | ||
let validBid = { | ||
bidder: 'pubmatic', | ||
params: { | ||
publisherId: '301', | ||
adSlot: '/15671365/DMDemo@300x250:0' | ||
} | ||
}, | ||
isValid = spec.isBidRequestValid(validBid); | ||
expect(isValid).to.equal(true); | ||
}); | ||
|
||
it('invalid bid case: publisherId not passed', () => { | ||
let validBid = { | ||
bidder: 'pubmatic', | ||
params: { | ||
adSlot: '/15671365/DMDemo@300x250:0' | ||
} | ||
}, | ||
isValid = spec.isBidRequestValid(validBid); | ||
expect(isValid).to.equal(false); | ||
}); | ||
|
||
it('invalid bid case: adSlot not passed', () => { | ||
let validBid = { | ||
bidder: 'pubmatic', | ||
params: { | ||
publisherId: '301' | ||
} | ||
}, | ||
isValid = spec.isBidRequestValid(validBid); | ||
expect(isValid).to.equal(false); | ||
}); | ||
}); | ||
|
||
describe('Request formation', () => { | ||
it('Endpoint checking', () => { | ||
let request = spec.buildRequests(bidRequests); | ||
expect(request.url).to.equal('//openbid.pubmatic.com/translator?source=prebid-client'); | ||
expect(request.method).to.equal('POST'); | ||
}); | ||
|
||
it('Request params check', () => { | ||
let request = spec.buildRequests(bidRequests); | ||
let data = JSON.parse(request.data); | ||
expect(data.at).to.equal(2); //auction type | ||
expect(data.cur[0]).to.equal("USD"); //currency | ||
expect(data.site.domain).to.be.a('string'); // domain should be set | ||
expect(data.site.page).to.equal(bidRequests[0].params.kadpageurl); // forced pageURL | ||
expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id | ||
expect(data.user.yob).to.equal(bidRequests[0].params.yob); // YOB | ||
expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender | ||
expect(data.user.lat).to.equal(bidRequests[0].params.lat); // Latitude | ||
expect(data.user.lon).to.equal(bidRequests[0].params.lon); // Lognitude | ||
expect(data.ext.wrapper.wv).to.equal(constants.REPO_AND_VERSION); // Wrapper Version | ||
expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].transactionId); // Prebid TransactionId | ||
expect(data.ext.wrapper.wiid).to.equal(bidRequests[0].params.wiid); // OpenWrap: Wrapper Impression ID | ||
expect(data.ext.wrapper.profile).to.equal(bidRequests[0].params.profId); // OpenWrap: Wrapper Profile ID | ||
expect(data.ext.wrapper.version).to.equal(bidRequests[0].params.verId); // OpenWrap: Wrapper Profile Version ID | ||
|
||
expect(data.imp[0].id).to.equal(bidRequests[0].bidId); // Prebid bid id is passed as id | ||
expect(data.imp[0].bidfloor).to.equal(parseFloat(bidRequests[0].params.kadfloor)); // kadfloor | ||
expect(data.imp[0].tagid).to.equal('/15671365/DMDemo'); // tagid | ||
expect(data.imp[0].banner.w).to.equal(300); // width | ||
expect(data.imp[0].banner.h).to.equal(250); // height | ||
expect(data.imp[0].ext.pmZoneId).to.equal(bidRequests[0].params.pmzoneid); // pmzoneid | ||
}); | ||
|
||
it('invalid adslot', () => { | ||
bidRequests[0].params.adSlot = '/15671365/DMDemo'; | ||
let request = spec.buildRequests(bidRequests); | ||
expect(request).to.equal(undefined); | ||
}); | ||
}); | ||
|
||
}); | ||
|
||
|
||
|
||
}); |