From 4b128af99d82b106102c151c0d749d33991b5b76 Mon Sep 17 00:00:00 2001 From: Keiran Flanigan Date: Wed, 21 Nov 2018 19:55:03 -0800 Subject: [PATCH 01/12] new TapHype bidder adapter --- modules/taphypeBidAdapter.js | 48 ++++++++++++++++++ modules/taphypeBidAdapter.md | 32 ++++++++++++ test/spec/modules/taphypeBidAdapter_spec.js | 56 +++++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 modules/taphypeBidAdapter.js create mode 100644 modules/taphypeBidAdapter.md create mode 100644 test/spec/modules/taphypeBidAdapter_spec.js diff --git a/modules/taphypeBidAdapter.js b/modules/taphypeBidAdapter.js new file mode 100644 index 00000000000..3e8b354fc31 --- /dev/null +++ b/modules/taphypeBidAdapter.js @@ -0,0 +1,48 @@ +import {BANNER} from 'src/mediaTypes'; +import {registerBidder} from 'src/adapters/bidderFactory'; + +export const spec = { + code: 'taphype', + supportedMediaTypes: [BANNER], + isBidRequestValid: function (bid) { + return !!bid.params.placementId; + }, + buildRequests: function (bidRequests) { + const requests = bidRequests.map(function (bid) { + const params = { + placementId: bid.params.placementId, + url: encodeURIComponent(window.location.href), + size: bid.sizes[0][0] + 'x' + bid.sizes[0][1], + rnd: Math.random(), + bidId: bid.bidId, + }; + + return {method: 'GET', url: 'https://us-central1-taphype-internal.cloudfunctions.net/th-prebid', data: params, options: {withCredentials: false}} + }); + + return requests; + }, + interpretResponse: function (serverResponse, bidRequest) { + console.log(serverResponse); + if (!serverResponse || !serverResponse.body || !serverResponse.body.ad) { + return []; + } + + const bid = serverResponse.body; + const sizes = bid.size.split(','); + + return [{ + requestId: bidRequest.data.bidId, + cpm: bid.price, + width: sizes[0], + height: sizes[1], + creativeId: bidRequest.data.bidId, + currency: bid.currency || 'USD', + netRevenue: true, + ad: bid.ad, + ttl: 360 + }]; + }, +}; + +registerBidder(spec); diff --git a/modules/taphypeBidAdapter.md b/modules/taphypeBidAdapter.md new file mode 100644 index 00000000000..c6ff40a42ba --- /dev/null +++ b/modules/taphypeBidAdapter.md @@ -0,0 +1,32 @@ +# Overview + +Module Name: TapHype Bidder Adapter +Module Type: Bidder Adapter +Maintainer: admin@taphype.com + +# Description + +You can use this adapter to get a bid from taphype.com. + + +# Test Parameters +```javascript + var adUnits = [ + { + code: 'div-taphype-example', + sizes: [[300, 250]], + bids: [ + { + bidder: "taphype", + params: { + placementId: 12345 + } + } + ] + } + ]; +``` + +Where: + +* placementId - TapHype Placement ID diff --git a/test/spec/modules/taphypeBidAdapter_spec.js b/test/spec/modules/taphypeBidAdapter_spec.js new file mode 100644 index 00000000000..2fcdd964520 --- /dev/null +++ b/test/spec/modules/taphypeBidAdapter_spec.js @@ -0,0 +1,56 @@ +import { expect } from 'chai'; +import { spec } from 'modules/taphypeBidAdapter'; + +describe('taphypeBidAdapterTests', function () { + it('validate_pub_params', function () { + expect(spec.isBidRequestValid({ + bidder: 'taphype', + params: { + placementId: 12345 + } + })).to.equal(true); + }); + + it('validate_generated_params', function () { + let bidRequestData = [{ + bidId: 'bid12345', + bidder: 'taphype', + params: { + placementId: 12345 + }, + sizes: [[300, 250]] + }]; + + let request = spec.buildRequests(bidRequestData); + let req_data = request[0].data; + + expect(req_data.bidId).to.equal('bid12345'); + }); + + it('validate_response_params', function () { + let bidRequestData = { + data: { + bidId: 'bid12345' + } + }; + + let serverResponse = { + body: { + price: 1.23, + ad: '', + size: '300,250' + } + }; + + let bids = spec.interpretResponse(serverResponse, bidRequestData); + expect(bids).to.have.lengthOf(1); + let bid = bids[0]; + expect(bid.cpm).to.equal(1.23); + expect(bid.currency).to.equal('USD'); + expect(bid.width).to.equal('300'); + expect(bid.height).to.equal('250'); + expect(bid.netRevenue).to.equal(true); + expect(bid.requestId).to.equal('bid12345'); + expect(bid.ad).to.equal(''); + }); +}); From e90b5d5597cf779d6dc8deda7d160556dac92d42 Mon Sep 17 00:00:00 2001 From: hdeodhar <35999856+hdeodhar@users.noreply.github.com> Date: Mon, 26 Nov 2018 16:42:00 +0000 Subject: [PATCH 02/12] Added 930x600 to Rubicon Adapter (#3323) --- modules/rubiconBidAdapter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 415027ef500..1eef3b4813d 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -50,6 +50,7 @@ var sizeMap = { 61: '1000x1000', 64: '580x500', 65: '640x480', + 66: '930x600', 67: '320x480', 68: '1800x1000', 72: '320x320', From 7990f9278bb721db2baaaf7f7bd696c9972420a7 Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Tue, 27 Nov 2018 11:46:31 +0530 Subject: [PATCH 03/12] PubMatic adapter to support TTD (#3311) * first commit * added unit test cases for TTD id --- modules/pubmaticBidAdapter.js | 19 ++ test/spec/modules/pubmaticBidAdapter_spec.js | 233 +++++++++++++++++++ 2 files changed, 252 insertions(+) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 354addc6def..7203cee2391 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -308,9 +308,28 @@ function _handleDigitrustId(eids) { } } +function _handleTTDId(eids) { + let adsrvrOrgId = config.getConfig('adsrvrOrgId'); + if (adsrvrOrgId && utils.isStr(adsrvrOrgId.TDID)) { + eids.push({ + 'source': 'adserver.org', + 'uids': [ + { + 'id': adsrvrOrgId.TDID, + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + } + ] + }); + } +} + function _handleEids(payload) { let eids = []; _handleDigitrustId(eids); + _handleTTDId(eids); if (eids.length > 0) { payload.user.eids = eids; } diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 4fca6656e46..05aaa191207 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -640,6 +640,239 @@ describe('PubMatic adapter', function () { }); }); + describe('AdsrvrOrgId from config', function() { + let sandbox; + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('Request should have adsrvrOrgId config params', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID': '5e740345-c25e-436d-b466-5f2f9fa95c17', + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2018-10-01T07:05:40' + } + }; + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'adserver.org', + 'uids': [{ + 'id': '5e740345-c25e-436d-b466-5f2f9fa95c17', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + }] + }]); + }); + + it('Request should NOT have adsrvrOrgId config params if id in adsrvrOrgId is NOT string', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID': 1, + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2018-10-01T07:05:40' + } + }; + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal(undefined); + }); + + it('Request should NOT have adsrvrOrgId config params if adsrvrOrgId is NOT object', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: null + }; + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal(undefined); + }); + + it('Request should NOT have adsrvrOrgId config params if id in adsrvrOrgId is NOT set', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2018-10-01T07:05:40' + } + }; + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal(undefined); + }); + }); + + describe('AdsrvrOrgId and Digitrust', function() { + // here we are considering cases only of accepting DigiTrustId from config + let sandbox; + beforeEach(() => { + sandbox = sinon.sandbox.create(); + window.DigiTrust = { + getUser: sandbox.spy() + }; + }); + + afterEach(() => { + sandbox.restore(); + delete window.DigiTrust; + }); + + it('Request should have id of both AdsrvrOrgId and Digitrust if both have returned valid ids', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID': '5e740345-c25e-436d-b466-5f2f9fa95c17', + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2018-10-01T07:05:40' + }, + digiTrustId: { + success: true, + identity: { + privacy: {optout: false}, + id: 'testId', + keyv: 4 + } + } + }; + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'digitru.st', + 'uids': [{ + 'id': 'testId', + 'atype': 1, + 'ext': { + 'keyv': 4 + } + }] + }, { + 'source': 'adserver.org', + 'uids': [{ + 'id': '5e740345-c25e-436d-b466-5f2f9fa95c17', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + }] + }]); + }); + + it('Request should have id of only AdsrvrOrgId and NOT Digitrust if only AdsrvrOrgId have returned valid id', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID': '5e740345-c25e-436d-b466-5f2f9fa95c17', + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2018-10-01T07:05:40' + }, + digiTrustId: { + success: true, + identity: { + privacy: {optout: true}, + id: 'testId', + keyv: 4 + } + } + }; + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'adserver.org', + 'uids': [{ + 'id': '5e740345-c25e-436d-b466-5f2f9fa95c17', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + }] + }]); + }); + + it('Request should have id of only Digitrust and NOT AdsrvrOrgId if only Digitrust have returned valid id', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2018-10-01T07:05:40' + }, + digiTrustId: { + success: true, + identity: { + privacy: {optout: false}, + id: 'testId', + keyv: 4 + } + } + }; + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'digitru.st', + 'uids': [{ + 'id': 'testId', + 'atype': 1, + 'ext': { + 'keyv': 4 + } + }] + }]); + }); + + it('Request should NOT have id of Digitrust and NOT AdsrvrOrgId if only both have NOT returned valid ids', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2018-10-01T07:05:40' + }, + digiTrustId: { + success: true, + identity: { + privacy: {optout: true}, + id: 'testId', + keyv: 4 + } + } + }; + return config[key]; + }); + + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal(undefined); + }); + }); + it('Request params check for video ad', function () { let request = spec.buildRequests(videoBidRequests); let data = JSON.parse(request.data); From c7b060b643bc038fddfe1002e2da6a653e5262c9 Mon Sep 17 00:00:00 2001 From: JonGoSonobi Date: Tue, 27 Nov 2018 15:16:37 -0500 Subject: [PATCH 04/12] Sonobi - support video and display same adunit (#3325) * changed adapter to support video and display for ad unit * added case for sbi_ct outstream * outstream is display media type --- modules/sonobiBidAdapter.js | 12 +++---- test/spec/modules/sonobiBidAdapter_spec.js | 39 ++++++++++++++++++---- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index 15bcca50a13..0400aa46ecf 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -1,7 +1,6 @@ import { registerBidder } from 'src/adapters/bidderFactory'; -import { getTopWindowLocation, parseSizesInput, logError, generateUUID, deepAccess, isEmpty } from '../src/utils'; +import { getTopWindowLocation, parseSizesInput, logError, generateUUID, isEmpty } from '../src/utils'; import { BANNER, VIDEO } from '../src/mediaTypes'; -import find from 'core-js/library/fn/array/find'; import { config } from '../src/config'; const BIDDER_CODE = 'sonobi'; @@ -104,12 +103,10 @@ export const spec = { } Object.keys(bidResponse.slots).forEach(slot => { + const bid = bidResponse.slots[slot]; const bidId = _getBidIdFromTrinityKey(slot); - const bidRequest = find(bidderRequests, bidReqest => bidReqest.bidId === bidId); - const videoMediaType = deepAccess(bidRequest, 'mediaTypes.video'); - const mediaType = bidRequest.mediaType || (videoMediaType ? 'video' : null); + const mediaType = (bid.sbi_ct === 'video') ? 'video' : null; const createCreative = _creative(mediaType); - const bid = bidResponse.slots[slot]; if (bid.sbi_aid && bid.sbi_mouse && bid.sbi_size) { const [ width = 1, @@ -132,8 +129,7 @@ export const spec = { bids.dealId = bid.sbi_dozer; } - const creativeType = bid.sbi_ct; - if (creativeType && (creativeType === 'video' || creativeType === 'outstream')) { + if (mediaType === 'video') { bids.mediaType = 'video'; bids.vastUrl = createCreative(bidResponse.sbi_dc, bid.sbi_aid); delete bids.ad; diff --git a/test/spec/modules/sonobiBidAdapter_spec.js b/test/spec/modules/sonobiBidAdapter_spec.js index 69138c063c9..43b35c7d961 100644 --- a/test/spec/modules/sonobiBidAdapter_spec.js +++ b/test/spec/modules/sonobiBidAdapter_spec.js @@ -242,8 +242,7 @@ describe('SonobiBidAdapter', function () { }, 'adUnitCode': 'adunit-code-2', 'sizes': [[120, 600], [300, 600], [160, 600]], - 'bidId': '30b31c1838de1e', - 'mediaType': 'video' + 'bidId': '30b31c1838de1e' }, { 'bidder': 'sonobi', @@ -274,6 +273,14 @@ describe('SonobiBidAdapter', function () { 'sbi_aid': '30292e432662bd5f86d90774b944b038', 'sbi_mouse': 1.25, 'sbi_dozer': 'dozerkey', + 'sbi_ct': 'video' + }, + '/7780971/sparks_prebid_LB_OUTSTREAM|30b31c1838de1g': { + 'sbi_size': '300x600', + 'sbi_apoc': 'remnant', + 'sbi_crid': '1234abcd', + 'sbi_aid': '30292e432662bd5f86d90774b944b038', + 'sbi_mouse': 1.07, }, '/7780971/sparks_prebid_LB|30b31c1838de1g': {}, }, @@ -313,7 +320,19 @@ describe('SonobiBidAdapter', function () { 'currency': 'USD', 'dealId': 'dozerkey', 'aid': '30292e432662bd5f86d90774b944b038' - } + }, + { + 'requestId': '30b31c1838de1g', + 'cpm': 1.07, + 'width': 300, + 'height': 600, + 'ad': ``, + 'ttl': 500, + 'creativeId': '1234abcd', + 'netRevenue': true, + 'currency': 'USD', + 'aid': '30292e432662bd5f86d90774b944b038' + }, ]; it('should map bidResponse to prebidResponse', function () { @@ -321,14 +340,22 @@ describe('SonobiBidAdapter', function () { response.forEach((resp, i) => { expect(resp.requestId).to.equal(prebidResponse[i].requestId); expect(resp.cpm).to.equal(prebidResponse[i].cpm); - expect(resp.width).to.equal(prebidResponse[i].width); - expect(resp.height).to.equal(prebidResponse[i].height); + expect(resp.ttl).to.equal(prebidResponse[i].ttl); expect(resp.creativeId).to.equal(prebidResponse[i].creativeId); expect(resp.netRevenue).to.equal(prebidResponse[i].netRevenue); expect(resp.currency).to.equal(prebidResponse[i].currency); expect(resp.aid).to.equal(prebidResponse[i].aid); - expect(resp.ad.indexOf('localhost')).to.be.greaterThan(0); + if (resp.mediaType === 'video') { + expect(resp.vastUrl.indexOf('vast.xml')).to.be.greaterThan(0); + expect(resp.ad).to.be.undefined; + expect(resp.width).to.be.undefined; + expect(resp.height).to.be.undefined; + } else { + expect(resp.ad.indexOf('localhost')).to.be.greaterThan(0); + expect(resp.width).to.equal(prebidResponse[i].width); + expect(resp.height).to.equal(prebidResponse[i].height); + } }); }); }); From 0654c064fe5c62994331e6548862da095f91cde7 Mon Sep 17 00:00:00 2001 From: Kelvin Chappell Date: Tue, 27 Nov 2018 21:55:01 +0000 Subject: [PATCH 05/12] Fix user-sync iframes insertion bug (#3300) According to documentation, iframes should be inserted at bottom of head, rather than at top. --- src/utils.js | 6 ++++-- test/spec/utils_spec.js | 9 +++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/utils.js b/src/utils.js index 843c0c11c76..05fc627f795 100644 --- a/src/utils.js +++ b/src/utils.js @@ -552,16 +552,18 @@ var hasOwn = function (objectToCheck, propertyToCheckFor) { exports.insertElement = function(elm, doc, target) { doc = doc || document; let elToAppend; + const head = doc.getElementsByTagName('head'); if (target) { elToAppend = doc.getElementsByTagName(target); } else { - elToAppend = doc.getElementsByTagName('head'); + elToAppend = head; } try { elToAppend = elToAppend.length ? elToAppend : doc.getElementsByTagName('body'); if (elToAppend.length) { elToAppend = elToAppend[0]; - elToAppend.insertBefore(elm, elToAppend.firstChild); + const refChild = head && head[0] === elToAppend ? null : elToAppend.firstChild; + return elToAppend.insertBefore(elm, refChild); } } catch (e) {} }; diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js index df5d46223c0..ea1ea55cd43 100755 --- a/test/spec/utils_spec.js +++ b/test/spec/utils_spec.js @@ -914,5 +914,14 @@ describe('Utils', function () { value: [''] }]); }); + + describe('insertElement', function () { + it('returns a node at bottom of head if no target is given', function () { + const toInsert = document.createElement('div'); + const head = document.getElementsByTagName('head')[0]; + const inserted = utils.insertElement(toInsert); + expect(inserted).to.equal(head.lastChild); + }); + }); }); }); From 2660078bc6ba70a55fea48d58ab7688ef001166d Mon Sep 17 00:00:00 2001 From: Gleb Glushtsov Date: Tue, 27 Nov 2018 16:58:18 -0500 Subject: [PATCH 06/12] Prevent 33Across adapter from throwing an error when unable to getElementById(), fix JSDocs in utils.js (#3333) * check gdpr in buildRequest * User sync based on whether gdpr applies or not * check if consent data exists during user sync * split user sync into further branches: 1) when gdpr does not apply 2) when consent data is unavailable * contribute viewability to ttxRequest * update tests * remove window mock from tests * use local variables * introduce ServerRequestBuilder * add withOptions() method to ServerRequestBuilder * add semicolons * sync up package-lock.json with upstream/master * stub window.top in tests * introduce getTopWindowSize() for test purpose * reformat code * add withSite() method to TtxRequestBuilder add withSite() method to TtxRequestBuilder * add isIframe() and _isViewabilityMeasurable() * handle NON_MEASURABLE viewability in nested iframes * consider page visibility, stub utils functions getWindowTop() and getWindowSelf() * contribute viewability as 0 for inactive tab * add prebidjs version to ttx request * send caller as an array * fix JSDoc in utils.js * send viewability as non measurable when unable to locate target HTMLElement, add warning message --- modules/33acrossBidAdapter.js | 12 ++++++--- src/utils.js | 46 +++++++++++++++++------------------ 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/modules/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js index 2ecab4013f9..8bb084a7aeb 100644 --- a/modules/33acrossBidAdapter.js +++ b/modules/33acrossBidAdapter.js @@ -27,8 +27,8 @@ function _createBidResponse(response) { } } -function _isViewabilityMeasurable() { - return !_isIframe(); +function _isViewabilityMeasurable(element) { + return !_isIframe() && element !== null; } function _getViewability(element, topWin, { w, h } = {}) { @@ -46,12 +46,16 @@ function _createServerRequest(bidRequest, gdprConsent) { const sizes = _transformSizes(bidRequest.sizes); const minSize = _getMinSize(sizes); - const viewabilityAmount = _isViewabilityMeasurable() + const viewabilityAmount = _isViewabilityMeasurable(element) ? _getViewability(element, utils.getWindowTop(), minSize) : NON_MEASURABLE; const contributeViewability = ViewabilityContributor(viewabilityAmount); + if (element === null) { + utils.logWarn(`Unable to locate element with id: '${bidRequest.adUnitCode}'`); + } + /* * Infer data for the request payload */ @@ -90,7 +94,7 @@ function _createServerRequest(bidRequest, gdprConsent) { 'version': '$prebid.version$' }] } - } + }; // Finally, set the openRTB 'test' param if this is to be a test bid if (params.test === 1) { diff --git a/src/utils.js b/src/utils.js index 05fc627f795..7d9424f8489 100644 --- a/src/utils.js +++ b/src/utils.js @@ -124,7 +124,7 @@ exports.transformAdServerTargetingObj = function (targeting) { * Read an adUnit object and return the sizes used in an [[728, 90]] format (even if they had [728, 90] defined) * Preference is given to the `adUnit.mediaTypes.banner.sizes` object over the `adUnit.sizes` * @param {object} adUnit one adUnit object from the normal list of adUnits - * @returns {array[array[number]]} array of arrays containing numeric sizes + * @returns {Array.} array of arrays containing numeric sizes */ export function getAdUnitSizes(adUnit) { if (!adUnit) { @@ -151,8 +151,8 @@ export function getAdUnitSizes(adUnit) { /** * Parse a GPT-Style general size Array like `[[300, 250]]` or `"300x250,970x90"` into an array of sizes `["300x250"]` or '['300x250', '970x90']' - * @param {array[array|number]} sizeObj Input array or double array [300,250] or [[300,250], [728,90]] - * @return {array[string]} Array of strings like `["300x250"]` or `["300x250", "728x90"]` + * @param {(Array.|Array.)} sizeObj Input array or double array [300,250] or [[300,250], [728,90]] + * @return {Array.} Array of strings like `["300x250"]` or `["300x250", "728x90"]` */ export function parseSizesInput(sizeObj) { var parsedSizes = []; @@ -190,7 +190,7 @@ export function parseSizesInput(sizeObj) { } return parsedSizes; -}; +} // parse a GPT style sigle size array, (i.e [300,250]) // into an AppNexus style string, (i.e. 300x250) @@ -199,7 +199,7 @@ export function parseGPTSingleSizeArray(singleSize) { if (exports.isArray(singleSize) && singleSize.length === 2 && (!isNaN(singleSize[0]) && !isNaN(singleSize[1]))) { return singleSize[0] + 'x' + singleSize[1]; } -}; +} /** * @deprecated This function will be removed soon. Use http://prebid.org/dev-docs/bidder-adaptor.html#referrers @@ -215,7 +215,7 @@ exports.getTopWindowLocation = function() { if (loc) return parse(loc, {'decodeSearchAsString': true}); } return exports.getWindowLocation(); -} +}; /** * @deprecated This function will be removed soon. Use http://prebid.org/dev-docs/bidder-adaptor.html#referrers @@ -375,9 +375,9 @@ exports.getParameterByName = getParameterByName; /** * This function validates paramaters. - * @param {object[string]} paramObj [description] + * @param {Object} paramObj [description] * @param {string[]} requiredParamsArr [description] - * @return {bool} Bool if paramaters are valid + * @return {boolean} Bool if paramaters are valid */ exports.hasValidBidRequest = function (paramObj, requiredParamsArr, adapter) { var found = false; @@ -439,11 +439,11 @@ exports.isNumber = function(object) { exports.isPlainObject = function(object) { return exports.isA(object, tObject); -} +}; exports.isBoolean = function(object) { return exports.isA(object, tBoolean); -} +}; /** * Return if the object is "empty"; @@ -671,8 +671,8 @@ exports.createTrackPixelIframeHtml = function (url, encodeUri = true, sandbox = /** * Returns iframe document in a browser agnostic way - * @param {object} iframe reference - * @return {object} iframe `document` reference + * @param {Object} iframe reference + * @return {Object} iframe `document` reference */ exports.getIframeDocument = function (iframe) { if (!iframe) { @@ -876,7 +876,7 @@ export function delayExecution(func, numRequiredCalls) { * @export * @param {array} xs * @param {string} key - * @returns {${key_value}: ${groupByArray}, key_value: {groupByArray}} + * @returns {Object} {${key_value}: ${groupByArray}, key_value: {groupByArray}} */ export function groupBy(xs, key) { return xs.reduce(function(rv, x) { @@ -887,7 +887,7 @@ export function groupBy(xs, key) { /** * deepAccess utility function useful for doing safe access (will not throw exceptions) of deep object paths. - * @param {object} obj The object containing the values you would like to access. + * @param {Object} obj The object containing the values you would like to access. * @param {string|number} path Object path to the value you would like to access. Non-strings are coerced to strings. * @returns {*} The value found at the specified object path, or undefined if path is not found. */ @@ -907,7 +907,7 @@ export function deepAccess(obj, path) { /** * Returns content for a friendly iframe to execute a URL in script tag - * @param {url} URL to be executed in a script tag in a friendly iframe + * @param {string} url URL to be executed in a script tag in a friendly iframe * and are macros left to be replaced if required */ export function createContentToExecuteExtScriptInFriendlyFrame(url) { @@ -921,9 +921,9 @@ export function createContentToExecuteExtScriptInFriendlyFrame(url) { /** * Build an object consisting of only defined parameters to avoid creating an * object with defined keys and undefined values. - * @param {object} object The object to pick defined params out of + * @param {Object} object The object to pick defined params out of * @param {string[]} params An array of strings representing properties to look for in the object - * @returns {object} An object containing all the specified values that are defined + * @returns {Object} An object containing all the specified values that are defined */ export function getDefinedParams(object, params) { return params @@ -968,8 +968,8 @@ export function getBidderRequest(bidRequests, bidder, adUnitCode) { } /** * Returns user configured bidder params from adunit - * @param {object} adunits - * @param {string} adunit code + * @param {Object} adUnits + * @param {string} adUnitCode code * @param {string} bidder code * @return {Array} user configured param for the given bidder adunit configuration */ @@ -1004,7 +1004,7 @@ const compareCodeAndSlot = (slot, adUnitCode) => slot.getAdUnitPath() === adUnit /** * Returns filter function to match adUnitCode in slot - * @param {object} slot GoogleTag slot + * @param {Object} slot GoogleTag slot * @return {function} filter function */ export function isAdUnitCodeMatchingSlot(slot) { @@ -1043,9 +1043,9 @@ export function unsupportedBidderMessage(adUnit, bidder) { * @return {Object} object */ export function deletePropertyFromObject(object, prop) { - let result = Object.assign({}, object) + let result = Object.assign({}, object); delete result[prop]; - return result + return result; } /** @@ -1083,7 +1083,7 @@ export function convertCamelToUnderscore(value) { * normally read from bidder params * eg { foo: ['bar', 'baz'], fizz: ['buzz'] } * becomes [{ key: 'foo', value: ['bar', 'baz']}, {key: 'fizz', value: ['buzz']}] - * @param {Object{Arrays}} keywords object of arrays representing keyvalue pairs + * @param {Object} keywords object of arrays representing keyvalue pairs * @param {string} paramName name of parent object (eg 'keywords') containing keyword data, used in error handling */ export function transformBidderParamKeywords(keywords, paramName = 'keywords') { From a4863aee9c06179bc543e7b4232221265573ed46 Mon Sep 17 00:00:00 2001 From: Brittany Zellman <33695402+brittanyzellman@users.noreply.github.com> Date: Tue, 27 Nov 2018 17:10:28 -0500 Subject: [PATCH 07/12] TripleliftBidAdapter-update creative_id (#3324) * removed dependancy on getTopWindowUrl for referer * protect against undefined obj and remove test on old dependency * added unit test for referer and gdpr in query string * removed gdpr test * removed gdpr from bidderRequest obj * decontructed bidder request obj in chai test * just need to run karma tests again * added gdpr consent to all bidderRequest obj in chai tests * changed creativeId to be a Triplelift specific id rather than represent SRA impression * error-proofed creative id From de1248bea2bb17af9aab3faacda12eda5730700a Mon Sep 17 00:00:00 2001 From: Keiran Date: Tue, 27 Nov 2018 15:55:14 -0800 Subject: [PATCH 08/12] Update taphypeBidAdapter.js removed `supportedMediaTypes` and logging --- modules/taphypeBidAdapter.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/taphypeBidAdapter.js b/modules/taphypeBidAdapter.js index 3e8b354fc31..53e5037bb0b 100644 --- a/modules/taphypeBidAdapter.js +++ b/modules/taphypeBidAdapter.js @@ -3,7 +3,6 @@ import {registerBidder} from 'src/adapters/bidderFactory'; export const spec = { code: 'taphype', - supportedMediaTypes: [BANNER], isBidRequestValid: function (bid) { return !!bid.params.placementId; }, @@ -23,7 +22,6 @@ export const spec = { return requests; }, interpretResponse: function (serverResponse, bidRequest) { - console.log(serverResponse); if (!serverResponse || !serverResponse.body || !serverResponse.body.ad) { return []; } From 1e62ef91596b4c0660beceb4d770df0dc671f71e Mon Sep 17 00:00:00 2001 From: Keiran Date: Tue, 27 Nov 2018 15:59:44 -0800 Subject: [PATCH 09/12] Update taphypeBidAdapter.js remove mediaTypes import --- modules/taphypeBidAdapter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/taphypeBidAdapter.js b/modules/taphypeBidAdapter.js index 53e5037bb0b..ae9eb7296e0 100644 --- a/modules/taphypeBidAdapter.js +++ b/modules/taphypeBidAdapter.js @@ -1,4 +1,3 @@ -import {BANNER} from 'src/mediaTypes'; import {registerBidder} from 'src/adapters/bidderFactory'; export const spec = { From 0ad7a28098071a3ce3c96832888cc80ed9620dac Mon Sep 17 00:00:00 2001 From: Keiran Flanigan Date: Wed, 21 Nov 2018 19:55:03 -0800 Subject: [PATCH 10/12] new TapHype bidder adapter --- modules/taphypeBidAdapter.js | 48 ++++++++++++++++++ modules/taphypeBidAdapter.md | 32 ++++++++++++ test/spec/modules/taphypeBidAdapter_spec.js | 56 +++++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 modules/taphypeBidAdapter.js create mode 100644 modules/taphypeBidAdapter.md create mode 100644 test/spec/modules/taphypeBidAdapter_spec.js diff --git a/modules/taphypeBidAdapter.js b/modules/taphypeBidAdapter.js new file mode 100644 index 00000000000..3e8b354fc31 --- /dev/null +++ b/modules/taphypeBidAdapter.js @@ -0,0 +1,48 @@ +import {BANNER} from 'src/mediaTypes'; +import {registerBidder} from 'src/adapters/bidderFactory'; + +export const spec = { + code: 'taphype', + supportedMediaTypes: [BANNER], + isBidRequestValid: function (bid) { + return !!bid.params.placementId; + }, + buildRequests: function (bidRequests) { + const requests = bidRequests.map(function (bid) { + const params = { + placementId: bid.params.placementId, + url: encodeURIComponent(window.location.href), + size: bid.sizes[0][0] + 'x' + bid.sizes[0][1], + rnd: Math.random(), + bidId: bid.bidId, + }; + + return {method: 'GET', url: 'https://us-central1-taphype-internal.cloudfunctions.net/th-prebid', data: params, options: {withCredentials: false}} + }); + + return requests; + }, + interpretResponse: function (serverResponse, bidRequest) { + console.log(serverResponse); + if (!serverResponse || !serverResponse.body || !serverResponse.body.ad) { + return []; + } + + const bid = serverResponse.body; + const sizes = bid.size.split(','); + + return [{ + requestId: bidRequest.data.bidId, + cpm: bid.price, + width: sizes[0], + height: sizes[1], + creativeId: bidRequest.data.bidId, + currency: bid.currency || 'USD', + netRevenue: true, + ad: bid.ad, + ttl: 360 + }]; + }, +}; + +registerBidder(spec); diff --git a/modules/taphypeBidAdapter.md b/modules/taphypeBidAdapter.md new file mode 100644 index 00000000000..c6ff40a42ba --- /dev/null +++ b/modules/taphypeBidAdapter.md @@ -0,0 +1,32 @@ +# Overview + +Module Name: TapHype Bidder Adapter +Module Type: Bidder Adapter +Maintainer: admin@taphype.com + +# Description + +You can use this adapter to get a bid from taphype.com. + + +# Test Parameters +```javascript + var adUnits = [ + { + code: 'div-taphype-example', + sizes: [[300, 250]], + bids: [ + { + bidder: "taphype", + params: { + placementId: 12345 + } + } + ] + } + ]; +``` + +Where: + +* placementId - TapHype Placement ID diff --git a/test/spec/modules/taphypeBidAdapter_spec.js b/test/spec/modules/taphypeBidAdapter_spec.js new file mode 100644 index 00000000000..2fcdd964520 --- /dev/null +++ b/test/spec/modules/taphypeBidAdapter_spec.js @@ -0,0 +1,56 @@ +import { expect } from 'chai'; +import { spec } from 'modules/taphypeBidAdapter'; + +describe('taphypeBidAdapterTests', function () { + it('validate_pub_params', function () { + expect(spec.isBidRequestValid({ + bidder: 'taphype', + params: { + placementId: 12345 + } + })).to.equal(true); + }); + + it('validate_generated_params', function () { + let bidRequestData = [{ + bidId: 'bid12345', + bidder: 'taphype', + params: { + placementId: 12345 + }, + sizes: [[300, 250]] + }]; + + let request = spec.buildRequests(bidRequestData); + let req_data = request[0].data; + + expect(req_data.bidId).to.equal('bid12345'); + }); + + it('validate_response_params', function () { + let bidRequestData = { + data: { + bidId: 'bid12345' + } + }; + + let serverResponse = { + body: { + price: 1.23, + ad: '', + size: '300,250' + } + }; + + let bids = spec.interpretResponse(serverResponse, bidRequestData); + expect(bids).to.have.lengthOf(1); + let bid = bids[0]; + expect(bid.cpm).to.equal(1.23); + expect(bid.currency).to.equal('USD'); + expect(bid.width).to.equal('300'); + expect(bid.height).to.equal('250'); + expect(bid.netRevenue).to.equal(true); + expect(bid.requestId).to.equal('bid12345'); + expect(bid.ad).to.equal(''); + }); +}); From 233663769d5a80590e07dee0769cb58df331c948 Mon Sep 17 00:00:00 2001 From: Keiran Date: Tue, 27 Nov 2018 15:55:14 -0800 Subject: [PATCH 11/12] Update taphypeBidAdapter.js removed `supportedMediaTypes` and logging --- modules/taphypeBidAdapter.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/taphypeBidAdapter.js b/modules/taphypeBidAdapter.js index 3e8b354fc31..53e5037bb0b 100644 --- a/modules/taphypeBidAdapter.js +++ b/modules/taphypeBidAdapter.js @@ -3,7 +3,6 @@ import {registerBidder} from 'src/adapters/bidderFactory'; export const spec = { code: 'taphype', - supportedMediaTypes: [BANNER], isBidRequestValid: function (bid) { return !!bid.params.placementId; }, @@ -23,7 +22,6 @@ export const spec = { return requests; }, interpretResponse: function (serverResponse, bidRequest) { - console.log(serverResponse); if (!serverResponse || !serverResponse.body || !serverResponse.body.ad) { return []; } From d1b2cc124ebd7be29057fd3c0a25e9cbee4c0b52 Mon Sep 17 00:00:00 2001 From: Keiran Date: Tue, 27 Nov 2018 15:59:44 -0800 Subject: [PATCH 12/12] Update taphypeBidAdapter.js remove mediaTypes import --- modules/taphypeBidAdapter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/taphypeBidAdapter.js b/modules/taphypeBidAdapter.js index 53e5037bb0b..ae9eb7296e0 100644 --- a/modules/taphypeBidAdapter.js +++ b/modules/taphypeBidAdapter.js @@ -1,4 +1,3 @@ -import {BANNER} from 'src/mediaTypes'; import {registerBidder} from 'src/adapters/bidderFactory'; export const spec = {