-
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
Update Adform adapter to Prebid v1.0 #1947
Changes from all commits
3aa3b53
84f662c
a6dbc4a
2f74c76
0fc8d12
f6a51be
a7b9408
29e7931
648ed8a
138f956
8df70f0
4e6a0ee
2879bd4
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 |
---|---|---|
@@ -0,0 +1,107 @@ | ||
'use strict'; | ||
|
||
import {registerBidder} from 'src/adapters/bidderFactory'; | ||
|
||
const BIDDER_CODE = 'adform'; | ||
export const spec = { | ||
code: BIDDER_CODE, | ||
supportedMediaTypes: [], | ||
isBidRequestValid: function (bid) { | ||
return !!(bid.params.mid); | ||
}, | ||
buildRequests: function (validBidRequests) { | ||
var i, l, j, k, bid, _key, _value, reqParams; | ||
var request = []; | ||
var globalParams = [ [ 'adxDomain', 'adx.adform.net' ], [ 'fd', 1 ], [ 'url', null ], [ 'tid', null ] ]; | ||
var netRevenue = 'net'; | ||
var bids = JSON.parse(JSON.stringify(validBidRequests)); | ||
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 copy function in utils |
||
for (i = 0, l = bids.length; i < l; i++) { | ||
bid = bids[i]; | ||
if (bid.params.priceType === 'gross') { | ||
netRevenue = 'gross'; | ||
} | ||
for (j = 0, k = globalParams.length; j < k; j++) { | ||
_key = globalParams[j][0]; | ||
_value = bid[_key] || bid.params[_key]; | ||
if (_value) { | ||
bid[_key] = bid.params[_key] = null; | ||
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. Why are you mutating the bids? Is there a way you can do this without mutating the original bid objects? 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. Hi, changes were made to avoid mutating original bid objects. |
||
globalParams[j][1] = _value; | ||
} | ||
} | ||
reqParams = bid.params; | ||
reqParams.transactionId = bid.transactionId; | ||
request.push(formRequestUrl(reqParams)); | ||
} | ||
|
||
request.unshift('//' + globalParams[0][1] + '/adx/?rp=4'); | ||
|
||
request.push('stid=' + validBidRequests[0].requestId); | ||
|
||
for (i = 1, l = globalParams.length; i < l; i++) { | ||
_key = globalParams[i][0]; | ||
_value = globalParams[i][1]; | ||
if (_value) { | ||
request.push(_key + '=' + encodeURIComponent(_value)); | ||
} | ||
} | ||
|
||
return { | ||
method: 'GET', | ||
url: request.join('&'), | ||
bids: validBidRequests, | ||
netRevenue: netRevenue, | ||
bidder: 'adform' | ||
}; | ||
|
||
function formRequestUrl(reqData) { | ||
var key; | ||
var url = []; | ||
|
||
for (key in reqData) { | ||
if (reqData.hasOwnProperty(key) && reqData[key]) { url.push(key, '=', reqData[key], '&'); } | ||
} | ||
|
||
return encodeURIComponent(btoa(url.join('').slice(0, -1))); | ||
} | ||
}, | ||
interpretResponse: function (serverResponse, bidRequest) { | ||
var bidObject, response, bid; | ||
var bidRespones = []; | ||
var bids = bidRequest.bids; | ||
var responses = serverResponse.body; | ||
for (var i = 0; i < responses.length; i++) { | ||
response = responses[i]; | ||
bid = bids[i]; | ||
if (response.response === 'banner' && verifySize(response, bid.sizes)) { | ||
bidObject = { | ||
requestId: bid.bidId, | ||
cpm: response.win_bid, | ||
width: response.width, | ||
height: response.height, | ||
creativeId: bid.bidId, | ||
dealId: response.deal_id, | ||
currency: response.win_cur, | ||
netRevenue: bidRequest.netRevenue !== 'gross', | ||
ttl: 360, | ||
ad: response.banner, | ||
bidderCode: bidRequest.bidder, | ||
transactionId: bid.transactionId | ||
}; | ||
bidRespones.push(bidObject); | ||
} | ||
} | ||
|
||
return bidRespones; | ||
|
||
function verifySize(adItem, validSizes) { | ||
for (var j = 0, k = validSizes.length; j < k; j++) { | ||
if (adItem.width === validSizes[j][0] && | ||
adItem.height === validSizes[j][1]) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
} | ||
}; | ||
registerBidder(spec); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# Overview | ||
|
||
Module Name: Adform Bidder Adapter | ||
Module Type: Bidder Adapter | ||
Maintainer: [email protected] | ||
|
||
# Description | ||
|
||
Module that connects to Adform demand sources to fetch bids. | ||
Banner formats are supported. | ||
|
||
# Test Parameters | ||
``` | ||
var adUnits = [ | ||
{ | ||
code: 'div-gpt-ad-1460505748561-0', | ||
sizes: [[300, 250], [250, 300], [300, 600], [600, 300]], // a display size | ||
bids: [ | ||
{ | ||
bidder: "adform", | ||
params: { | ||
adxDomain: 'adx.adform.net', //optional | ||
mid: '292063', | ||
priceType: 'gross' // default is 'net' | ||
} | ||
} | ||
] | ||
}, | ||
]; | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
import {assert, expect} from 'chai'; | ||
import * as url from 'src/url'; | ||
import {spec} from 'modules/adformBidAdapter'; | ||
|
||
describe('Adform adapter', () => { | ||
let serverResponse, bidRequest, bidResponses; | ||
let bids = []; | ||
describe('isBidRequestValid', () => { | ||
let bid = { | ||
'bidder': 'adform', | ||
'params': { | ||
'mid': '19910113' | ||
} | ||
}; | ||
|
||
it('should return true when required params found', () => { | ||
assert(spec.isBidRequestValid(bid)); | ||
}); | ||
|
||
it('should return false when required params are missing', () => { | ||
bid.params = { | ||
adxDomain: 'adx.adform.net' | ||
}; | ||
assert.isFalse(spec.isBidRequestValid(bid)); | ||
}) | ||
}); | ||
|
||
describe('buildRequests', () => { | ||
it('should pass multiple bids via single request', () => { | ||
let request = spec.buildRequests(bids); | ||
let parsedUrl = parseUrl(request.url); | ||
assert.lengthOf(parsedUrl.items, 3); | ||
}); | ||
|
||
it('should handle global request parameters', () => { | ||
let parsedUrl = parseUrl(spec.buildRequests([bids[0]]).url); | ||
let query = parsedUrl.query; | ||
|
||
assert.equal(parsedUrl.path, '//newDomain/adx'); | ||
assert.equal(query.tid, 45); | ||
assert.equal(query.rp, 4); | ||
assert.equal(query.fd, 1); | ||
assert.equal(query.stid, '7aefb970-2045'); | ||
assert.equal(query.url, encodeURIComponent('some// there')); | ||
}); | ||
|
||
it('should set correct request method', () => { | ||
let request = spec.buildRequests([bids[0]]); | ||
assert.equal(request.method, 'GET'); | ||
}); | ||
|
||
it('should correctly form bid items', () => { | ||
let bidList = bids; | ||
let request = spec.buildRequests(bidList); | ||
let parsedUrl = parseUrl(request.url); | ||
assert.deepEqual(parsedUrl.items, [ | ||
{ | ||
mid: '1', | ||
transactionId: '5f33781f-9552-4ca1' | ||
}, | ||
{ | ||
mid: '2', | ||
someVar: 'someValue', | ||
transactionId: '5f33781f-9552-4iuy', | ||
priceType: 'gross' | ||
}, | ||
{ | ||
mid: '3', | ||
pdom: 'home', | ||
transactionId: '5f33781f-9552-7ev3' | ||
} | ||
]); | ||
}); | ||
|
||
it('should not change original validBidRequests object', () => { | ||
var resultBids = JSON.parse(JSON.stringify(bids[0])); | ||
let request = spec.buildRequests([bids[0]]); | ||
assert.deepEqual(resultBids, bids[0]); | ||
}); | ||
}); | ||
|
||
describe('interpretResponse', () => { | ||
it('should respond with empty response when there is empty serverResponse', () => { | ||
let result = spec.interpretResponse({ body: {}}, {}); | ||
assert.deepEqual(result, []); | ||
}); | ||
it('should respond with empty response when sizes doesn\'t match', () => { | ||
serverResponse.body[0].response = 'banner'; | ||
serverResponse.body[0].width = 100; | ||
serverResponse.body[0].height = 150; | ||
|
||
serverResponse.body = [serverResponse.body[0]]; | ||
bidRequest.bids = [bidRequest.bids[0]]; | ||
let result = spec.interpretResponse(serverResponse, bidRequest); | ||
|
||
assert.equal(serverResponse.body.length, 1); | ||
assert.equal(serverResponse.body[0].response, 'banner'); | ||
assert.deepEqual(result, []); | ||
}); | ||
it('should respond with empty response when response from server is not banner', () => { | ||
serverResponse.body[0].response = 'not banner'; | ||
serverResponse.body = [serverResponse.body[0]]; | ||
bidRequest.bids = [bidRequest.bids[0]]; | ||
let result = spec.interpretResponse(serverResponse, bidRequest); | ||
|
||
assert.deepEqual(result, []); | ||
}); | ||
it('should interpret server response correctly with one bid', () => { | ||
serverResponse.body = [serverResponse.body[0]]; | ||
bidRequest.bids = [bidRequest.bids[0]]; | ||
let result = spec.interpretResponse(serverResponse, bidRequest)[0]; | ||
|
||
assert.equal(result.requestId, '2a0cf4e'); | ||
assert.equal(result.cpm, 13.9); | ||
assert.equal(result.width, 300); | ||
assert.equal(result.height, 250); | ||
assert.equal(result.creativeId, '2a0cf4e'); | ||
assert.equal(result.dealId, '123abc'); | ||
assert.equal(result.currency, 'EUR'); | ||
assert.equal(result.netRevenue, true); | ||
assert.equal(result.ttl, 360); | ||
assert.equal(result.ad, '<tag1>'); | ||
assert.equal(result.bidderCode, 'adform'); | ||
assert.equal(result.transactionId, '5f33781f-9552-4ca1'); | ||
}); | ||
|
||
it('should set correct netRevenue', () => { | ||
serverResponse.body = [serverResponse.body[0]]; | ||
bidRequest.bids = [bidRequest.bids[1]]; | ||
bidRequest.netRevenue = 'gross'; | ||
let result = spec.interpretResponse(serverResponse, bidRequest)[0]; | ||
|
||
assert.equal(result.netRevenue, false); | ||
}); | ||
|
||
it('should create bid response item for every requested item', () => { | ||
let result = spec.interpretResponse(serverResponse, bidRequest); | ||
assert.lengthOf(result, 3); | ||
}); | ||
}); | ||
|
||
beforeEach(() => { | ||
let sizes = [[250, 300], [300, 250], [300, 600]]; | ||
let adUnitCode = ['div-01', 'div-02', 'div-03']; | ||
let placementCode = adUnitCode; | ||
let params = [{ mid: 1, url: 'some// there' }, {adxDomain: null, mid: 2, someVar: 'someValue', priceType: 'gross'}, { adxDomain: null, mid: 3, pdom: 'home' }]; | ||
bids = [ | ||
{ | ||
adUnitCode: placementCode[0], | ||
bidId: '2a0cf4e', | ||
bidder: 'adform', | ||
bidderRequestId: '1ab8d9', | ||
params: params[0], | ||
adxDomain: 'newDomain', | ||
tid: 45, | ||
placementCode: placementCode[0], | ||
requestId: '7aefb970-2045', | ||
sizes: [[300, 250], [250, 300], [300, 600], [600, 300]], | ||
transactionId: '5f33781f-9552-4ca1' | ||
}, | ||
{ | ||
adUnitCode: placementCode[1], | ||
bidId: '2a0cf5b', | ||
bidder: 'adform', | ||
bidderRequestId: '1ab8d9', | ||
params: params[1], | ||
placementCode: placementCode[1], | ||
requestId: '7aefb970-2045', | ||
sizes: [[300, 250], [250, 300], [300, 600], [600, 300]], | ||
transactionId: '5f33781f-9552-4iuy' | ||
}, | ||
{ | ||
adUnitCode: placementCode[2], | ||
bidId: '2a0cf6n', | ||
bidder: 'adform', | ||
bidderRequestId: '1ab8d9', | ||
params: params[2], | ||
placementCode: placementCode[2], | ||
requestId: '7aefb970-2045', | ||
sizes: [[300, 250], [250, 300], [300, 600], [600, 300]], | ||
transactionId: '5f33781f-9552-7ev3' | ||
} | ||
]; | ||
serverResponse = { | ||
body: [ | ||
{ | ||
banner: '<tag1>', | ||
deal_id: '123abc', | ||
height: 250, | ||
response: 'banner', | ||
width: 300, | ||
win_bid: 13.9, | ||
win_cur: 'EUR' | ||
}, | ||
{ | ||
banner: '<tag2>', | ||
deal_id: '123abc', | ||
height: 300, | ||
response: 'banner', | ||
width: 250, | ||
win_bid: 13.9, | ||
win_cur: 'EUR' | ||
}, | ||
{ | ||
banner: '<tag3>', | ||
deal_id: '123abc', | ||
height: 300, | ||
response: 'banner', | ||
width: 600, | ||
win_bid: 10, | ||
win_cur: 'EUR' | ||
} | ||
], | ||
headers: {} | ||
}; | ||
bidRequest = { | ||
bidder: 'adform', | ||
bids: bids, | ||
method: 'GET', | ||
url: 'url' | ||
}; | ||
}); | ||
}); | ||
|
||
function parseUrl(url) { | ||
const parts = url.split('/'); | ||
const query = parts.pop().split('&'); | ||
return { | ||
path: parts.join('/'), | ||
items: query | ||
.filter((i) => !~i.indexOf('=')) | ||
.map((i) => atob(decodeURIComponent(i)) | ||
.split('&') | ||
.reduce(toObject, {})), | ||
query: query | ||
.filter((i) => ~i.indexOf('=')) | ||
.map((i) => i.replace('?', '')) | ||
.reduce(toObject, {}) | ||
}; | ||
} | ||
|
||
function toObject(cache, string) { | ||
const keyValue = string.split('='); | ||
cache[keyValue[0]] = keyValue[1]; | ||
return cache; | ||
} |
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.
nit: prefer
const
|let