From 0c53a473964fcf618b14a77d5e09bef763c2b4a6 Mon Sep 17 00:00:00 2001 From: Cary White Date: Mon, 15 Oct 2018 09:17:12 -0700 Subject: [PATCH] Trafficroots Resubmit (#3141) circleci is still failing, but I cannot find a workaround for this PR at the moment --- modules/trafficrootsBidAdapter.js | 129 +++++++++++++++ modules/trafficrootsBidAdapter.md | 37 +++++ .../modules/trafficrootsBidAdapter_spec.js | 149 ++++++++++++++++++ 3 files changed, 315 insertions(+) create mode 100644 modules/trafficrootsBidAdapter.js create mode 100644 modules/trafficrootsBidAdapter.md create mode 100644 test/spec/modules/trafficrootsBidAdapter_spec.js diff --git a/modules/trafficrootsBidAdapter.js b/modules/trafficrootsBidAdapter.js new file mode 100644 index 00000000000..c931ef3b4a4 --- /dev/null +++ b/modules/trafficrootsBidAdapter.js @@ -0,0 +1,129 @@ +import {registerBidder} from 'src/adapters/bidderFactory'; +import * as utils from 'src/utils'; + +const TR_BIDDER_CODE = 'trafficroots'; +const TR_CURRENCY = 'USD'; +const TR_DEFAULT_BID_URL = '//service.trafficroots.com/prebid'; +const TR_TTL = 60; + +const LOCATION_PARAM_NAME = 'siteurl'; +const ID_PARAM_NAME = 'id'; +const IFRAME_PARAM_NAME = 'if'; +const ZONE_ID_PARAM_NAME = 'zoneId'; +const SIZE_PARAM_NAME = 'size'; +const KEYWORDS_PARAM_NAME = 'keywords'; +const MOBILE_PARAM_NAME = 'mobile'; +const TRID_PARAM_NAME = 'trid'; + +const ARRAY_PARAM_SEPARATOR = ';'; +const ARRAY_SIZE_SEPARATOR = ','; +const SIZE_SEPARATOR = 'x'; +const IS_MOBILE = window.navigator.userAgent.toLowerCase().indexOf('mobi'); + +let keywords = () => { + let clean = input => { + return input.replace(/\W/g, ' ').replace(/[ ]{2,}/g, ' ').trim(); + }; + let meta = name => { + let tag = document.querySelector("meta[name='" + name + "']"); + return (tag !== null) ? tag.getAttribute('content') : ''; + }; + return encodeURIComponent( + clean( + meta('keywords') + ' ' + meta('description') + ' ' + document.title + ) + ).substring(0, 400); +}; + +export const spec = { + code: TR_BIDDER_CODE, + isBidRequestValid: function(bid) { + return bid.params && !!bid.params.zoneId; + }, + + buildRequests: function(validBidRequests, bidderRequest) { + let deliveryUrl = ''; + const idParams = []; + const sizeParams = []; + const zoneIds = []; + let trid = ''; + if (window.localStorage) { + try { + var myid = window.localStorage.getItem('trafficroots:trid'); + if (myid) { + trid = myid; + } + } catch (ex) { + } + } + utils._each(validBidRequests, function(bid) { + if (!deliveryUrl && typeof bid.params.deliveryUrl === 'string') { + deliveryUrl = bid.params.deliveryUrl; + } + idParams.push(bid.bidId); + sizeParams.push(bid.sizes.map(size => size.join(SIZE_SEPARATOR)).join(ARRAY_SIZE_SEPARATOR)); + zoneIds.push(bid.params.zoneId); + }); + + if (!deliveryUrl) { + deliveryUrl = TR_DEFAULT_BID_URL; + } + + let data = { + [IFRAME_PARAM_NAME]: 0, + [LOCATION_PARAM_NAME]: utils.getTopWindowUrl(), + [SIZE_PARAM_NAME]: sizeParams.join(ARRAY_PARAM_SEPARATOR), + [ID_PARAM_NAME]: idParams.join(ARRAY_PARAM_SEPARATOR), + [ZONE_ID_PARAM_NAME]: zoneIds.join(ARRAY_PARAM_SEPARATOR), + [MOBILE_PARAM_NAME]: IS_MOBILE, + [KEYWORDS_PARAM_NAME]: decodeURIComponent(keywords()), + [TRID_PARAM_NAME]: trid + }; + + if (bidderRequest && bidderRequest.gdprConsent) { + data.gdpr = { + applies: bidderRequest.gdprConsent.gdprApplies, + consent: bidderRequest.gdprConsent.consentString + }; + } + + return { + method: 'GET', + url: deliveryUrl, + data: data + }; + }, + + interpretResponse: function(serverResponses, request) { + const bidResponses = []; + var tridSet = false; + utils._each(serverResponses.body, function(response) { + if (!tridSet) { + try { + if (window.localStorage) { + window.localStorage.setItem('trafficroots:trid', response.trid); + tridSet = true; + } + } catch (ex) {} + } + if (response.cpm > 0) { + const bidResponse = { + requestId: response.id, + creativeId: response.id, + adId: response.id, + cpm: response.cpm, + width: response.width, + height: response.height, + currency: TR_CURRENCY, + netRevenue: true, + ttl: TR_TTL, + ad: response.ad + }; + bidResponses.push(bidResponse); + } + }); + return bidResponses; + } +}; + +registerBidder(spec); diff --git a/modules/trafficrootsBidAdapter.md b/modules/trafficrootsBidAdapter.md new file mode 100644 index 00000000000..2aceb0c866b --- /dev/null +++ b/modules/trafficrootsBidAdapter.md @@ -0,0 +1,37 @@ +# Overview + +Module Name: Trafficroots Bid Adapter + +Module Type: Bidder Adapter + +Maintainer: cary@trafficroots.com + +# Description + +Module that connects to Trafficroots demand sources + +# Test Parameters +```javascript + + var adUnits = [ + { + code: 'test-div', + sizes: [[300, 250],[300,600]], // a display size + bids: [ + { + bidder: 'trafficroots', + params: { + zoneId: 'aa0444af31', + deliveryUrl: location.protocol + '//service.trafficroots.com/prebid' + } + },{ + bidder: 'trafficroots', + params: { + zoneId: '8f527a4835', + deliveryUrl: location.protocol + '//service.trafficroots.com/prebid' + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/trafficrootsBidAdapter_spec.js b/test/spec/modules/trafficrootsBidAdapter_spec.js new file mode 100644 index 00000000000..40d549e4a5a --- /dev/null +++ b/test/spec/modules/trafficrootsBidAdapter_spec.js @@ -0,0 +1,149 @@ +import { expect } from 'chai'; +import { spec } from 'modules/trafficrootsBidAdapter'; + +describe('trafficrootsAdapterTests', () => { + describe('bidRequestValidity', () => { + it('bidRequest with zoneId and deliveryUrl params', () => { + expect(spec.isBidRequestValid({ + bidder: 'trafficroots', + params: { + zoneId: 'aa0444af31', + deliveryUrl: 'https://service.trafficroosts.com/prebid' + } + })).to.equal(true); + }); + + it('bidRequest with only zoneId', () => { + expect(spec.isBidRequestValid({ + bidder: 'trafficroots', + params: { + zoneId: '8f527a4835' + } + })).to.equal(true); + }); + + it('bidRequest with only deliveryUrl', () => { + expect(spec.isBidRequestValid({ + bidder: 'trafficroots', + params: { + deliveryUrl: 'https://service.trafficroosts.com/prebid' + } + })).to.equal(false); + }); + }); + + describe('bidRequest', () => { + const bidRequests = [{ + 'bidder': 'trafficroots', + 'bidId': '29fa5c08928bde', + 'params': { + 'zoneId': 'aa0444af31', + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'sizes': [[300, 250], [300, 600]], + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757' + }, { + 'bidder': 'trafficroots', + 'bidId': '29fa5c08928bde', + 'params': { + 'zoneId': '8f527a4835', + 'deliveryUrl': 'https://service.trafficroosts.com/prebid' + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'sizes': [[300, 250], [300, 600]], + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757' + }]; + + it('bidRequest method', () => { + const request = spec.buildRequests(bidRequests); + expect(request.method).to.equal('GET'); + }); + + it('bidRequest url', () => { + const request = spec.buildRequests(bidRequests); + expect(request.url).to.match(new RegExp(`${bidRequests[1].params.deliveryUrl}`)); + }); + + it('bidRequest data', () => { + const request = spec.buildRequests(bidRequests); + expect(request.data).to.exists; + }); + + it('bidRequest zoneIds', () => { + const request = spec.buildRequests(bidRequests); + expect(request.data.zoneId).to.equal('aa0444af31;8f527a4835'); + }); + + it('bidRequest gdpr consent', () => { + const consentString = 'consentString'; + const bidderRequest = { + bidderCode: 'trafficroots', + auctionId: '18fd8b8b0bd757', + bidderRequestId: '418b37f85e772c', + timeout: 3000, + gdprConsent: { + consentString: consentString, + gdprApplies: true + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + + expect(request.data.gdpr).to.exist; + expect(request.data.gdpr.applies).to.exist.and.to.be.true; + expect(request.data.gdpr.consent).to.exist.and.to.equal(consentString); + }); + }); + + describe('interpretResponse', () => { + const bidRequest = [{ + 'bidder': 'trafficroots', + 'bidId': '29fa5c08928bde', + 'params': { + 'zoneId': 'aa0444af31', + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'sizes': [[300, 250], [300, 600]], + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757' + }]; + + const bidResponse = { + body: [{ + 'id': 'div-gpt-ad-1460505748561-0', + 'ad': 'test ad', + 'width': 320, + 'height': 250, + 'cpm': 5.2 + }], + headers: {} + }; + + it('required keys', () => { + const result = spec.interpretResponse(bidResponse, bidRequest); + + let requiredKeys = [ + 'requestId', + 'creativeId', + 'adId', + 'cpm', + 'width', + 'height', + 'currency', + 'netRevenue', + 'ttl', + 'ad' + ]; + + let resultKeys = Object.keys(result[0]); + resultKeys.forEach(function(key) { + expect(requiredKeys.indexOf(key) !== -1).to.equal(true); + }); + }) + }); +});