Skip to content

Commit

Permalink
Update Adform adapter to Prebid v1.0 (#1947)
Browse files Browse the repository at this point in the history
* 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
Pupis authored and Matt Kendall committed Jan 10, 2018
1 parent 00e8e92 commit 9f3f16f
Show file tree
Hide file tree
Showing 4 changed files with 384 additions and 1 deletion.
2 changes: 1 addition & 1 deletion integrationExamples/gpt/pbjs_example_gpt.html
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
},
{
bidder: 'adform',
// available params: [ 'mid', 'inv', 'pdom', 'mname', 'mkw', 'mkv', 'cat', 'bcat', 'bcatrt', 'adv', 'advt', 'cntr', 'cntrt', 'maxp', 'minp', 'sminp', 'w', 'h', 'pb', 'pos', 'cturl', 'iturl', 'cttype', 'hidedomain', 'cdims', 'test' ]
// available params: [ 'mid', 'inv', 'pdom', 'mname', 'mkw', 'mkv', 'cat', 'bcat', 'bcatrt', 'adv', 'advt', 'cntr', 'cntrt', 'maxp', 'minp', 'sminp', 'w', 'h', 'pb', 'pos', 'cturl', 'iturl', 'cttype', 'hidedomain', 'cdims', 'test', priceType ]
params: {
adxDomain: 'adx.adform.net', //optional
mid: 158989,
Expand Down
107 changes: 107 additions & 0 deletions modules/adformBidAdapter.js
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);
30 changes: 30 additions & 0 deletions modules/adformBidAdapter.md
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'
}
}
]
},
];
```
246 changes: 246 additions & 0 deletions test/spec/modules/adformBidAdapter_spec.js
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;
}

0 comments on commit 9f3f16f

Please sign in to comment.