-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update Adform adapter to Prebid v1.0 (#1947)
* test * update adform bid adapter * update unit tests * Added adform adapter description file * updated tests * Another tests update * add auctionId to adformBidAdapter * Final updates to fit 1.0 version * update docs and integration example * Do not mutate original validBidRequests * use atob and btoa instead of custom made module * Renaming one query string parameter
- Loading branch information
Showing
4 changed files
with
384 additions
and
1 deletion.
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
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,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)); | ||
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; | ||
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); |
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,30 @@ | ||
# Overview | ||
|
||
Module Name: Adform Bidder Adapter | ||
Module Type: Bidder Adapter | ||
Maintainer: Scope.FL.Scripts@adform.com | ||
|
||
# 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' | ||
} | ||
} | ||
] | ||
}, | ||
]; | ||
``` |
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,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; | ||
} |