From 0b520f03027bd8f893957509dd3c9c2c8507c933 Mon Sep 17 00:00:00 2001
From: Jurij Sinickij <jurij.sinickij@adform.com>
Date: Tue, 27 Sep 2016 13:57:36 +0300
Subject: [PATCH 1/6] params whitelisting removed

---
 src/adapters/adform.js | 43 ++++++++++++++++++++++--------------------
 1 file changed, 23 insertions(+), 20 deletions(-)

diff --git a/src/adapters/adform.js b/src/adapters/adform.js
index 5261b526c70..8a0239ab3bf 100644
--- a/src/adapters/adform.js
+++ b/src/adapters/adform.js
@@ -10,29 +10,38 @@ function AdformAdapter() {
   };
 
   function _callBids(params) {
-    //var callbackName = '_adf_' + utils.getUniqueIdentifierStr();
-    var bid;
-    var noDomain = true;
+    var bid, _value, _key, i, j, k, l;
     var bids = params.bids;
     var request = [];
     var callbackName = '_adf_' + utils.getUniqueIdentifierStr();
+    var globalParams = [ [ 'adxDomain', 'adx.adform.net' ], [ 'url', null ], [ 'callback', '$$PREBID_GLOBAL$$.' + callbackName ] ];
 
-    for (var i = 0, l = bids.length; i < l; i++) {
+    for (i = 0, l = bids.length; i < l; i++) {
       bid = bids[i];
-      if (bid.adxDomain && noDomain) {
-        noDomain = false;
-        request.unshift('//' + bid.adxDomain + '/adx/?rp=4');
+
+      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;
+        }
       }
 
       request.push(formRequestUrl(bid.params));
     }
 
-    if (noDomain) {
-      request.unshift('//adx.adform.net/adx/?rp=4');
+    request.unshift('//' + globalParams[0][1]+ '/adx/?rp=4');
+
+    for (i = 1, l = globalParams.length; i < l; i++) {
+      _key = globalParams[i][0];
+      _value = globalParams[i][1];
+      if (_value) {
+        request.push(globalParams[i][0] + '='+ encodeURIComponent(_value));
+      }
     }
 
     $$PREBID_GLOBAL$$[callbackName] = handleCallback(bids);
-    request.push('callback=$$PREBID_GLOBAL$$.' + callbackName);
 
     adloader.loadScript(request.join('&'));
   }
@@ -41,18 +50,12 @@ function AdformAdapter() {
     var key;
     var url = [];
 
-    var validProps = [
-        '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'
-    ];
-
-    for (var i = 0, l = validProps.length; i < l; i++) {
-      key = validProps[i];
-      if (reqData.hasOwnProperty(key))
-          url.push(key, '=', reqData[key], '&');
+    for (key in reqData) {
+      if (reqData[key])
+        url.push(key, '=', reqData[key], '&');
     }
 
-    return encode64(url.join(''));
+    return encode64(url.join('').slice(0, -1));
   }
 
   function handleCallback(bids) {

From 500382de2a23123577869eafd8323f318659ac1d Mon Sep 17 00:00:00 2001
From: Jurij Sinickij <jurij.sinickij@adform.com>
Date: Tue, 27 Sep 2016 14:30:50 +0300
Subject: [PATCH 2/6] sanity check added

---
 src/adapters/adform.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/adapters/adform.js b/src/adapters/adform.js
index 8a0239ab3bf..fa826aa59c2 100644
--- a/src/adapters/adform.js
+++ b/src/adapters/adform.js
@@ -51,7 +51,7 @@ function AdformAdapter() {
     var url = [];
 
     for (key in reqData) {
-      if (reqData[key])
+      if (reqData.hasOwnProperty(key) && reqData[key])
         url.push(key, '=', reqData[key], '&');
     }
 

From 6d5148053d4dcbaaf444cc2a8efce22c1c274a16 Mon Sep 17 00:00:00 2001
From: Jurij Sinickij <jurij.sinickij@adform.com>
Date: Tue, 27 Sep 2016 18:22:00 +0300
Subject: [PATCH 3/6] global request parameter tid added

---
 src/adapters/adform.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/adapters/adform.js b/src/adapters/adform.js
index fa826aa59c2..b5d7fea3f8a 100644
--- a/src/adapters/adform.js
+++ b/src/adapters/adform.js
@@ -14,7 +14,7 @@ function AdformAdapter() {
     var bids = params.bids;
     var request = [];
     var callbackName = '_adf_' + utils.getUniqueIdentifierStr();
-    var globalParams = [ [ 'adxDomain', 'adx.adform.net' ], [ 'url', null ], [ 'callback', '$$PREBID_GLOBAL$$.' + callbackName ] ];
+    var globalParams = [ [ 'adxDomain', 'adx.adform.net' ], [ 'url', null ], [ 'tid', null ], [ 'callback', '$$PREBID_GLOBAL$$.' + callbackName ] ];
 
     for (i = 0, l = bids.length; i < l; i++) {
       bid = bids[i];

From 001785bbcb989cc94270bf8c9e476d68a755ba13 Mon Sep 17 00:00:00 2001
From: Jurij Sinickij <jurij.sinickij@adform.com>
Date: Thu, 29 Sep 2016 17:16:02 +0300
Subject: [PATCH 4/6] tests for adform adapter added

---
 test/spec/adapters/adform_spec.js | 168 ++++++++++++++++++++++++++++++
 1 file changed, 168 insertions(+)
 create mode 100644 test/spec/adapters/adform_spec.js

diff --git a/test/spec/adapters/adform_spec.js b/test/spec/adapters/adform_spec.js
new file mode 100644
index 00000000000..e4e462bca86
--- /dev/null
+++ b/test/spec/adapters/adform_spec.js
@@ -0,0 +1,168 @@
+// jshint esversion: 6
+
+import { assert } from 'chai';
+import * as utils from '../../../src/utils';
+import adLoader from '../../../src/adloader';
+import bidManager from '../../../src/bidmanager';
+import adapter from '../../../src/adapters/adform';
+
+describe('Adform adapter', () => {
+  let _adapter, sandbox; 
+
+  describe('request', () => {
+    it('should create callback method on PREBID_GLOBAL', () => {
+      assert.typeOf($$PREBID_GLOBAL$$._adf_callback, 'function');
+    });
+
+    it('should pass multiple bids via single request', () => {
+      const _request = adLoader.loadScript;
+
+      assert(_request.calledOnce);
+      assert.lengthOf(_request.args[0], 1);
+      assert.lengthOf(parseUrl(_request.args[0][0]).items, 3);
+    });
+
+    it('should handle global request parameters', () => {
+      const _request = parseUrl(adLoader.loadScript.args[0][0]);
+      const _query = _request.query;
+
+      assert.equal(_request.path, '//newdomain/adx');
+      assert.equal(_query.callback.split('.')[1], '_adf_callback');
+      assert.equal(_query.tid, 145);
+      assert.equal(_query.rp, 4);
+      assert.equal(_query.url, encodeURIComponent('some// there'));
+    });
+
+
+    it('should correctly form bid items', () => {
+      const _items = parseUrl(adLoader.loadScript.args[0][0]).items;
+
+      assert.deepEqual(_items[0], { mid: '1' });
+      assert.deepEqual(_items[1], { mid: '2', someVar: 'someValue' });
+      assert.deepEqual(_items[2], { mid: '3', pdom: 'home' });
+    });
+  });
+
+  describe('response callback', () => {
+    it('should create bid response item for every requested item', () => {
+      assert(bidManager.addBidResponse.calledThrice);
+    });
+
+    it('should correctly form bid response object', () => {
+      const _bid = bidManager.addBidResponse.firstCall.args;
+      const _bidObject = _bid[1];
+
+      assert.equal(_bid[0], 'code-1');
+      assert.equal(_bidObject.statusMessage, 'Bid available');
+      assert.equal(_bidObject.bidderCode, 'adform');
+      assert.equal(_bidObject.cpm, 1.1);
+      assert.equal(_bidObject.cur, 'EUR');
+      assert.equal(_bidObject.ad, '<tag>');
+      assert.equal(_bidObject.width, 90);
+      assert.equal(_bidObject.height, 90);
+    });
+
+    it('should correctly form empty bid response object', () => {
+      const _bid = bidManager.addBidResponse.secondCall.args;
+      const _bidObject = _bid[1];
+
+      assert.equal(_bid[0], 'code-2');
+      assert.equal(_bidObject.statusMessage, 'Bid returned empty or error response');
+      assert.equal(_bidObject.bidderCode, 'adform');
+    });
+
+    it('should filter out item which does not fit required size', () => {
+      const _bid = bidManager.addBidResponse.thirdCall.args;
+      const _bidObject = _bid[1];
+
+      assert.equal(_bid[0], 'code-3');
+      assert.equal(_bidObject.statusMessage, 'Bid returned empty or error response');
+      assert.equal(_bidObject.bidderCode, 'adform');
+    });
+
+    beforeEach(() => {
+      sandbox.stub(bidManager, 'addBidResponse');
+      $$PREBID_GLOBAL$$._adf_callback([
+        {
+          response: 'banner',
+          width: 90,
+          height: 90,
+          banner: '<tag>',
+          win_bid: 1.1,
+          win_cur: 'EUR'
+        },
+        {},
+        {
+          response: 'banner',
+          width: 50,
+          height: 50,
+          banner: '<tag>'
+        }
+      ]);
+    });
+  });
+
+  beforeEach(() => {
+    _adapter = adapter();
+    utils.getUniqueIdentifierStr = () => 'callback';
+    sandbox = sinon.sandbox.create();
+    sandbox.stub(adLoader, 'loadScript');
+    _adapter.callBids({
+      bids: [
+        {
+          placementCode: 'code-1',
+          sizes: [ [ 100, 100], [ 90, 90 ] ],
+          params: {
+            mid: 1,
+            url: 'some// there'
+          },
+          adxDomain: 'newdomain',
+          tid: 45
+        },
+        {
+          placementCode: 'code-2',
+          sizes: [ [ 100, 100] ],
+          params: {
+            mid: 2,
+            tid: 145,
+            someVar: 'someValue'
+          }
+        },
+        {
+          placementCode: 'code-3',
+          sizes: [ [ 50, 40], [ 40, 50 ] ],
+          params: {
+            mid: 3,
+            pdom: 'home'
+          }
+        }
+    ]});
+  });
+
+  afterEach(() => {
+    sandbox.restore();
+  });
+});
+
+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(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;
+}
\ No newline at end of file

From 2159b395af921b811d05c950f2de899c95f8f39b Mon Sep 17 00:00:00 2001
From: Sergey Grigorenko <s.grigorenko@adform.com>
Date: Wed, 19 Oct 2016 12:33:42 +0300
Subject: [PATCH 5/6] Add deal id handling for Adform adapter

---
 src/adapters/adform.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/adapters/adform.js b/src/adapters/adform.js
index b5d7fea3f8a..61b013315f8 100644
--- a/src/adapters/adform.js
+++ b/src/adapters/adform.js
@@ -77,6 +77,7 @@ function AdformAdapter() {
           bidObject.ad = adItem.banner;
           bidObject.width = adItem.width;
           bidObject.height = adItem.height;
+          bidObject.dealId = adItem.deal_id;
           bidmanager.addBidResponse(bid.placementCode, bidObject);
         } else {
           bidObject = bidfactory.createBid(2);

From ad6661d3af75b6ca342a872b0a2a3df67537aca5 Mon Sep 17 00:00:00 2001
From: Jurij Sinickij <jurij.sinickij@adform.com>
Date: Wed, 19 Oct 2016 15:26:48 +0300
Subject: [PATCH 6/6] testcase for dealId passing added

---
 test/spec/adapters/adform_spec.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/test/spec/adapters/adform_spec.js b/test/spec/adapters/adform_spec.js
index e4e462bca86..3b559a11589 100644
--- a/test/spec/adapters/adform_spec.js
+++ b/test/spec/adapters/adform_spec.js
@@ -60,6 +60,7 @@ describe('Adform adapter', () => {
       assert.equal(_bidObject.ad, '<tag>');
       assert.equal(_bidObject.width, 90);
       assert.equal(_bidObject.height, 90);
+      assert.equal(_bidObject.dealId, 'deal-1');
     });
 
     it('should correctly form empty bid response object', () => {
@@ -89,7 +90,8 @@ describe('Adform adapter', () => {
           height: 90,
           banner: '<tag>',
           win_bid: 1.1,
-          win_cur: 'EUR'
+          win_cur: 'EUR',
+          deal_id: 'deal-1'
         },
         {},
         {