From 0a489f4c6972e5d3ba60809be9cd9cc720ff994e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandr=20S=CC=8Cts=CC=8Cepelin?= Date: Thu, 3 Sep 2020 13:45:51 +0300 Subject: [PATCH 1/3] New adapter "Cointraffic" added --- modules/cointrafficBidAdapter.js | 104 +++++++++++++++ modules/cointrafficBidAdapter.md | 24 ++++ .../modules/cointrafficBidAdapter_spec.js | 125 ++++++++++++++++++ 3 files changed, 253 insertions(+) create mode 100644 modules/cointrafficBidAdapter.js create mode 100644 modules/cointrafficBidAdapter.md create mode 100644 test/spec/modules/cointrafficBidAdapter_spec.js diff --git a/modules/cointrafficBidAdapter.js b/modules/cointrafficBidAdapter.js new file mode 100644 index 00000000000..692fa25a59e --- /dev/null +++ b/modules/cointrafficBidAdapter.js @@ -0,0 +1,104 @@ +import * as utils from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; + +const BIDDER_CODE = 'cointraffic'; +const ENDPOINT_URL = 'https://appspb.cointraffic.io/pb/tmp'; + +function detectDevice() { + let hasTouchScreen + if ('maxTouchPoints' in navigator) { + hasTouchScreen = navigator.maxTouchPoints > 0 + } else { + let mQ = window.matchMedia && matchMedia('(pointer:coarse)') + if (mQ && mQ.media === '(pointer:coarse)') { + hasTouchScreen = !!mQ.matches + } else if ('orientation' in window) { + hasTouchScreen = true // deprecated, but good fallback + } else { + // Only as a last resort, fall back to user agent sniffing + let UA = navigator.userAgent + hasTouchScreen = /\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(UA) || /\b(Android|Windows Phone|iPad|iPod)\b/i.test(UA) + } + } + + return hasTouchScreen && window.innerWidth < 1280 ? 'mobile' : 'desktop' +} + +export const spec = { + code: BIDDER_CODE, + + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function (bid) { + return !!(bid.params.placementId); + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param validBidRequests + * @param bidderRequest + * @return Array Info describing the request to the server. + */ + buildRequests: function (validBidRequests, bidderRequest) { + if (validBidRequests.length === 0) { + return []; + } + + const device = detectDevice() + + return validBidRequests.map(bidRequest => { + const sizes = utils.parseSizesInput(bidRequest.params.size || bidRequest.sizes); + + const payload = { + placementId: bidRequest.params.placementId, + device: device, + sizes: sizes, + bidId: bidRequest.bidId, + referer: bidderRequest.refererInfo.referer, + }; + + return { + method: 'POST', + url: ENDPOINT_URL, + data: payload + }; + }); + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @param bidRequest + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse, bidRequest) { + const bidResponses = []; + const response = serverResponse.body; + + if (response) { + const bidResponse = { + requestId: response.requestId, + cpm: response.cpm, + currency: response.currency, + netRevenue: response.netRevenue, + width: response.width, + height: response.height, + creativeId: response.creativeId, + ttl: response.ttl, + ad: response.ad + }; + + bidResponses.push(bidResponse); + } + + return bidResponses; + } +}; + +registerBidder(spec); diff --git a/modules/cointrafficBidAdapter.md b/modules/cointrafficBidAdapter.md new file mode 100644 index 00000000000..5bc1f9cbe5c --- /dev/null +++ b/modules/cointrafficBidAdapter.md @@ -0,0 +1,24 @@ +# Overview + +``` +Module Name: Cointraffic Bidder Adapter +Module Type: Cointraffic Adapter +Maintainer: tech@cointraffic.io +``` + +# Description +The Cointraffic client module makes it easy to implement Cointraffic directly into your website. To get started, simply replace the ``placementId`` with your assigned tracker key. This is dependent on the size required by your account dashboard. For additional information on this module, please contact us at ``support@cointraffic.io``. + +# Test Parameters +``` + var adUnits = [{ + code: 'test-ad-div', + sizes: [[300, 250]], + bids: [{ + bidder: 'cointraffic', + params: { + placementId: 'testPlacementId' + } + }] + }]; +``` diff --git a/test/spec/modules/cointrafficBidAdapter_spec.js b/test/spec/modules/cointrafficBidAdapter_spec.js new file mode 100644 index 00000000000..a52edc60368 --- /dev/null +++ b/test/spec/modules/cointrafficBidAdapter_spec.js @@ -0,0 +1,125 @@ +import { expect } from 'chai'; +import { spec } from 'modules/cointrafficBidAdapter.js'; + +const ENDPOINT_URL = 'https://appspb.cointraffic.io/pb/tmp'; + +describe('cointrafficBidAdapter', function () { + describe('isBidRequestValid', function () { + let bid = { + bidder: 'cointraffic', + params: { + placementId: 'ct_testPlacementId' + }, + adUnitCode: 'adunit-code', + sizes: [ + [300, 250] + ], + bidId: 'bidId12345', + bidderRequestId: 'bidderRequestId12345', + auctionId: 'auctionId12345' + }; + + it('should return true where required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + bidder: 'cointraffic', + params: { + placementId: 'ct_testPlacementId' + }, + adUnitCode: 'adunit-code', + sizes: [ + [300, 250] + ], + bidId: 'bidId12345', + bidderRequestId: 'bidderRequestId12345', + auctionId: 'auctionId12345', + }, + { + bidder: 'cointraffic', + params: { + placementId: 'ct_testPlacementId' + }, + adUnitCode: 'adunit-code2', + sizes: [ + [300, 250] + ], + bidId: 'bidId67890"', + bidderRequestId: 'bidderRequestId67890', + auctionId: 'auctionId12345', + } + ]; + + let bidderRequests = { + refererInfo: { + numIframes: 0, + reachedTop: true, + referer: 'https://example.com', + stack: [ + 'https://example.com' + ] + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequests); + + it('sends bid request to our endpoint via POST', function () { + expect(request[0].method).to.equal('POST'); + expect(request[1].method).to.equal('POST'); + }); + it('attaches source and version to endpoint URL as query params', function () { + expect(request[0].url).to.equal(ENDPOINT_URL); + expect(request[1].url).to.equal(ENDPOINT_URL); + }); + }); + + describe('interpretResponse', function () { + let bidRequest = [ + { + method: 'POST', + url: ENDPOINT_URL, + data: { + placementId: 'ct_testPlacementId', + device: 'desktop', + sizes: ['300x250'], + bidId: 'bidId12345', + referer: 'www.example.com' + } + } + ]; + + let serverResponse = { + body: { + requestId: 'bidId12345', + cpm: 3.9, + currency: 'EUR', + netRevenue: true, + width: 300, + height: 250, + creativeId: 'creativeId12345', + ttl: 90, + ad: '

I am an ad

', + } + }; + + it('should get the correct bid response', function () { + let expectedResponse = [{ + requestId: 'bidId12345', + cpm: 3.9, + currency: 'EUR', + netRevenue: true, + width: 300, + height: 250, + creativeId: 'creativeId12345', + ttl: 90, + ad: '

I am an ad

' + }]; + let result = spec.interpretResponse(serverResponse, bidRequest[0]); + expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); + }); + }); +}); From c4fc3883f892a93d82f45498c480227fa20175f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandr=20S=CC=8Cts=CC=8Cepelin?= Date: Thu, 3 Sep 2020 15:49:52 +0300 Subject: [PATCH 2/3] removed mobile detection --- modules/cointrafficBidAdapter.js | 27 ------------------- .../modules/cointrafficBidAdapter_spec.js | 8 +++--- 2 files changed, 4 insertions(+), 31 deletions(-) diff --git a/modules/cointrafficBidAdapter.js b/modules/cointrafficBidAdapter.js index 692fa25a59e..5dad688f852 100644 --- a/modules/cointrafficBidAdapter.js +++ b/modules/cointrafficBidAdapter.js @@ -4,26 +4,6 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'cointraffic'; const ENDPOINT_URL = 'https://appspb.cointraffic.io/pb/tmp'; -function detectDevice() { - let hasTouchScreen - if ('maxTouchPoints' in navigator) { - hasTouchScreen = navigator.maxTouchPoints > 0 - } else { - let mQ = window.matchMedia && matchMedia('(pointer:coarse)') - if (mQ && mQ.media === '(pointer:coarse)') { - hasTouchScreen = !!mQ.matches - } else if ('orientation' in window) { - hasTouchScreen = true // deprecated, but good fallback - } else { - // Only as a last resort, fall back to user agent sniffing - let UA = navigator.userAgent - hasTouchScreen = /\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(UA) || /\b(Android|Windows Phone|iPad|iPod)\b/i.test(UA) - } - } - - return hasTouchScreen && window.innerWidth < 1280 ? 'mobile' : 'desktop' -} - export const spec = { code: BIDDER_CODE, @@ -45,18 +25,11 @@ export const spec = { * @return Array Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { - if (validBidRequests.length === 0) { - return []; - } - - const device = detectDevice() - return validBidRequests.map(bidRequest => { const sizes = utils.parseSizesInput(bidRequest.params.size || bidRequest.sizes); const payload = { placementId: bidRequest.params.placementId, - device: device, sizes: sizes, bidId: bidRequest.bidId, referer: bidderRequest.refererInfo.referer, diff --git a/test/spec/modules/cointrafficBidAdapter_spec.js b/test/spec/modules/cointrafficBidAdapter_spec.js index a52edc60368..dd03ed53e9b 100644 --- a/test/spec/modules/cointrafficBidAdapter_spec.js +++ b/test/spec/modules/cointrafficBidAdapter_spec.js @@ -8,7 +8,7 @@ describe('cointrafficBidAdapter', function () { let bid = { bidder: 'cointraffic', params: { - placementId: 'ct_testPlacementId' + placementId: 'testPlacementId' }, adUnitCode: 'adunit-code', sizes: [ @@ -29,7 +29,7 @@ describe('cointrafficBidAdapter', function () { { bidder: 'cointraffic', params: { - placementId: 'ct_testPlacementId' + placementId: 'testPlacementId' }, adUnitCode: 'adunit-code', sizes: [ @@ -42,7 +42,7 @@ describe('cointrafficBidAdapter', function () { { bidder: 'cointraffic', params: { - placementId: 'ct_testPlacementId' + placementId: 'testPlacementId' }, adUnitCode: 'adunit-code2', sizes: [ @@ -83,7 +83,7 @@ describe('cointrafficBidAdapter', function () { method: 'POST', url: ENDPOINT_URL, data: { - placementId: 'ct_testPlacementId', + placementId: 'testPlacementId', device: 'desktop', sizes: ['300x250'], bidId: 'bidId12345', From ff68b27168314f60b777cc4a24680a1c91e7deeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandr=20S=CC=8Cts=CC=8Cepelin?= Date: Wed, 9 Sep 2020 11:34:56 +0300 Subject: [PATCH 3/3] The sizes property has been updated, added supportedMediaTypes. --- modules/cointrafficBidAdapter.js | 32 +++++++------ modules/cointrafficBidAdapter.md | 6 ++- .../modules/cointrafficBidAdapter_spec.js | 48 +++++++++++++------ 3 files changed, 57 insertions(+), 29 deletions(-) diff --git a/modules/cointrafficBidAdapter.js b/modules/cointrafficBidAdapter.js index 5dad688f852..aa6860d1fc6 100644 --- a/modules/cointrafficBidAdapter.js +++ b/modules/cointrafficBidAdapter.js @@ -1,11 +1,13 @@ import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER} from '../src/mediaTypes.js' const BIDDER_CODE = 'cointraffic'; const ENDPOINT_URL = 'https://appspb.cointraffic.io/pb/tmp'; export const spec = { code: BIDDER_CODE, + supportedMediaTypes: [BANNER], /** * Determines whether or not the given bid request is valid. @@ -54,22 +56,24 @@ export const spec = { const bidResponses = []; const response = serverResponse.body; - if (response) { - const bidResponse = { - requestId: response.requestId, - cpm: response.cpm, - currency: response.currency, - netRevenue: response.netRevenue, - width: response.width, - height: response.height, - creativeId: response.creativeId, - ttl: response.ttl, - ad: response.ad - }; - - bidResponses.push(bidResponse); + if (utils.isEmpty(response)) { + return bidResponses; } + const bidResponse = { + requestId: response.requestId, + cpm: response.cpm, + currency: response.currency, + netRevenue: response.netRevenue, + width: response.width, + height: response.height, + creativeId: response.creativeId, + ttl: response.ttl, + ad: response.ad + }; + + bidResponses.push(bidResponse); + return bidResponses; } }; diff --git a/modules/cointrafficBidAdapter.md b/modules/cointrafficBidAdapter.md index 5bc1f9cbe5c..ad608a1319e 100644 --- a/modules/cointrafficBidAdapter.md +++ b/modules/cointrafficBidAdapter.md @@ -13,7 +13,11 @@ The Cointraffic client module makes it easy to implement Cointraffic directly in ``` var adUnits = [{ code: 'test-ad-div', - sizes: [[300, 250]], + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, bids: [{ bidder: 'cointraffic', params: { diff --git a/test/spec/modules/cointrafficBidAdapter_spec.js b/test/spec/modules/cointrafficBidAdapter_spec.js index dd03ed53e9b..6d948e36cb9 100644 --- a/test/spec/modules/cointrafficBidAdapter_spec.js +++ b/test/spec/modules/cointrafficBidAdapter_spec.js @@ -92,21 +92,21 @@ describe('cointrafficBidAdapter', function () { } ]; - let serverResponse = { - body: { - requestId: 'bidId12345', - cpm: 3.9, - currency: 'EUR', - netRevenue: true, - width: 300, - height: 250, - creativeId: 'creativeId12345', - ttl: 90, - ad: '

I am an ad

', - } - }; - it('should get the correct bid response', function () { + let serverResponse = { + body: { + requestId: 'bidId12345', + cpm: 3.9, + currency: 'EUR', + netRevenue: true, + width: 300, + height: 250, + creativeId: 'creativeId12345', + ttl: 90, + ad: '

I am an ad

', + } + }; + let expectedResponse = [{ requestId: 'bidId12345', cpm: 3.9, @@ -121,5 +121,25 @@ describe('cointrafficBidAdapter', function () { let result = spec.interpretResponse(serverResponse, bidRequest[0]); expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); }); + + it('should get empty bid response if server response body is empty', function () { + let serverResponse = { + body: {} + }; + + let expectedResponse = []; + + let result = spec.interpretResponse(serverResponse, bidRequest[0]); + expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); + }); + + it('should get empty bid response if no server response', function () { + let serverResponse = {}; + + let expectedResponse = []; + + let result = spec.interpretResponse(serverResponse, bidRequest[0]); + expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); + }); }); });