From f3244db12729ef7ec053e3e98685a5805f24dd13 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Tue, 16 Apr 2019 11:38:48 -0700 Subject: [PATCH 001/146] Update rubiconBidAdapter.js (#3753) Prebid-Server expects price granularity param to be all lowercase, Camel case causes it to not consider it. --- modules/rubiconBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 034b861f0f7..c57e10aede7 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -151,7 +151,7 @@ export const spec = { includewinners: true, // includebidderkeys always false for openrtb includebidderkeys: false, - priceGranularity: getPriceGranularity(config) + pricegranularity: getPriceGranularity(config) } } } From 2f0e98d580cb455ad46972346b34d58f269a0484 Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Tue, 16 Apr 2019 15:07:49 -0700 Subject: [PATCH 002/146] Prebid 2.11.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 79aa74cfa28..b3c42a4a601 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.11.0-pre", + "version": "2.11.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 8de1fac1580e9327ff6b93dffabf12b71a57606f Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Wed, 17 Apr 2019 08:10:27 -0700 Subject: [PATCH 003/146] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b3c42a4a601..c3b4e5cd042 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.11.0", + "version": "2.12.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 984dfb1f47aedcc7bad651d337c50613c2432e07 Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Thu, 18 Apr 2019 10:16:51 -0700 Subject: [PATCH 004/146] PubCommonId - Add support for localStorage (#3661) * Add support for localStorage * Pubcid uses local storage first --- modules/pubCommonId.js | 192 +++++++++++++++++++++++--- modules/pubCommonId.md | 37 +++++ test/spec/modules/pubCommonId_spec.js | 175 ++++++++++++++++++++--- 3 files changed, 366 insertions(+), 38 deletions(-) create mode 100644 modules/pubCommonId.md diff --git a/modules/pubCommonId.js b/modules/pubCommonId.js index 3175dc22613..5b92592f07a 100644 --- a/modules/pubCommonId.js +++ b/modules/pubCommonId.js @@ -6,15 +6,126 @@ import * as utils from '../src/utils' import { config } from '../src/config'; -const COOKIE_NAME = '_pubcid'; -const DEFAULT_EXPIRES = 2628000; // 5-year worth of minutes +const ID_NAME = '_pubcid'; +const OPTOUT_NAME = '_pubcid_optout'; +const DEFAULT_EXPIRES = 525600; // 1-year worth of minutes const PUB_COMMON = 'PublisherCommonId'; +const EXP_SUFFIX = '_exp'; +const COOKIE = 'cookie'; +const LOCAL_STORAGE = 'html5'; -var pubcidEnabled = true; -var interval = DEFAULT_EXPIRES; +let pubcidConfig = { + enabled: true, + interval: DEFAULT_EXPIRES, + typeEnabled: LOCAL_STORAGE, + readOnly: false +}; -export function isPubcidEnabled() { return pubcidEnabled; } -export function getExpInterval() { return interval; } +/** + * Set an item in the storage with expiry time. + * @param {string} key Key of the item to be stored + * @param {string} val Value of the item to be stored + * @param {number} expires Expiry time in minutes + */ + +export function setStorageItem(key, val, expires) { + try { + if (expires !== undefined && expires != null) { + const expStr = (new Date(Date.now() + (expires * 60 * 1000))).toUTCString(); + localStorage.setItem(key + EXP_SUFFIX, expStr); + } + + localStorage.setItem(key, val); + } catch (e) { + utils.logMessage(e); + } +} + +/** + * Retrieve an item from storage if it exists and hasn't expired. + * @param {string} key Key of the item. + * @returns {string|null} Value of the item. + */ +export function getStorageItem(key) { + let val = null; + + try { + const expVal = localStorage.getItem(key + EXP_SUFFIX); + + if (!expVal) { + // If there is no expiry time, then just return the item + val = localStorage.getItem(key); + } else { + // Only return the item if it hasn't expired yet. + // Otherwise delete the item. + const expDate = new Date(expVal); + const isValid = (expDate.getTime() - Date.now()) > 0; + if (isValid) { + val = localStorage.getItem(key); + } else { + removeStorageItem(key); + } + } + } catch (e) { + utils.logMessage(e); + } + + return val; +} + +/** + * Remove an item from storage + * @param {string} key Key of the item to be removed + */ +export function removeStorageItem(key) { + try { + localStorage.removeItem(key + EXP_SUFFIX); + localStorage.removeItem(key); + } catch (e) { + utils.logMessage(e); + } +} + +/** + * Read a value either from cookie or local storage + * @param {string} name Name of the item + * @returns {string|null} a string if item exists + */ +function readValue(name) { + let value; + if (pubcidConfig.typeEnabled === COOKIE) { + value = getCookie(name); + } else if (pubcidConfig.typeEnabled === LOCAL_STORAGE) { + value = getStorageItem(name); + if (!value) { + value = getCookie(name); + } + } + + if (value === 'undefined' || value === 'null') { return null; } + + return value; +} + +/** + * Write a value to either cookies or local storage + * @param {string} name Name of the item + * @param {string} value Value to be stored + * @param {number} expInterval Expiry time in minutes + */ +function writeValue(name, value, expInterval) { + if (name && value) { + if (pubcidConfig.typeEnabled === COOKIE) { + setCookie(name, value, expInterval); + } else if (pubcidConfig.typeEnabled === LOCAL_STORAGE) { + setStorageItem(name, value, expInterval); + } + } +} + +export function isPubcidEnabled() { return pubcidConfig.enabled; } +export function getExpInterval() { return pubcidConfig.interval; } +export function getPubcidConfig() { return pubcidConfig; } /** * Decorate ad units with pubcid. This hook function is called before the @@ -29,7 +140,7 @@ export function requestBidHook(next, config) { let pubcid = null; // Pass control to the next function if not enabled - if (!pubcidEnabled) { + if (!pubcidConfig.enabled || !pubcidConfig.typeEnabled) { return next.call(this, config); } @@ -38,11 +149,22 @@ export function requestBidHook(next, config) { pubcid = window[PUB_COMMON].getId(); utils.logMessage(PUB_COMMON + ': pubcid = ' + pubcid); } else { - // Otherwise get the existing cookie or create a new id - pubcid = getCookie(COOKIE_NAME) || utils.generateUUID(); + // Otherwise get the existing cookie + pubcid = readValue(ID_NAME); + + if (!pubcidConfig.readOnly) { + if (!pubcid) { + pubcid = utils.generateUUID(); + // Update the cookie/storage with the latest expiration date + writeValue(ID_NAME, pubcid, pubcidConfig.interval); + // Only return pubcid if it is saved successfully + pubcid = readValue(ID_NAME); + } else { + // Update the cookie/storage with the latest expiration date + writeValue(ID_NAME, pubcid, pubcidConfig.interval); + } + } - // Update the cookie with the latest expiration date - setCookie(COOKIE_NAME, pubcid, interval); utils.logMessage('pbjs: pubcid = ' + pubcid); } @@ -68,21 +190,49 @@ export function setCookie(name, value, expires) { // Helper to read a cookie export function getCookie(name) { - let m = window.document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]*)\\s*(;|$)'); - return m ? decodeURIComponent(m[2]) : null; + if (name && window.document.cookie) { + let m = window.document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]*)\\s*(;|$)'); + return m ? decodeURIComponent(m[2]) : null; + } + return null; } /** * Configuration function * @param {boolean} enable Enable or disable pubcid. By default the module is enabled. * @param {number} expInterval Expiration interval of the cookie in minutes. + * @param {string} type Type of storage to use + * @param {boolean} readOnly Read but not update id */ -export function setConfig({ enable = true, expInterval = DEFAULT_EXPIRES } = {}) { - pubcidEnabled = enable; - interval = parseInt(expInterval, 10); - if (isNaN(interval)) { - interval = DEFAULT_EXPIRES; +export function setConfig({ enable = true, expInterval = DEFAULT_EXPIRES, type = 'html5,cookie', readOnly = false } = {}) { + pubcidConfig.enabled = enable; + pubcidConfig.interval = parseInt(expInterval, 10); + if (isNaN(pubcidConfig.interval)) { + pubcidConfig.interval = DEFAULT_EXPIRES; + } + + pubcidConfig.readOnly = readOnly; + + // Default is to use local storage. Fall back to + // cookie only if local storage is not supported. + + pubcidConfig.typeEnabled = null; + + const typeArray = type.split(','); + for (let i = 0; i < typeArray.length; ++i) { + const name = typeArray[i].trim(); + if (name === COOKIE) { + if (utils.cookiesAreEnabled()) { + pubcidConfig.typeEnabled = COOKIE; + break; + } + } else if (name === LOCAL_STORAGE) { + if (utils.hasLocalStorage()) { + pubcidConfig.typeEnabled = LOCAL_STORAGE; + break; + } + } } } @@ -92,10 +242,8 @@ export function setConfig({ enable = true, expInterval = DEFAULT_EXPIRES } = {}) export function initPubcid() { config.getConfig('pubcid', config => setConfig(config.pubcid)); - if (utils.cookiesAreEnabled()) { - if (!getCookie('_pubcid_optout')) { - $$PREBID_GLOBAL$$.requestBids.before(requestBidHook); - } + if (!readValue(OPTOUT_NAME)) { + $$PREBID_GLOBAL$$.requestBids.before(requestBidHook); } } diff --git a/modules/pubCommonId.md b/modules/pubCommonId.md new file mode 100644 index 00000000000..79531bfe87c --- /dev/null +++ b/modules/pubCommonId.md @@ -0,0 +1,37 @@ +## Publisher Common ID Example Configuration + +When the module is included, it's automatically enabled and saves an id to both cookie and local storage with an expiration time of 1 year. + +Example of disabling publisher common id. + +``` +pbjs.setConfig( + pubcid: { + enable: false + } +); +``` + +Example of setting expiration interval to 30 days. The interval is expressed in minutes. + +``` +pbjs.setConfig( + pubcid: { + expInterval: 43200 + } +); +``` + +Example of using local storage only and setting expiration interval to 30 days. + +``` +pbjs.setConfig( + pubcid: { + expInterval: 43200, + type: 'html5' + } +); +``` + + + diff --git a/test/spec/modules/pubCommonId_spec.js b/test/spec/modules/pubCommonId_spec.js index f648e165c93..fb4a58377c3 100644 --- a/test/spec/modules/pubCommonId_spec.js +++ b/test/spec/modules/pubCommonId_spec.js @@ -5,7 +5,11 @@ import { setConfig, isPubcidEnabled, getExpInterval, - initPubcid } from 'modules/pubCommonId'; + initPubcid, + setStorageItem, + getStorageItem, + removeStorageItem, + getPubcidConfig } from 'modules/pubCommonId'; import { getAdUnits } from 'test/fixtures/fixtures'; import * as auctionModule from 'src/auction'; import { registerBidder } from 'src/adapters/bidderFactory'; @@ -14,16 +18,28 @@ import * as utils from 'src/utils'; var assert = require('chai').assert; var expect = require('chai').expect; -const COOKIE_NAME = '_pubcid'; +const ID_NAME = '_pubcid'; +const EXP = '_exp'; const TIMEOUT = 2000; +const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89a-f][0-9a-f]{3}-[0-9a-f]{12}$/; + +function cleanUp() { + window.document.cookie = ID_NAME + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;'; + localStorage.removeItem(ID_NAME); + localStorage.removeItem(ID_NAME + EXP); +} + describe('Publisher Common ID', function () { afterEach(function () { $$PREBID_GLOBAL$$.requestBids.removeAll(); }); describe('Decorate adUnits', function () { - before(function() { - window.document.cookie = COOKIE_NAME + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;'; + beforeEach(function() { + cleanUp(); + }); + afterEach(function() { + cleanUp(); }); it('Check same cookie', function () { @@ -31,12 +47,13 @@ describe('Publisher Common ID', function () { let adUnits2 = getAdUnits(); let innerAdUnits1; let innerAdUnits2; - let pubcid = getCookie(COOKIE_NAME); + let pubcid; - expect(pubcid).to.be.null; // there should be no cookie initially + expect(getCookie(ID_NAME)).to.be.null; // there should be no cookie initially + expect(localStorage.getItem(ID_NAME)).to.be.null; // there should be no local storage item either requestBidHook((config) => { innerAdUnits1 = config.adUnits }, {adUnits: adUnits1}); - pubcid = getCookie(COOKIE_NAME); // cookies is created after requestbidHook + pubcid = localStorage.getItem(ID_NAME); // local storage item is created after requestbidHook innerAdUnits1.forEach((unit) => { unit.bids.forEach((bid) => { @@ -44,6 +61,11 @@ describe('Publisher Common ID', function () { expect(bid.crumbs.pubcid).to.equal(pubcid); }); }); + + // verify cookie is null + expect(getCookie(ID_NAME)).to.be.null; + + // verify same pubcid is preserved requestBidHook((config) => { innerAdUnits2 = config.adUnits }, {adUnits: adUnits2}); assert.deepEqual(innerAdUnits1, innerAdUnits2); }); @@ -57,8 +79,10 @@ describe('Publisher Common ID', function () { let pubcid2; requestBidHook((config) => { innerAdUnits1 = config.adUnits }, {adUnits: adUnits1}); - pubcid1 = getCookie(COOKIE_NAME); // get first cookie - setCookie(COOKIE_NAME, '', -1); // erase cookie + pubcid1 = localStorage.getItem(ID_NAME); // get first pubcid + removeStorageItem(ID_NAME); // remove storage + + expect(pubcid1).to.not.be.null; innerAdUnits1.forEach((unit) => { unit.bids.forEach((bid) => { @@ -68,7 +92,7 @@ describe('Publisher Common ID', function () { }); requestBidHook((config) => { innerAdUnits2 = config.adUnits }, {adUnits: adUnits2}); - pubcid2 = getCookie(COOKIE_NAME); // get second cookie + pubcid2 = localStorage.getItem(ID_NAME); // get second pubcid innerAdUnits2.forEach((unit) => { unit.bids.forEach((bid) => { @@ -77,6 +101,7 @@ describe('Publisher Common ID', function () { }); }); + expect(pubcid2).to.not.be.null; expect(pubcid1).to.not.equal(pubcid2); }); @@ -85,7 +110,7 @@ describe('Publisher Common ID', function () { let innerAdUnits; let pubcid = utils.generateUUID(); - setCookie(COOKIE_NAME, pubcid, 600); + setCookie(ID_NAME, pubcid, 600); requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); innerAdUnits.forEach((unit) => { unit.bids.forEach((bid) => { @@ -94,16 +119,79 @@ describe('Publisher Common ID', function () { }); }); }); + + it('Replicate cookie to storage', function() { + let adUnits = getAdUnits(); + let innerAdUnits; + let pubcid = utils.generateUUID(); + + setCookie(ID_NAME, pubcid, 600); + requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + + expect(getStorageItem(ID_NAME)).to.equal(pubcid); + }); + + it('Does not replicate storage to cookie', function() { + let adUnits = getAdUnits(); + let innerAdUnits; + let pubcid = utils.generateUUID(); + + setStorageItem(ID_NAME, pubcid, 600); + requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + + expect(getCookie(ID_NAME)).to.be.null; + }); + + it('Cookie only', function() { + setConfig({type: 'cookie'}); + let adUnits = getAdUnits(); + let innerAdUnits; + + requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + + expect(getCookie(ID_NAME)).to.match(uuidPattern); + expect(getStorageItem(ID_NAME)).to.be.null; + }); + + it('Storage only', function() { + setConfig({type: 'html5'}); + let adUnits = getAdUnits(); + let innerAdUnits; + + requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + + expect(getCookie(ID_NAME)).to.be.null; + expect(getStorageItem(ID_NAME)).to.match(uuidPattern); + }); + + it('Bad id recovery', function() { + let adUnits = getAdUnits(); + let innerAdUnits; + + setStorageItem(ID_NAME, 'undefined', 600); + requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + + expect(getStorageItem(ID_NAME)).to.match(uuidPattern); + }); }); describe('Configuration', function () { + beforeEach(() => { + setConfig(); + cleanUp(); + }); + afterEach(() => { + setConfig(); + cleanUp(); + }); + it('empty config', function () { // this should work as usual setConfig({}); let adUnits = getAdUnits(); let innerAdUnits; requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - let pubcid = getCookie(COOKIE_NAME); + let pubcid = localStorage.getItem(ID_NAME); innerAdUnits.forEach((unit) => { unit.bids.forEach((bid) => { expect(bid).to.have.deep.nested.property('crumbs.pubcid'); @@ -114,13 +202,12 @@ describe('Publisher Common ID', function () { it('disable', function () { setConfig({enable: false}); - setCookie(COOKIE_NAME, '', -1); // erase cookie let adUnits = getAdUnits(); let unmodified = getAdUnits(); let innerAdUnits; expect(isPubcidEnabled()).to.be.false; requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - expect(getCookie(COOKIE_NAME)).to.be.null; + expect(getCookie(ID_NAME)).to.be.null; assert.deepEqual(innerAdUnits, unmodified); setConfig({enable: true}); // reset requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); @@ -133,7 +220,6 @@ describe('Publisher Common ID', function () { it('change expiration time', function () { setConfig({expInterval: 100}); - setCookie(COOKIE_NAME, '', -1); // erase cookie expect(getExpInterval()).to.equal(100); let adUnits = getAdUnits(); let innerAdUnits; @@ -142,7 +228,24 @@ describe('Publisher Common ID', function () { unit.bids.forEach((bid) => { expect(bid).to.have.deep.nested.property('crumbs.pubcid'); }); - }) + }); + }); + + it('read only', function() { + setConfig({ + readOnly: true + }); + + const config = getPubcidConfig(); + expect(config.readOnly).to.be.true; + expect(config.typeEnabled).to.equal('html5'); + + let adUnits = getAdUnits(); + let innerAdUnits; + requestBidHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); + + const pubcid = localStorage.getItem(ID_NAME); + expect(pubcid).to.be.null; }); }); @@ -192,4 +295,44 @@ describe('Publisher Common ID', function () { }); }); }); + + describe('Storage item functions', () => { + beforeEach(() => { cleanUp(); }); + afterEach(() => { cleanUp(); }); + + it('Test set', () => { + const key = ID_NAME; + const val = 'test-set-value'; + // Set item in localStorage + const now = Date.now(); + setStorageItem(key, val, 100); + // Check both item and expiry time are stored + const expVal = localStorage.getItem(key + EXP); + const storedVal = localStorage.getItem(key); + // Verify expiry + expect(expVal).to.not.be.null; + const expDate = new Date(expVal); + expect((expDate.getTime() - now) / 1000).to.be.closeTo(100 * 60, 5); + // Verify value + expect(storedVal).to.equal(val); + }); + + it('Test get and remove', () => { + const key = ID_NAME; + const val = 'test-get-remove'; + setStorageItem(key, val, 10); + expect(getStorageItem(key)).to.equal(val); + removeStorageItem(key); + expect(getStorageItem(key)).to.be.null; + }); + + it('Test expiry', () => { + const key = ID_NAME; + const val = 'test-expiry'; + setStorageItem(key, val, -1); + expect(localStorage.getItem(key)).to.equal(val); + expect(getStorageItem(key)).to.be.null; + expect(localStorage.getItem(key)).to.be.null; + }); + }); }); From e119938c80a34fec237d1a0c8a97429f88fab4b5 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Thu, 18 Apr 2019 10:31:28 -0700 Subject: [PATCH 005/146] Rubicon BidAdapter - SRA support for >10 bids (#3514) * added support to use multiple requests for SRA requests with more than 10 bids * updated unit test to test SRA dividing 100 bids into 10 requests --- modules/rubiconBidAdapter.js | 58 ++++++++++++--------- test/spec/modules/rubiconBidAdapter_spec.js | 41 ++++++++++----- 2 files changed, 63 insertions(+), 36 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index c57e10aede7..55937a3feda 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -232,30 +232,30 @@ export const spec = { return groupedBids; }, {}); - requests = videoRequests.concat(Object.keys(groupedBidRequests).map(bidGroupKey => { - let bidsInGroup = groupedBidRequests[bidGroupKey]; - - // fastlane SRA has a limit of 10 slots - if (bidsInGroup.length > 10) { - utils.logWarn(`Rubicon bid adapter Warning: single request mode has a limit of 10 bids: ${bidsInGroup.length - 10} bids were not sent`); - bidsInGroup = bidsInGroup.slice(0, 10); - } - - const combinedSlotParams = spec.combineSlotUrlParams(bidsInGroup.map(bidRequest => { - return spec.createSlotParams(bidRequest, bidderRequest); - })); - - // SRA request returns grouped bidRequest arrays not a plain bidRequest - return { - method: 'GET', - url: FASTLANE_ENDPOINT, - data: spec.getOrderedParams(combinedSlotParams).reduce((paramString, key) => { - const propValue = combinedSlotParams[key]; - return ((utils.isStr(propValue) && propValue !== '') || utils.isNumber(propValue)) ? `${paramString}${key}=${encodeURIComponent(propValue)}&` : paramString; - }, '') + `slots=${bidsInGroup.length}&rand=${Math.random()}`, - bidRequest: bidsInGroup, - }; - })); + // fastlane SRA has a limit of 10 slots + const SRA_BID_LIMIT = 10; + + // multiple requests are used if bids groups have more than 10 bids + requests = videoRequests.concat(Object.keys(groupedBidRequests).reduce((aggregate, bidGroupKey) => { + // for each partioned bidGroup, append a bidRequest to requests list + partitionArray(groupedBidRequests[bidGroupKey], SRA_BID_LIMIT).forEach(bidsInGroup => { + const combinedSlotParams = spec.combineSlotUrlParams(bidsInGroup.map(bidRequest => { + return spec.createSlotParams(bidRequest, bidderRequest); + })); + + // SRA request returns grouped bidRequest arrays not a plain bidRequest + aggregate.push({ + method: 'GET', + url: FASTLANE_ENDPOINT, + data: spec.getOrderedParams(combinedSlotParams).reduce((paramString, key) => { + const propValue = combinedSlotParams[key]; + return ((utils.isStr(propValue) && propValue !== '') || utils.isNumber(propValue)) ? `${paramString}${key}=${encodeURIComponent(propValue)}&` : paramString; + }, '') + `slots=${bidsInGroup.length}&rand=${Math.random()}`, + bidRequest: bidsInGroup + }); + }); + return aggregate; + }, [])); } return requests; }, @@ -897,6 +897,16 @@ export function hasValidVideoParams(bid) { return isValid; } +/** + * split array into multiple arrays of defined size + * @param {Array} array + * @param {number} size + * @returns {Array} + */ +function partitionArray(array, size) { + return array.map((e, i) => (i % size === 0) ? array.slice(i, i + size) : null).filter((e) => e) +} + var hasSynced = false; export function resetUserSync() { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index f3b244b1bc1..33e5d04466a 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -957,7 +957,7 @@ describe('the rubicon adapter', function () { }); }); - it('should not send more than 10 bids in a request', function () { + it('should not send more than 10 bids in a request (split into separate requests with <= 10 bids each)', function () { sandbox.stub(config, 'getConfig').callsFake((key) => { const config = { 'rubicon.singleRequest': true @@ -965,27 +965,44 @@ describe('the rubicon adapter', function () { return config[key]; }); - for (let i = 0; i < 20; i++) { + let serverRequests; + let data; + + // TEST '10' BIDS, add 9 to 1 existing bid + for (let i = 0; i < 9; i++) { let bidCopy = clone(bidderRequest.bids[0]); bidCopy.params.zoneId = `${i}0000`; bidderRequest.bids.push(bidCopy); } - - const serverRequests = spec.buildRequests(bidderRequest.bids, bidderRequest); - - // if bids are greater than 10, additional bids are dropped + serverRequests = spec.buildRequests(bidderRequest.bids, bidderRequest); + // '10' bids per SRA request: so there should be 1 request + expect(serverRequests.length).to.equal(1); + // and that one request should have data from 10 bids expect(serverRequests[0].bidRequest).to.have.lengthOf(10); - // check that slots param value matches - const foundSlotsCount = serverRequests[0].data.indexOf('&slots=10&'); - expect(foundSlotsCount !== -1).to.equal(true); - + expect(serverRequests[0].data.indexOf('&slots=10&') !== -1).to.equal(true); // check that zone_id has 10 values (since all zone_ids are unique all should exist in get param) - const data = parseQuery(serverRequests[0].data); - + data = parseQuery(serverRequests[0].data); expect(data).to.be.a('object'); expect(data).to.have.property('zone_id'); expect(data.zone_id.split(';')).to.have.lengthOf(10); + + // TEST '100' BIDS, add 90 to the previously added 10 + for (let i = 0; i < 90; i++) { + let bidCopy = clone(bidderRequest.bids[0]); + bidCopy.params.zoneId = `${(i + 10)}0000`; + bidderRequest.bids.push(bidCopy); + } + serverRequests = spec.buildRequests(bidderRequest.bids, bidderRequest); + // '100' bids: should be '10' SRA requests + expect(serverRequests.length).to.equal(10); + // check that each request has 10 items + serverRequests.forEach((serverRequest) => { + // and that one request should have data from 10 bids + expect(serverRequest.bidRequest).to.have.lengthOf(10); + // check that slots param value matches + expect(serverRequest.data.indexOf('&slots=10&') !== -1).to.equal(true); + }); }); it('should not group bid requests if singleRequest does not equal true', function () { From 8016edbdea4ce446c58f55f8cd46cab78344aef4 Mon Sep 17 00:00:00 2001 From: Andrew Slagle <42588549+spotxslagle@users.noreply.github.com> Date: Thu, 18 Apr 2019 18:16:18 -0600 Subject: [PATCH 006/146] SpotX: Add hide_skin parameter (#3760) --- modules/spotxBidAdapter.js | 4 ++++ modules/spotxBidAdapter.md | 4 +++- test/spec/modules/spotxBidAdapter_spec.js | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index bc109ccc635..6e374b991d3 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -84,6 +84,10 @@ export const spec = { versionOrtb: ORTB_VERSION }; + if (utils.getBidIdParameter('hide_skin', bid.params) != '') { + ext.hide_skin = +!!utils.getBidIdParameter('hide_skin', bid.params); + } + if (utils.getBidIdParameter('ad_volume', bid.params) != '') { ext.ad_volume = utils.getBidIdParameter('ad_volume', bid.params); } diff --git a/modules/spotxBidAdapter.md b/modules/spotxBidAdapter.md index db5fd448e04..b9907e5bbd2 100644 --- a/modules/spotxBidAdapter.md +++ b/modules/spotxBidAdapter.md @@ -63,7 +63,8 @@ This adapter requires setup and approval from the SpotX team. click_to_replay: '1', continue_out_of_view: '1', ad_volume: '100', - content_container_id: 'video1' + content_container_id: 'video1', + hide_skin: '1' } } } @@ -121,6 +122,7 @@ function myOutstreamFunction(bid) { script.setAttribute('data-spotx_click_to_replay', '1'); script.setAttribute('data-spotx_continue_out_of_view', '1'); script.setAttribute('data-spotx_ad_volume', '100'); + script.setAttribute('data-spotx_hide_skin', '1'); if (bid.renderer.config.inIframe && window.document.getElementById(bid.renderer.config.inIframe).nodeName == 'IFRAME') { let rawframe = window.document.getElementById(bid.renderer.config.inIframe); let framedoc = rawframe.contentDocument; diff --git a/test/spec/modules/spotxBidAdapter_spec.js b/test/spec/modules/spotxBidAdapter_spec.js index ba015331b68..33cf85a855e 100644 --- a/test/spec/modules/spotxBidAdapter_spec.js +++ b/test/spec/modules/spotxBidAdapter_spec.js @@ -150,6 +150,7 @@ describe('the spotx adapter', function () { expect(request.data.imp.video.ext).to.deep.equal({ ad_volume: 1, ad_unit: 'incontent', + hide_skin: 1, outstream_options: {foo: 'bar'}, outstream_function: '987', custom: {bar: 'foo'}, From 9ffff943bc14ffa39fdd95c3ee2a9c04b5a1e98e Mon Sep 17 00:00:00 2001 From: Rich Audience Date: Fri, 19 Apr 2019 02:18:50 +0200 Subject: [PATCH 007/146] Added dealId to response (#3762) --- modules/richAudienceBidAdapter.js | 1 + test/spec/modules/richAudienceBidAdapter_spec.js | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/richAudienceBidAdapter.js b/modules/richAudienceBidAdapter.js index 9701bb4ec48..bd47e481a76 100644 --- a/modules/richAudienceBidAdapter.js +++ b/modules/richAudienceBidAdapter.js @@ -91,6 +91,7 @@ export const spec = { netRevenue: response.netRevenue, currency: response.currency, ttl: response.ttl, + dealId: response.dealId, }; if (response.media_type === 'video') { diff --git a/test/spec/modules/richAudienceBidAdapter_spec.js b/test/spec/modules/richAudienceBidAdapter_spec.js index 689c29a2646..c974ff70ce3 100644 --- a/test/spec/modules/richAudienceBidAdapter_spec.js +++ b/test/spec/modules/richAudienceBidAdapter_spec.js @@ -81,7 +81,9 @@ describe('Rich Audience adapter tests', function () { creative_id: '189198063', netRevenue: true, currency: 'USD', - ttl: 300 + ttl: 300, + dealId: 'dealId' + } }; @@ -95,7 +97,8 @@ describe('Rich Audience adapter tests', function () { netRevenue: true, currency: 'USD', ttl: 300, - vastXML: '' + vastXML: '', + dealId: 'dealId' } }; @@ -228,6 +231,7 @@ describe('Rich Audience adapter tests', function () { expect(bid.netRevenue).to.equal(true); expect(bid.currency).to.equal('USD'); expect(bid.ttl).to.equal(300); + expect(bid.dealId).to.equal('dealId'); }); it('no banner media response', function () { From 12eceeabf984fbf4c706b5c6f3f606ea8e45f420 Mon Sep 17 00:00:00 2001 From: Vadim Mazzherin Date: Fri, 19 Apr 2019 20:22:05 +0600 Subject: [PATCH 008/146] add ShowHeroes Adapter (#3733) --- modules/shBidAdapter.js | 184 ++++++++++++++++++++++++ modules/shBidAdapter.md | 69 +++++++++ test/spec/modules/shBidAdapter_spec.js | 187 +++++++++++++++++++++++++ 3 files changed, 440 insertions(+) create mode 100644 modules/shBidAdapter.js create mode 100644 modules/shBidAdapter.md create mode 100644 test/spec/modules/shBidAdapter_spec.js diff --git a/modules/shBidAdapter.js b/modules/shBidAdapter.js new file mode 100644 index 00000000000..94d28770548 --- /dev/null +++ b/modules/shBidAdapter.js @@ -0,0 +1,184 @@ +import * as utils from '../src/utils'; +import { config } from '../src/config'; +import { registerBidder } from '../src/adapters/bidderFactory'; +import { VIDEO, BANNER } from '../src/mediaTypes'; + +const PROD_ENDPOINT = 'https://bs1.showheroes.com/api/v1/bid'; +const STAGE_ENDPOINT = 'https://bid-service.stage.showheroes.com/api/v1/bid'; +const PROD_PUBLISHER_TAG = 'https://static.showheroes.com/publishertag.js'; +const STAGE_PUBLISHER_TAG = 'https://pubtag.stage.showheroes.com/publishertag.js'; +const PROD_VL = 'https://video-library.showheroes.com'; +const STAGE_VL = 'https://video-library.stage.showheroes.com'; +const BIDDER_CODE = 'showheroes-bs'; +const TTL = 300; + +export const spec = { + code: BIDDER_CODE, + aliases: ['showheroesBs'], + supportedMediaTypes: [VIDEO, BANNER], + isBidRequestValid: function(bid) { + return !!bid.params.playerId; + }, + buildRequests: function(validBidRequests, bidderRequest) { + const pageURL = validBidRequests[0].params.contentPageUrl || bidderRequest.refererInfo.referer; + const isStage = !!validBidRequests[0].params.stage; + const isBanner = !!validBidRequests[0].mediaTypes.banner; + + let adUnits = validBidRequests.map((bid) => { + const vpaidMode = utils.getBidIdParameter('vpaidMode', bid.params); + + let sizes = bid.sizes.length === 1 ? bid.sizes[0] : bid.sizes; + if (sizes && !sizes.length) { + let mediaSize; + if (!isBanner) { + mediaSize = bid.mediaTypes.video.playerSize; + } else { + mediaSize = bid.mediaTypes.banner.sizes; + } + if (utils.isArray(mediaSize[0])) { + sizes = mediaSize[0]; + } else if (utils.isNumber(mediaSize[0])) { + sizes = mediaSize; + } + } + + const context = utils.deepAccess(bid, 'mediaTypes.video.context'); + + let streamType = 2; + + if (vpaidMode && context === 'instream') { + streamType = 1; + } + if (context === 'outstream' || isBanner) { + streamType = 5; + } + + return { + type: streamType, + bidId: bid.bidId, + mediaType: isBanner ? BANNER : VIDEO, + playerId: utils.getBidIdParameter('playerId', bid.params), + auctionId: bidderRequest.auctionId, + bidderCode: BIDDER_CODE, + gdprConsent: bidderRequest.gdprConsent, + start: +new Date(), + timeout: 3000, + video: { + width: sizes[0], + height: sizes[1] + }, + }; + }); + + return { + url: isStage ? STAGE_ENDPOINT : PROD_ENDPOINT, + method: 'POST', + options: {contentType: 'application/json', accept: 'application/json'}, + data: { + 'user': [], + 'meta': { + 'pageURL': encodeURIComponent(pageURL), + 'vastCacheEnabled': (!!config.getConfig('cache') && !isBanner) || false, + 'isDesktop': utils.getWindowTop().document.documentElement.clientWidth > 700, + 'stage': isStage || undefined + }, + 'requests': adUnits, + 'debug': validBidRequests[0].params.debug || false, + } + }; + }, + interpretResponse: function(response, request) { + return createBids(response.body, request.data); + }, + getUserSyncs: function(syncOptions, serverResponses) { + const syncs = []; + + if (!serverResponses.length || !serverResponses[0].body.userSync) { + return syncs; + } + + const userSync = serverResponses[0].body.userSync; + + if (syncOptions.iframeEnabled) { + (userSync.iframes || []).forEach(url => { + syncs.push({ + type: 'iframe', + url + }); + }); + } + + if (syncOptions.pixelEnabled) { + (userSync.pixels || []).forEach(url => { + syncs.push({ + type: 'image', + url + }); + }); + } + return syncs; + }, +}; + +function createBids(bidRes, reqData) { + if (bidRes && (!Array.isArray(bidRes.bids) || bidRes.bids.length < 1)) { + return []; + } + + const bids = []; + const bidMap = {}; + (reqData.requests || []).forEach((bid) => { + bidMap[bid.bidId] = bid; + }); + + bidRes.bids.forEach(function (bid) { + const reqBid = bidMap[bid.bidId]; + let bidUnit = {}; + bidUnit.cpm = bid.cpm; + bidUnit.requestId = bid.bidId; + bidUnit.currency = bid.currency; + bidUnit.mediaType = reqBid.mediaType || VIDEO; + bidUnit.ttl = TTL; + bidUnit.creativeId = 'c_' + bid.bidId; + bidUnit.netRevenue = true; + bidUnit.width = bid.video.width; + bidUnit.height = bid.video.height; + if (bid.vastXml) { + bidUnit.vastXml = bid.vastXml; + bidUnit.adResponse = { + content: bid.vastXml, + }; + } + if (bid.vastTag) { + bidUnit.vastUrl = bid.vastTag; + } + if (reqBid.mediaType === BANNER) { + bidUnit.ad = getBannerHtml(bid, reqBid, reqData); + } + bids.push(bidUnit); + }); + + return bids; +} + +function getBannerHtml (bid, reqBid, reqData) { + const isStage = !!reqData.meta.stage; + const pubTag = isStage ? STAGE_PUBLISHER_TAG : PROD_PUBLISHER_TAG; + const vlHost = isStage ? STAGE_VL : PROD_VL; + return ` + + + +
+ + `; +} + +registerBidder(spec); diff --git a/modules/shBidAdapter.md b/modules/shBidAdapter.md new file mode 100644 index 00000000000..b1ca1782725 --- /dev/null +++ b/modules/shBidAdapter.md @@ -0,0 +1,69 @@ +# Overview + +Module Name: ShowHeroes Bidder Adapter + +Module Type: Bidder Adapter + +Maintainer: tech@showheroes.com + +# Description + +Module that connects to ShowHeroes demand source to fetch bids. + +# Test Parameters +``` + var adUnits = [ + { + code: 'video', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream', + } + }, + bids: [ + { + bidder: "showheroes-bs", + params: { + playerId: '0151f985-fb1a-4f37-bb26-cfc62e43ec05', + vpaidMode: true // by default is 'false' + } + } + ] + }, + { + code: 'video', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'outstream', + } + }, + bids: [ + { + bidder: "showheroes-bs", + params: { + playerId: '0151f985-fb1a-4f37-bb26-cfc62e43ec05', + vpaidMode: true // by default is 'false' + } + } + ] + }, + { + code: 'banner', + mediaTypes: { + banner: { + sizes: [[640, 480]], + } + }, + bids: [ + { + bidder: "showheroes-bs", + params: { + playerId: '0151f985-fb1a-4f37-bb26-cfc62e43ec05', + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/shBidAdapter_spec.js b/test/spec/modules/shBidAdapter_spec.js new file mode 100644 index 00000000000..588d4c54150 --- /dev/null +++ b/test/spec/modules/shBidAdapter_spec.js @@ -0,0 +1,187 @@ +import {expect} from 'chai' +import {spec} from 'modules/shBidAdapter' +import {newBidder} from 'src/adapters/bidderFactory' +import {VIDEO, BANNER} from 'src/mediaTypes' + +const bidderRequest = { + refererInfo: { + referer: 'http://example.com' + } +} + +const gdpr = { + 'gdprConsent': { + 'consentString': 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA', + 'gdprApplies': true + } +} + +const bidRequestVideo = { + 'bidder': 'showheroes-bs', + 'params': { + 'playerId': '47427aa0-f11a-4d24-abca-1295a46a46cd', + }, + 'mediaTypes': { + 'video': { + 'playerSize': [640, 480], + 'context': 'instream', + } + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[640, 480]], + 'bidId': '38b373e1e31c18', + 'bidderRequestId': '12e3ade2543ba6', + 'auctionId': '43aa080090a47f', +} + +const bidRequestVideoVpaid = { + 'bidder': 'showheroes-bs', + 'params': { + 'playerId': '47427aa0-f11a-4d24-abca-1295a46a46cd', + 'vpaidMode': true, + }, + 'mediaTypes': { + 'video': { + 'playerSize': [640, 480], + 'context': 'instream', + } + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[640, 480]], + 'bidId': '38b373e1e31c18', + 'bidderRequestId': '12e3ade2543ba6', + 'auctionId': '43aa080090a47f', +} + +const bidRequestBanner = { + 'bidder': 'showheroes-bs', + 'params': { + 'playerId': '47427aa0-f11a-4d24-abca-1295a46a46cd', + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[640, 360]] + } + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[640, 480]], + 'bidId': '38b373e1e31c18', + 'bidderRequestId': '12e3ade2543ba6', + 'auctionId': '43aa080090a47f', +} + +describe('shBidAdapter', function () { + const adapter = newBidder(spec) + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function') + }) + }) + + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + const request = { + 'params': { + 'playerId': '47427aa0-f11a-4d24-abca-1295a46a46cd', + } + } + expect(spec.isBidRequestValid(request)).to.equal(true) + }) + + it('should return false when required params are not passed', function () { + const request = { + 'params': {} + } + expect(spec.isBidRequestValid(request)).to.equal(false) + }) + }) + + describe('buildRequests', function () { + it('sends bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests([bidRequestVideo], bidderRequest) + expect(request.method).to.equal('POST') + }) + + it('should attach valid params to the payload when type is video', function () { + const request = spec.buildRequests([bidRequestVideo], bidderRequest) + const payload = request.data.requests[0]; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('playerId', '47427aa0-f11a-4d24-abca-1295a46a46cd'); + expect(payload).to.have.property('mediaType', VIDEO); + expect(payload).to.have.property('type', 2); + }) + + it('should attach valid params to the payload when type is video & vpaid mode on', function () { + const request = spec.buildRequests([bidRequestVideoVpaid], bidderRequest) + const payload = request.data.requests[0]; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('playerId', '47427aa0-f11a-4d24-abca-1295a46a46cd'); + expect(payload).to.have.property('mediaType', VIDEO); + expect(payload).to.have.property('type', 1); + }) + + it('should attach valid params to the payload when type is banner', function () { + const request = spec.buildRequests([bidRequestBanner], bidderRequest) + const payload = request.data.requests[0]; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('playerId', '47427aa0-f11a-4d24-abca-1295a46a46cd'); + expect(payload).to.have.property('mediaType', BANNER); + expect(payload).to.have.property('type', 5); + }) + + it('passes gdpr if present', function () { + const request = spec.buildRequests([bidRequestVideo], {...bidderRequest, ...gdpr}) + const payload = request.data.requests[0]; + expect(payload).to.be.an('object'); + expect(payload.gdprConsent).to.eql(gdpr.gdprConsent) + }) + }) + + describe('interpretResponse', function () { + it('handles nobid responses', function () { + expect(spec.interpretResponse({body: {}}, {data: {meta: {}}}).length).to.equal(0) + expect(spec.interpretResponse({body: []}, {data: {meta: {}}}).length).to.equal(0) + }) + + const response = { + 'bids': [{ + 'cpm': 5, + 'currency': 'EUR', + 'bidId': '38b373e1e31c18', + 'video': {'width': 640, 'height': 480}, + 'vastTag': 'https:\/\/video-library.stage.showheroes.com\/commercial\/wrapper?player_id=47427aa0-f11a-4d24-abca-1295a46a46cd&ad_bidder=showheroes-bs&master_shadt=1&description_url=https%3A%2F%2Fbid-service.stage.showheroes.com%2Fvast%2Fad%2Fcache%2F4840b920-40e1-4e09-9231-60bbf088c8d6', + }], + } + + it('should get correct bid response when type is video', function () { + const request = spec.buildRequests([bidRequestVideo], bidderRequest) + const expectedResponse = [ + { + 'cpm': 5, + 'creativeId': 'c_38b373e1e31c18', + 'currency': 'EUR', + 'width': 640, + 'height': 480, + 'mediaType': 'video', + 'netRevenue': true, + 'vastUrl': 'https://video-library.stage.showheroes.com/commercial/wrapper?player_id=47427aa0-f11a-4d24-abca-1295a46a46cd&ad_bidder=showheroes-bs&master_shadt=1&description_url=https%3A%2F%2Fbid-service.stage.showheroes.com%2Fvast%2Fad%2Fcache%2F4840b920-40e1-4e09-9231-60bbf088c8d6', + 'requestId': '38b373e1e31c18', + 'ttl': 300, + } + ] + + const result = spec.interpretResponse({'body': response}, request) + expect(result).to.deep.equal(expectedResponse) + }) + + it('should get correct bid response when type is banner', function () { + const request = spec.buildRequests([bidRequestBanner], bidderRequest) + + const result = spec.interpretResponse({'body': response}, request) + expect(result[0]).to.have.property('mediaType', BANNER); + expect(result[0].ad).to.include('' + adm: '', + nurl: '//uat-net.technoratimedia.com/openrtb/tags?ID=k5JkFVQ1RJT05fSU1QX0lEPXYyZjczN&AUCTION_PRICE=${AUCTION_PRICE}' }; let bidResponse2 = { id: '10865933907263800~9999~0', - impid: '9876abcd~300x600', + impid: 'b9876abcd-300x600', price: 1.99, crid: '9993-013', - adm: '' + adm: '', + nurl: '//uat-net.technoratimedia.com/openrtb/tags?ID=OTk5OX4wJkFVQ1RJT05fU0VBVF9JR&AUCTION_PRICE=${AUCTION_PRICE}' }; let serverResponse; @@ -282,6 +436,52 @@ describe('synacormediaBidAdapter ', function () { } }; }); + + it('should return 1 video bid when 1 bid is in the video response', function () { + let serverRespVideo = { + body: { + id: 'abcd1234', + seatbid: [ + { + bid: [ + { + id: '11339128001692337~9999~0', + impid: 'v2da7322b2df61f-640x480', + price: 0.45, + nurl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=${AUCTION_PRICE}', + adm: '\n\n\n\nSynacor Media Ad Server - 9999\nhttps://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=${AUCTION_PRICE}\n\n\n', + adomain: [ 'psacentral.org' ], + cid: 'bidder-crid', + crid: 'bidder-cid', + cat: [] + } + ], + seat: '9999' + } + ] + } + }; + + // serverResponse.body.seatbid[0].bid.push(bidResponse); + let resp = spec.interpretResponse(serverRespVideo); + expect(resp).to.be.an('array').to.have.lengthOf(1); + expect(resp[0]).to.eql({ + requestId: '2da7322b2df61f', + adId: '11339128001692337-9999-0', + cpm: 0.45, + width: 640, + height: 480, + creativeId: '9999_bidder-cid', + currency: 'USD', + netRevenue: true, + mediaType: 'video', + ad: '\n\n\n\nSynacor Media Ad Server - 9999\nhttps://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=0.45\n\n\n', + ttl: 60, + videoCacheKey: 'QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk', + vastUrl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=0.45' + }); + }); + it('should return 1 bid when 1 bid is in the response', function () { serverResponse.body.seatbid[0].bid.push(bidResponse); let resp = spec.interpretResponse(serverResponse); @@ -292,11 +492,11 @@ describe('synacormediaBidAdapter ', function () { cpm: 0.13, width: 300, height: 250, - creativeId: '9998~1022-250', + creativeId: '9998_1022-250', currency: 'USD', netRevenue: true, mediaType: BANNER, - ad: '', + ad: '', ttl: 60 }); }); @@ -315,31 +515,34 @@ describe('synacormediaBidAdapter ', function () { cpm: 0.13, width: 300, height: 250, - creativeId: '9998~1022-250', + creativeId: '9998_1022-250', currency: 'USD', netRevenue: true, mediaType: BANNER, - ad: '', + ad: '', ttl: 60 }); + expect(resp[1]).to.eql({ requestId: '9876abcd', adId: '10865933907263800-9999-0', cpm: 1.99, width: 300, height: 600, - creativeId: '9999~9993-013', + creativeId: '9999_9993-013', currency: 'USD', netRevenue: true, mediaType: BANNER, - ad: '', + ad: '', ttl: 60 }); }); + it('should not return a bid when no bid is in the response', function () { let resp = spec.interpretResponse(serverResponse); expect(resp).to.be.an('array').that.is.empty; }); + it('should not return a bid when there is no response body', function () { expect(spec.interpretResponse({ body: null })).to.not.exist; expect(spec.interpretResponse({ body: 'some error text' })).to.not.exist; From 253cbf4de5d575a70a288b3249d7bf4f1f7ddfc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E9=9B=A8=E8=BB=92=20=D0=9F=D0=B5=D1=82=D1=80?= Date: Mon, 22 Apr 2019 20:09:08 +0200 Subject: [PATCH 019/146] Improve emoteevBidAdapter (#3673) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Improve emoteevBidAdapter ** Squash several pending changes - Improve test coverage - Extreme programming: 100% test coverage - Document important constants - Extreme programming: document all functions - Imperative shell, functional core - Send events onBidWon and onTimeout - Report GDPR relevance and consent Code documentation uses JSDoc tags wherever possible. See the following link for general explanation of the notation used: https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler ** Test coverage - 100% Statements 110/110 - 100% Branches 55/55 - 100% Functions 28/28 - 100% Lines 99/99 ** Integration tests Tested against production endpoint with the possible combinations of these following parameters: - Browser: · Chrome Canary 75.0.3739.0 · Firefox Developer Edition 67.0b3 (64-bit) · Safari version 12.0.3 (14606.4.5) · Tor Browser 8.0.6 (based on Mozilla Firefox 60.5.1esr) - Integration page: · integrationExamples/gpt/hello_world_emoteev.html · https://jsfiddle.net/8aqotw1k/6/ Localhost test server launched with: $ npm install && gulp serve * Tentative CI fix for IE and Edge webGL test https://circleci.com/gh/prebid/Prebid.js/2052 * Tentative CI fix for IE and Edge webGL test #2 * Give up on webGL tests Has anybody an idea to make it pass? https://circleci.com/gh/prebid/Prebid.js/2056 New test coverage: - 94.5% Statements 103/109 - 98.18% Branches 54/55 - 100% Functions 28/28 - 93.88% Lines 92/98 * Avoid useless noice in diff * Remove unallowed metric pixel ON_ADAPTER_CALLED --- .../gpt/hello_world_emoteev.html | 117 ++- modules/emoteevBidAdapter.js | 465 ++++++++-- ...teevBidAdapter.md => emoteevBidAdapter.md} | 0 test/spec/modules/emoteevBidAdapter_spec.js | 860 +++++++++++++----- 4 files changed, 1056 insertions(+), 386 deletions(-) rename modules/{emokteevBidAdapter.md => emoteevBidAdapter.md} (100%) diff --git a/integrationExamples/gpt/hello_world_emoteev.html b/integrationExamples/gpt/hello_world_emoteev.html index 5e4d0716d2b..5a33e2d9701 100644 --- a/integrationExamples/gpt/hello_world_emoteev.html +++ b/integrationExamples/gpt/hello_world_emoteev.html @@ -5,74 +5,69 @@ + setTimeout(function () { + initAdserver(); + }, FAILSAFE_TIMEOUT); + googletag.cmd.push(function () { + googletag.defineSlot('/19968336/header-bid-tag-1', sizes, 'div-1') + .addService(googletag.pubads()); + googletag.pubads().enableSingleRequest(); + googletag.enableServices(); + }); + @@ -80,9 +75,9 @@

Basic Prebid.js Example

Div-1
diff --git a/modules/emoteevBidAdapter.js b/modules/emoteevBidAdapter.js index 9b03b357818..4436d39bb70 100644 --- a/modules/emoteevBidAdapter.js +++ b/modules/emoteevBidAdapter.js @@ -1,76 +1,322 @@ +/** + * This file contains Emoteev bid adpater. + * + * It is organised as follows: + * - Constants values; + * - Spec API functions, which should be pristine pure; + * - Ancillary functions, which should be as pure as possible; + * - Adapter API, where unpure side-effects happen. + * + * The code style is « functional core, imperative shell ». + * + * @link https://www.emoteev.io + * @file This files defines the spec of EmoteevBidAdapter. + * @author Emoteev Engineering . + */ + import {registerBidder} from '../src/adapters/bidderFactory'; import {BANNER} from '../src/mediaTypes'; -import * as utils from '../src/utils'; +import { + triggerPixel, + getUniqueIdentifierStr, + contains, + deepAccess, + isArray, + getParameterByName +} from '../src/utils'; import {config} from '../src/config'; +import * as url from '../src/url'; +import {getCookie} from './pubCommonId'; export const BIDDER_CODE = 'emoteev'; -export const AK_PBJS_VERSION = '1.35.0'; -export const EMOTEEV_BASE_URL = 'https://prebid.emoteev.io'; -export const EMOTEEV_BASE_URL_STAGING = 'https://prebid-staging.emoteev.io'; -export const EMOTEEV_BASE_URL_DEVELOPMENT = 'http://localhost:3000'; +/** + * Version number of the adapter API. + */ +export const ADAPTER_VERSION = '1.35.0'; + +export const DOMAIN = 'prebid.emoteev.io'; +export const DOMAIN_STAGING = 'prebid-staging.emoteev.io'; +export const DOMAIN_DEVELOPMENT = 'localhost:3000'; -export const ENDPOINT_PATH = '/api/prebid/bid'; -export const USER_SYNC_IFRAME_URL_PATH = '/api/prebid/sync-iframe'; -export const USER_SYNC_IMAGE_URL_PATH = '/api/prebid/sync-image'; +/** + * Path of Emoteev endpoint for events. + */ +export const EVENTS_PATH = '/api/ad_event.json'; + +/** + * Path of Emoteev bidder. + */ +export const BIDDER_PATH = '/api/prebid/bid'; +export const USER_SYNC_IFRAME_PATH = '/api/prebid/sync-iframe'; +export const USER_SYNC_IMAGE_PATH = '/api/prebid/sync-image'; export const PRODUCTION = 'production'; export const STAGING = 'staging'; export const DEVELOPMENT = 'development'; export const DEFAULT_ENV = PRODUCTION; -export const conformBidRequest = bidRequest => { +export const ON_ADAPTER_CALLED = 'on_adapter_called'; +export const ON_BID_WON = 'on_bid_won'; +export const ON_BIDDER_TIMEOUT = 'on_bidder_timeout'; + +/** + * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#valid-build-requests-array for detailed semantic. + * + * @param {AdUnit.bidRequest} bidRequest + * @returns {boolean} Is this bidRequest valid? + */ +export const isBidRequestValid = (bidRequest) => { + return !!( + bidRequest && + bidRequest.params && + deepAccess(bidRequest, 'params.adSpaceId') && + bidRequest.bidder === BIDDER_CODE && + validateSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes'))); +}; + +/** + * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#serverrequest-objects for detailed semantic. + * + * @param {string} env Emoteev environment parameter + * @param {boolean} debug Pbjs debug parameter. + * @param {string} currency See http://prebid.org/dev-docs/modules/currency.html for detailed semantic. + * @param {Array} validBidRequests Takes an array of bid requests, which are guaranteed to have passed the isBidRequestValid() test. + * @param bidderRequest General context for a bidder request being constructed + * @returns {ServerRequest} + */ +export const buildRequests = (env, debug, currency, validBidRequests, bidderRequest) => { return { - params: bidRequest.params, - crumbs: bidRequest.crumbs, - sizes: bidRequest.sizes, - bidId: bidRequest.bidId, - bidderRequestId: bidRequest.bidderRequestId, + method: 'POST', + url: bidderUrl(env), + data: JSON.stringify(requestsPayload(debug, currency, validBidRequests, bidderRequest)) }; }; -export const emoteevDebug = (parameterDebug, configDebug) => { - if (parameterDebug && parameterDebug.length && parameterDebug.length > 0) return JSON.parse(parameterDebug); - else if (configDebug) return configDebug; - else return false; -}; +/** + * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#interpreting-the-response for detailed semantic. + * + * @param {Array} serverResponse.body The body of the server response is an array of bid objects. + * @returns {Array} + */ +export const interpretResponse = (serverResponse) => serverResponse.body; -export const emoteevEnv = (parameteremoteevEnv, configemoteevEnv) => { - if (utils.contains([PRODUCTION, STAGING, DEVELOPMENT], parameteremoteevEnv)) return parameteremoteevEnv; - else if (utils.contains([PRODUCTION, STAGING, DEVELOPMENT], configemoteevEnv)) return configemoteevEnv; - else return DEFAULT_ENV; +/** + * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#registering-on-set-targeting for detailed semantic. + * + * @param {string} env Emoteev environment parameter. + * @param {BidRequest} bidRequest + * @returns {UrlObject} + */ +export function onAdapterCalled(env, bidRequest) { + return { + protocol: 'https', + hostname: domain(env), + pathname: EVENTS_PATH, + search: { + eventName: ON_ADAPTER_CALLED, + pubcId: deepAccess(bidRequest, 'crumbs.pubcid'), + bidId: bidRequest.bidId, + adSpaceId: deepAccess(bidRequest, 'params.adSpaceId'), + cache_buster: getUniqueIdentifierStr() + } + }; +} + +/** + * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#registering-on-bid-won for detailed semantic. + * + * @param {string} env Emoteev environment parameter. + * @param {string} pubcId Publisher common id. See http://prebid.org/dev-docs/modules/pubCommonId.html for detailed semantic. + * @param bidObject + * @returns {UrlObject} + */ +export const onBidWon = (env, pubcId, bidObject) => { + const bidId = bidObject.requestId; + return { + protocol: 'https', + hostname: domain(env), + pathname: EVENTS_PATH, + search: { + eventName: ON_BID_WON, + pubcId, + bidId, + cache_buster: getUniqueIdentifierStr() + } + }; }; -export const emoteevOverrides = (parameteremoteevOverrides, configemoteevOverrides) => { - if (parameteremoteevOverrides && parameteremoteevOverrides.length !== 0) { - let parsedParams = null; - try { - parsedParams = JSON.parse(parameteremoteevOverrides); - } catch (error) { - parsedParams = null; +/** + * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#registering-on-timeout for detailed semantic. + * + * @param {string} env Emoteev environment parameter. + * @param {BidRequest} bidRequest + * @returns {UrlObject} + */ +export const onTimeout = (env, bidRequest) => { + return { + protocol: 'https', + hostname: domain(env), + pathname: EVENTS_PATH, + search: { + eventName: ON_BIDDER_TIMEOUT, + pubcId: deepAccess(bidRequest, 'crumbs.pubcid'), + bidId: bidRequest.bidId, + adSpaceId: deepAccess(bidRequest, 'params.adSpaceId'), + timeout: bidRequest.timeout, + cache_buster: getUniqueIdentifierStr() } - if (parsedParams) return parsedParams; } - if (configemoteevOverrides && Object.keys(configemoteevOverrides).length !== 0) return configemoteevOverrides; - else return {}; }; -export const akUrl = (environment) => { - switch (environment) { +/** + * Pure function. See http://prebid.org/dev-docs/bidder-adaptor.html#registering-user-syncs for detailed semantic. + * + * @param {string} env Emoteev environment parameter + * @param {SyncOptions} syncOptions + * @returns userSyncs + */ +export const getUserSyncs = (env, syncOptions) => { + let syncs = []; + if (syncOptions.pixelEnabled) { + syncs.push({ + type: 'image', + url: userSyncImageUrl(env), + }); + } + if (syncOptions.iframeEnabled) { + syncs.push({ + type: 'iframe', + url: userSyncIframeUrl(env), + }); + } + return syncs; +}; + +/** + * Pure function. + * + * @param {string} env Emoteev environment parameter + * @returns {string} The domain for network calls to Emoteev. + */ +export const domain = (env) => { + switch (env) { case DEVELOPMENT: - return EMOTEEV_BASE_URL_DEVELOPMENT; + return DOMAIN_DEVELOPMENT; case STAGING: - return EMOTEEV_BASE_URL_STAGING; + return DOMAIN_STAGING; default: - return EMOTEEV_BASE_URL; + return DOMAIN; } }; -export const endpointUrl = (parameteremoteevEnv, configemoteevEnv) => akUrl(emoteevEnv(parameteremoteevEnv, configemoteevEnv)).concat(ENDPOINT_PATH); -export const userSyncIframeUrl = (parameteremoteevEnv, configemoteevEnv) => akUrl(emoteevEnv(parameteremoteevEnv, configemoteevEnv)).concat(USER_SYNC_IFRAME_URL_PATH); -export const userSyncImageUrl = (parameteremoteevEnv, configemoteevEnv) => akUrl(emoteevEnv(parameteremoteevEnv, configemoteevEnv)).concat(USER_SYNC_IMAGE_URL_PATH); +/** + * Pure function. + * + * @param {string} env Emoteev environment parameter + * @returns {string} The full URL which events is sent to. + */ +export const eventsUrl = env => url.format({ + protocol: (env === DEVELOPMENT) ? 'http' : 'https', + hostname: domain(env), + pathname: EVENTS_PATH +}); -export const getViewDimensions = () => { +/** + * Pure function. + * + * @param {string} env Emoteev environment parameter + * @returns {string} The full URL which bidderRequest is sent to. + */ +export const bidderUrl = env => url.format({ + protocol: (env === DEVELOPMENT) ? 'http' : 'https', + hostname: domain(env), + pathname: BIDDER_PATH +}); + +/** + * Pure function. + * + * @param {string} env Emoteev environment parameter + * @returns {string} The full URL called for iframe-based user sync + */ +export const userSyncIframeUrl = env => url.format({ + protocol: (env === DEVELOPMENT) ? 'http' : 'https', + hostname: domain(env), + pathname: USER_SYNC_IFRAME_PATH +}); + +/** + * Pure function. + * + * @param {string} env Emoteev environment parameter + * @returns {string} The full URL called for image-based user sync + */ +export const userSyncImageUrl = env => url.format({ + protocol: (env === DEVELOPMENT) ? 'http' : 'https', + hostname: domain(env), + pathname: USER_SYNC_IMAGE_PATH +}); + +/** + * Pure function. + * + * @param {Array>} sizes + * @returns {boolean} are sizes valid? + */ +const validateSizes = sizes => isArray(sizes) && sizes.some(size => isArray(size) && size.length === 2); + +/** + * Pure function. + * + * @param {BidRequest} bidRequest + * @returns {object} An object which represents a BidRequest for Emoteev server side. + */ +export const conformBidRequest = bidRequest => { + return { + params: bidRequest.params, + crumbs: bidRequest.crumbs, + sizes: bidRequest.sizes, + bidId: bidRequest.bidId, + bidderRequestId: bidRequest.bidderRequestId, + }; +}; + +/** + * Pure function. + * + * @param {boolean} debug Pbjs debug parameter + * @param {string} currency See http://prebid.org/dev-docs/modules/currency.html for detailed information + * @param {BidRequest} validBidRequests + * @param {object} bidderRequest + * @returns + */ +export const requestsPayload = (debug, currency, validBidRequests, bidderRequest) => { + return { + akPbjsVersion: ADAPTER_VERSION, + bidRequests: validBidRequests.map(conformBidRequest), + currency: currency, + debug: debug, + language: navigator.language, + refererInfo: bidderRequest.refererInfo, + deviceInfo: getDeviceInfo( + getDeviceDimensions(window), + getViewDimensions(window, document), + getDocumentDimensions(document), + isWebGLEnabled(document)), + userAgent: navigator.userAgent, + gdprApplies: deepAccess(bidderRequest, 'gdprConsent.gdprApplies'), + gdprConsent: deepAccess(bidderRequest, 'gdprConsent.consentString'), + }; +}; + +/** + * Pure function + * @param {Window} window + * @param {Document} document + * @returns {{width: number, height: number}} View dimensions + */ +export const getViewDimensions = (window, document) => { let w = window; let prefix = 'inner'; @@ -85,14 +331,24 @@ export const getViewDimensions = () => { }; }; -export const getDeviceDimensions = () => { +/** + * Pure function + * @param {Window} window + * @returns {{width: number, height: number}} Device dimensions + */ +export const getDeviceDimensions = (window) => { return { width: window.screen ? window.screen.width : '', height: window.screen ? window.screen.height : '', }; }; -export const getDocumentDimensions = () => { +/** + * Pure function + * @param {Document} document + * @returns {{width: number, height: number}} Document dimensions + */ +export const getDocumentDimensions = (document) => { const de = document.documentElement; const be = document.body; @@ -112,7 +368,12 @@ export const getDocumentDimensions = () => { }; }; -export const isWebGLEnabled = () => { +/** + * Unpure function + * @param {Document} document + * @returns {boolean} Is WebGL enabled? + */ +export const isWebGLEnabled = (document) => { // Create test canvas let canvas = document.createElement('canvas'); @@ -141,6 +402,14 @@ export const isWebGLEnabled = () => { return !!gl; }; +/** + * Pure function + * @param {{width: number, height: number}} deviceDimensions + * @param {{width: number, height: number}} viewDimensions + * @param {{width: number, height: number}} documentDimensions + * @param {boolean} webGL + * @returns {object} Device information + */ export const getDeviceInfo = (deviceDimensions, viewDimensions, documentDimensions, webGL) => { return { browserWidth: viewDimensions.width, @@ -153,62 +422,62 @@ export const getDeviceInfo = (deviceDimensions, viewDimensions, documentDimensio }; }; -const validateSizes = sizes => utils.isArray(sizes) && sizes.some(size => utils.isArray(size) && size.length === 2); +/** + * Pure function + * @param {object} config pbjs config value + * @param {string} parameter Environment override from URL query param. + * @returns One of [PRODUCTION, STAGING, DEVELOPMENT]. + */ +export const resolveEnv = (config, parameter) => { + const configEnv = deepAccess(config, 'emoteev.env'); + + if (contains([PRODUCTION, STAGING, DEVELOPMENT], parameter)) return parameter; + else if (contains([PRODUCTION, STAGING, DEVELOPMENT], configEnv)) return configEnv; + else return DEFAULT_ENV; +}; + +/** + * Pure function + * @param {object} config pbjs config value + * @param {string} parameter Debug override from URL query param. + * @returns {boolean} + */ +export const resolveDebug = (config, parameter) => { + if (parameter && parameter.length && parameter.length > 0) return JSON.parse(parameter); + else if (config.debug) return config.debug; + else return false; +}; +/** + * EmoteevBidAdapter spec + * @access public + * @type {BidderSpec} + */ export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER], - - isBidRequestValid: (bid) => { - return !!( - bid && - bid.params && - bid.params.adSpaceId && - bid.bidder === BIDDER_CODE && - validateSizes(bid.mediaTypes.banner.sizes) - ); - }, - - buildRequests: (validBidRequests, bidderRequest) => { - const payload = Object.assign({}, - { - akPbjsVersion: AK_PBJS_VERSION, - bidRequests: validBidRequests.map(conformBidRequest), - currency: config.getConfig('currency'), - debug: emoteevDebug(utils.getParameterByName('emoteevDebug'), config.getConfig('emoteev.debug')), - language: navigator.language, - refererInfo: bidderRequest.refererInfo, - deviceInfo: getDeviceInfo(getDeviceDimensions(), getViewDimensions(), getDocumentDimensions(), isWebGLEnabled()), - userAgent: navigator.userAgent, - }, - emoteevOverrides(utils.getParameterByName('emoteevOverrides'), config.getConfig('emoteev.overrides'))); - - return { - method: 'POST', - url: endpointUrl(utils.getParameterByName('emoteevEnv'), config.getConfig('emoteev.env')), - data: JSON.stringify(payload), - }; - }, - - interpretResponse: (serverResponse) => serverResponse.body, - - getUserSyncs: (syncOptions, serverResponses) => { - const parameteremoteevEnv = utils.getParameterByName('emoteev.env'); - const configemoteevEnv = config.getConfig('emoteev.env'); - const syncs = []; - if (syncOptions.iframeEnabled) { - syncs.push({ - type: 'iframe', - url: userSyncIframeUrl(parameteremoteevEnv, configemoteevEnv), - }); - } - if (syncOptions.pixelEnabled && serverResponses.length > 0) { - syncs.push({ - type: 'image', - url: userSyncImageUrl(parameteremoteevEnv, configemoteevEnv), - }); - } - return syncs; - }, + isBidRequestValid: isBidRequestValid, + buildRequests: (validBidRequests, bidderRequest) => + buildRequests( + resolveEnv(config.getConfig(), getParameterByName('emoteevEnv')), + resolveDebug(config.getConfig(), getParameterByName('debug')), + config.getConfig('currency'), + validBidRequests, + bidderRequest), + interpretResponse: interpretResponse, + onBidWon: (bidObject) => + triggerPixel(url.format(onBidWon( + resolveEnv(config.getConfig(), getParameterByName('emoteevEnv')), + getCookie('_pubcid'), + bidObject))), + onTimeout: (bidRequest) => + triggerPixel(url.format(onTimeout( + resolveEnv(config.getConfig(), getParameterByName('emoteevEnv')), + bidRequest))), + getUserSyncs: (syncOptions) => + getUserSyncs( + resolveEnv(config.getConfig(), getParameterByName('emoteevEnv')), + syncOptions), }; + registerBidder(spec); diff --git a/modules/emokteevBidAdapter.md b/modules/emoteevBidAdapter.md similarity index 100% rename from modules/emokteevBidAdapter.md rename to modules/emoteevBidAdapter.md diff --git a/test/spec/modules/emoteevBidAdapter_spec.js b/test/spec/modules/emoteevBidAdapter_spec.js index a5f5c439e6f..a5460ab939d 100644 --- a/test/spec/modules/emoteevBidAdapter_spec.js +++ b/test/spec/modules/emoteevBidAdapter_spec.js @@ -1,26 +1,49 @@ -import {expect} from 'chai'; import { - AK_PBJS_VERSION, - EMOTEEV_BASE_URL, - EMOTEEV_BASE_URL_STAGING, - emoteevDebug, - emoteevEnv, - emoteevOverrides, - akUrl, + assert, expect +} from 'chai'; +import { + ADAPTER_VERSION, + DOMAIN, + DOMAIN_DEVELOPMENT, + DOMAIN_STAGING, + domain, + BIDDER_PATH, + bidderUrl, + buildRequests, conformBidRequest, DEFAULT_ENV, - ENDPOINT_PATH, - endpointUrl, + DEVELOPMENT, + EVENTS_PATH, + eventsUrl, + getDeviceDimensions, + getDeviceInfo, + getDocumentDimensions, + getUserSyncs, + getViewDimensions, + interpretResponse, + isBidRequestValid, + isWebGLEnabled, + ON_ADAPTER_CALLED, + ON_BID_WON, + ON_BIDDER_TIMEOUT, + onBidWon, + onAdapterCalled, + onTimeout, PRODUCTION, + requestsPayload, + resolveDebug, + resolveEnv, spec, STAGING, - USER_SYNC_IFRAME_URL_PATH, - USER_SYNC_IMAGE_URL_PATH, + USER_SYNC_IFRAME_PATH, + USER_SYNC_IMAGE_PATH, userSyncIframeUrl, userSyncImageUrl, } from 'modules/emoteevBidAdapter'; -import {newBidder} from 'src/adapters/bidderFactory'; -import {config} from 'src/config'; +import * as url from '../../../src/url'; +import * as utils from '../../../src/utils'; +import * as pubCommonId from '../../../modules/pubCommonId'; +import {config} from '../../../src/config'; const cannedValidBidRequests = [{ adUnitCode: '/19968336/header-bid-tag-1', @@ -48,7 +71,11 @@ const cannedBidderRequest = { stack: ['http://localhost:9999/integrationExamples/gpt/hello_world_emoteev.html'] }, start: 1544200012839, - timeout: 3000 + timeout: 3000, + gdprConsent: { + gdprApplies: true, + consentString: 'my consentString' + } }; const serverResponse = { @@ -68,106 +95,11 @@ const serverResponse = }; describe('emoteevBidAdapter', function () { - const adapter = newBidder(spec); - - describe('inherited functions', function () { - it('exists and is a function', function () { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - }); - - describe('conformBidRequest', function () { - it('returns a bid-request', function () { - expect(conformBidRequest(cannedValidBidRequests[0])).to.deep.equal({ - params: cannedValidBidRequests[0].params, - crumbs: cannedValidBidRequests[0].crumbs, - sizes: cannedValidBidRequests[0].sizes, - bidId: cannedValidBidRequests[0].bidId, - bidderRequestId: cannedValidBidRequests[0].bidderRequestId, - }); - }) - }); - - describe('emoteevDebug', function () { - expect(emoteevDebug(null, null)).to.deep.equal(false) - }); - describe('emoteevDebug', function () { - expect(emoteevDebug(null, true)).to.deep.equal(true) - }); - describe('emoteevDebug', function () { - expect(emoteevDebug(JSON.stringify(true), null)).to.deep.equal(true) - }); - - describe('emoteevEnv', function () { - expect(emoteevEnv(null, null)).to.deep.equal(DEFAULT_ENV) - }); - describe('emoteevEnv', function () { - expect(emoteevEnv(null, STAGING)).to.deep.equal(STAGING) - }); - describe('emoteevEnv', function () { - expect(emoteevEnv(STAGING, null)).to.deep.equal(STAGING) - }); - - describe('emoteevOverrides', function () { - expect(emoteevOverrides(null, null)).to.deep.equal({}) - }); - describe('emoteevOverrides', function () { - expect(emoteevOverrides(JSON.stringify({a: 1}), null)).to.deep.equal({a: 1}) - }); - describe('emoteevOverrides', function () { - expect(emoteevOverrides('incorrect', null)).to.deep.equal({}) - }); // expect no exception - describe('emoteevOverrides', function () { - expect(emoteevOverrides(null, {a: 1})).to.deep.equal({a: 1}) - }); - - describe('akUrl', function () { - expect(akUrl(null)).to.deep.equal(EMOTEEV_BASE_URL) - }); - describe('akUrl', function () { - expect(akUrl('anything')).to.deep.equal(EMOTEEV_BASE_URL) - }); - describe('akUrl', function () { - expect(akUrl(STAGING)).to.deep.equal(EMOTEEV_BASE_URL_STAGING) - }); - describe('akUrl', function () { - expect(akUrl('production')).to.deep.equal(EMOTEEV_BASE_URL) - }); - - describe('endpointUrl', function () { - expect(endpointUrl(null, null)).to.deep.equal(EMOTEEV_BASE_URL.concat(ENDPOINT_PATH)) - }); - describe('endpointUrl', function () { - expect(endpointUrl(null, STAGING)).to.deep.equal(EMOTEEV_BASE_URL_STAGING.concat(ENDPOINT_PATH)) - }); - describe('endpointUrl', function () { - expect(endpointUrl(STAGING, null)).to.deep.equal(EMOTEEV_BASE_URL_STAGING.concat(ENDPOINT_PATH)) - }); - - describe('userSyncIframeUrl', function () { - expect(userSyncIframeUrl(null, null)).to.deep.equal(EMOTEEV_BASE_URL.concat(USER_SYNC_IFRAME_URL_PATH)) - }); - describe('userSyncIframeUrl', function () { - expect(userSyncIframeUrl(null, STAGING)).to.deep.equal(EMOTEEV_BASE_URL_STAGING.concat(USER_SYNC_IFRAME_URL_PATH)) - }); - describe('userSyncIframeUrl', function () { - expect(userSyncIframeUrl(STAGING, null)).to.deep.equal(EMOTEEV_BASE_URL_STAGING.concat(USER_SYNC_IFRAME_URL_PATH)) - }); - - describe('userSyncImageUrl', function () { - expect(userSyncImageUrl(null, null)).to.deep.equal(EMOTEEV_BASE_URL.concat(USER_SYNC_IMAGE_URL_PATH)) - }); - describe('userSyncImageUrl', function () { - expect(userSyncImageUrl(null, STAGING)).to.deep.equal(EMOTEEV_BASE_URL_STAGING.concat(USER_SYNC_IMAGE_URL_PATH)) - }); - describe('userSyncImageUrl', function () { - expect(userSyncImageUrl(STAGING, null)).to.deep.equal(EMOTEEV_BASE_URL_STAGING.concat(USER_SYNC_IMAGE_URL_PATH)) - }); - describe('isBidRequestValid', function () { - it('should return true when required params found', function () { + it('should return true when valid', function () { const validBid = { bidder: 'emoteev', + bidId: '23a45b4e3', params: { adSpaceId: 12345, }, @@ -177,11 +109,14 @@ describe('emoteevBidAdapter', function () { } }, }; - expect(spec.isBidRequestValid(validBid)).to.equal(true); + expect(isBidRequestValid(validBid)).to.equal(true); + + expect(spec.isBidRequestValid(validBid)).to.exist.and.to.be.a('boolean'); + expect(spec.isBidRequestValid({})).to.exist.and.to.be.a('boolean'); }); it('should return false when required params are invalid', function () { - expect(spec.isBidRequestValid({ + expect(isBidRequestValid({ bidder: '', // invalid bidder params: { adSpaceId: 12345, @@ -192,7 +127,7 @@ describe('emoteevBidAdapter', function () { } }, })).to.equal(false); - expect(spec.isBidRequestValid({ + expect(isBidRequestValid({ bidder: 'emoteev', params: { adSpaceId: '', // invalid adSpaceId @@ -203,7 +138,7 @@ describe('emoteevBidAdapter', function () { } }, })).to.equal(false); - expect(spec.isBidRequestValid({ + expect(isBidRequestValid({ bidder: 'emoteev', params: { adSpaceId: 12345, @@ -219,131 +154,602 @@ describe('emoteevBidAdapter', function () { describe('buildRequests', function () { const + env = DEFAULT_ENV, + debug = true, currency = 'EUR', - emoteevEnv = STAGING, - emoteevDebug = true, - emoteevOverrides = { - iAmOverride: 'iAmOverride' - }; - config.setConfig({ // asynchronous - currency, - emoteev: { - env: STAGING, - debug: emoteevDebug, - overrides: emoteevOverrides - } - }); + request = buildRequests(env, debug, currency, cannedValidBidRequests, cannedBidderRequest); - config.getConfig('emoteev', function () { - const request = spec.buildRequests(cannedValidBidRequests, cannedBidderRequest); - - it('creates a request object with correct method, url and data', function () { - expect(request).to.exist.and.have.all.keys( - 'method', - 'url', - 'data', - ); - expect(request.method).to.equal('POST'); - expect(request.url).to.equal(endpointUrl(emoteevEnv, emoteevEnv)); - - let requestData = JSON.parse(request.data); - expect(requestData).to.exist.and.have.all.keys( - 'akPbjsVersion', - 'bidRequests', - 'currency', - 'debug', - 'iAmOverride', - 'language', - 'refererInfo', - 'deviceInfo', - 'userAgent', - ); - - expect(requestData.bidRequests[0]).to.exist.and.have.all.keys( - 'params', - 'crumbs', - 'sizes', - 'bidId', - 'bidderRequestId', - ); - - expect(requestData.akPbjsVersion).to.deep.equal(AK_PBJS_VERSION); - expect(requestData.bidRequests[0].params).to.deep.equal(cannedValidBidRequests[0].params); - expect(requestData.bidRequests[0].crumbs).to.deep.equal(cannedValidBidRequests[0].crumbs); - expect(requestData.bidRequests[0].mediaTypes).to.deep.equal(cannedValidBidRequests[0].mediaTypes); - expect(requestData.bidRequests[0].bidId).to.deep.equal(cannedValidBidRequests[0].bidId); - expect(requestData.bidRequests[0].bidderRequestId).to.deep.equal(cannedValidBidRequests[0].bidderRequestId); - expect(requestData.currency).to.deep.equal(currency); - expect(requestData.debug).to.deep.equal(emoteevDebug); - expect(requestData.iAmOverride).to.deep.equal('iAmOverride'); - expect(requestData.language).to.deep.equal(navigator.language); - expect(requestData.deviceInfo).to.exist.and.have.all.keys( - 'browserWidth', - 'browserHeight', - 'deviceWidth', - 'deviceHeight', - 'documentWidth', - 'documentHeight', - 'webGL', - ); - expect(requestData.userAgent).to.deep.equal(navigator.userAgent); - }); - }); + expect(request).to.exist.and.have.all.keys( + 'method', + 'url', + 'data', + ); + + expect(request.method).to.equal('POST'); + expect(request.url).to.equal(bidderUrl(env)); + + expect(spec.buildRequests(cannedValidBidRequests, cannedBidderRequest)).to.exist.and.to.be.an('object'); }); describe('interpretResponse', function () { it('bid objects from response', function () { - const bidResponses = spec.interpretResponse(serverResponse); - expect(bidResponses).to.be.an('array').that.is.not.empty; // yes, syntax is correct - expect(bidResponses[0]).to.have.all.keys( - 'requestId', - 'cpm', - 'width', - 'height', - 'ad', - 'ttl', - 'creativeId', - 'netRevenue', - 'currency', - ); - - expect(bidResponses[0].requestId).to.equal(cannedValidBidRequests[0].bidId); - expect(bidResponses[0].cpm).to.equal(serverResponse.body[0].cpm); - expect(bidResponses[0].width).to.equal(serverResponse.body[0].width); - expect(bidResponses[0].height).to.equal(serverResponse.body[0].height); - expect(bidResponses[0].ad).to.equal(serverResponse.body[0].ad); - expect(bidResponses[0].ttl).to.equal(serverResponse.body[0].ttl); - expect(bidResponses[0].creativeId).to.equal(serverResponse.body[0].creativeId); - expect(bidResponses[0].netRevenue).to.equal(serverResponse.body[0].netRevenue); - expect(bidResponses[0].currency).to.equal(serverResponse.body[0].currency); + const bidResponses = interpretResponse(serverResponse); + expect(bidResponses).to.be.an('array').that.is.not.empty; + expect(bidResponses[0]).to.have.property('requestId', cannedValidBidRequests[0].bidId); + expect(bidResponses[0]).to.have.property('cpm', serverResponse.body[0].cpm); + expect(bidResponses[0]).to.have.property('width', serverResponse.body[0].width); + expect(bidResponses[0]).to.have.property('height', serverResponse.body[0].height); + expect(bidResponses[0]).to.have.property('ad', serverResponse.body[0].ad); + expect(bidResponses[0]).to.have.property('ttl', serverResponse.body[0].ttl); + expect(bidResponses[0]).to.have.property('creativeId', serverResponse.body[0].creativeId); + expect(bidResponses[0]).to.have.property('netRevenue', serverResponse.body[0].netRevenue); + expect(bidResponses[0]).to.have.property('currency', serverResponse.body[0].currency); }); }); - describe('getUserSyncs', function () { - config.setConfig({emoteevEnv: PRODUCTION}); - expect(spec.getUserSyncs({ - iframeEnabled: true - }, [{}])).to.deep.equal([{ - type: 'iframe', - url: EMOTEEV_BASE_URL.concat(USER_SYNC_IFRAME_URL_PATH) - }]); + describe('onAdapterCalled', function () { + const + bidRequest = cannedValidBidRequests[0], + url = onAdapterCalled(DEFAULT_ENV, bidRequest); + + expect(url).to.have.property('protocol'); + expect(url).to.have.property('hostname'); + expect(url).to.have.property('pathname', EVENTS_PATH); + expect(url).to.have.nested.property('search.eventName', ON_ADAPTER_CALLED); + expect(url).to.have.nested.property('search.pubcId', bidRequest.crumbs.pubcid); + expect(url).to.have.nested.property('search.bidId', bidRequest.bidId); + expect(url).to.have.nested.property('search.adSpaceId', bidRequest.params.adSpaceId); + expect(url).to.have.nested.property('search.cache_buster'); + }); + + describe('onBidWon', function () { + const + pubcId = cannedValidBidRequests[0].crumbs.pubcid, + bidObject = serverResponse.body[0], + url = onBidWon(DEFAULT_ENV, pubcId, bidObject); + + expect(url).to.have.property('protocol'); + expect(url).to.have.property('hostname'); + expect(url).to.have.property('pathname', EVENTS_PATH); + expect(url).to.have.nested.property('search.eventName', ON_BID_WON); + expect(url).to.have.nested.property('search.pubcId', pubcId); + expect(url).to.have.nested.property('search.bidId', bidObject.requestId); + expect(url).to.have.nested.property('search.cache_buster'); + }); + + describe('onTimeout', function () { + const + data = { + ...cannedValidBidRequests[0], + timeout: 123, + }, + url = onTimeout(DEFAULT_ENV, data); - expect(spec.getUserSyncs({ - pixelEnabled: true - }, [{}])).to.deep.equal([{ + expect(url).to.have.property('protocol'); + expect(url).to.have.property('hostname'); + expect(url).to.have.property('pathname', EVENTS_PATH); + expect(url).to.have.nested.property('search.eventName', ON_BIDDER_TIMEOUT); + expect(url).to.have.nested.property('search.bidId', data.bidId); + expect(url).to.have.nested.property('search.pubcId', data.crumbs.pubcid); + expect(url).to.have.nested.property('search.adSpaceId', data.params.adSpaceId); + expect(url).to.have.nested.property('search.timeout', data.timeout); + expect(url).to.have.nested.property('search.cache_buster'); + }); + + describe('getUserSyncs', function () { + expect(getUserSyncs( + DEFAULT_ENV, + { + iframeEnabled: false, + pixelEnabled: false + })).to.deep.equal([]); + expect(getUserSyncs( + PRODUCTION, + { + iframeEnabled: false, + pixelEnabled: true + })).to.deep.equal([{ type: 'image', - url: EMOTEEV_BASE_URL.concat(USER_SYNC_IMAGE_URL_PATH) + url: userSyncImageUrl(PRODUCTION) }]); - - expect(spec.getUserSyncs({ - iframeEnabled: true, - pixelEnabled: true - }, [{}])).to.deep.equal([{ + expect(getUserSyncs( + STAGING, + { + iframeEnabled: true, + pixelEnabled: false + })).to.deep.equal([{ type: 'iframe', - url: EMOTEEV_BASE_URL.concat(USER_SYNC_IFRAME_URL_PATH) - }, { + url: userSyncIframeUrl(STAGING) + }]); + expect(getUserSyncs( + DEVELOPMENT, + { + iframeEnabled: true, + pixelEnabled: true + })).to.deep.equal([{ type: 'image', - url: EMOTEEV_BASE_URL.concat(USER_SYNC_IMAGE_URL_PATH) + url: userSyncImageUrl(DEVELOPMENT) + }, { + type: 'iframe', + url: userSyncIframeUrl(DEVELOPMENT) }]); }); + + describe('domain', function () { + expect(domain(null)).to.deep.equal(DOMAIN); + expect(domain('anything')).to.deep.equal(DOMAIN); + expect(domain(PRODUCTION)).to.deep.equal(DOMAIN); + expect(domain(STAGING)).to.deep.equal(DOMAIN_STAGING); + expect(domain(DEVELOPMENT)).to.deep.equal(DOMAIN_DEVELOPMENT); + }); + + describe('eventsUrl', function () { + expect(eventsUrl(null)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: EVENTS_PATH + })); + expect(eventsUrl('anything')).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: EVENTS_PATH + })); + expect(eventsUrl(PRODUCTION)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(PRODUCTION), + pathname: EVENTS_PATH + })); + expect(eventsUrl(STAGING)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(STAGING), + pathname: EVENTS_PATH + })); + expect(eventsUrl(DEVELOPMENT)).to.deep.equal(url.format({ + hostname: domain(DEVELOPMENT), + pathname: EVENTS_PATH + })); + }); + + describe('bidderUrl', function () { + expect(bidderUrl(null)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: BIDDER_PATH + })); + expect(bidderUrl('anything')).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: BIDDER_PATH + })); + expect(bidderUrl(PRODUCTION)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(PRODUCTION), + pathname: BIDDER_PATH + })); + expect(bidderUrl(STAGING)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(STAGING), + pathname: BIDDER_PATH + })); + expect(bidderUrl(DEVELOPMENT)).to.deep.equal(url.format({ + hostname: domain(DEVELOPMENT), + pathname: BIDDER_PATH + })); + }); + + describe('userSyncIframeUrl', function () { + expect(userSyncIframeUrl(null)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: USER_SYNC_IFRAME_PATH + })); + expect(userSyncIframeUrl('anything')).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: USER_SYNC_IFRAME_PATH + })); + expect(userSyncIframeUrl(PRODUCTION)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(PRODUCTION), + pathname: USER_SYNC_IFRAME_PATH + })); + expect(userSyncIframeUrl(STAGING)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(STAGING), + pathname: USER_SYNC_IFRAME_PATH + })); + expect(userSyncIframeUrl(DEVELOPMENT)).to.deep.equal(url.format({ + hostname: domain(DEVELOPMENT), + pathname: USER_SYNC_IFRAME_PATH + })); + }); + + describe('userSyncImageUrl', function () { + expect(userSyncImageUrl(null)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: USER_SYNC_IMAGE_PATH + })); + expect(userSyncImageUrl('anything')).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(DEFAULT_ENV), + pathname: USER_SYNC_IMAGE_PATH + })); + expect(userSyncImageUrl(PRODUCTION)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(PRODUCTION), + pathname: USER_SYNC_IMAGE_PATH + })); + expect(userSyncImageUrl(STAGING)).to.deep.equal(url.format({ + protocol: 'https', + hostname: domain(STAGING), + pathname: USER_SYNC_IMAGE_PATH + })); + expect(userSyncImageUrl(DEVELOPMENT)).to.deep.equal(url.format({ + hostname: domain(DEVELOPMENT), + pathname: USER_SYNC_IMAGE_PATH + })); + }); + + describe('conformBidRequest', function () { + expect(conformBidRequest(cannedValidBidRequests[0])).to.deep.equal({ + params: cannedValidBidRequests[0].params, + crumbs: cannedValidBidRequests[0].crumbs, + sizes: cannedValidBidRequests[0].sizes, + bidId: cannedValidBidRequests[0].bidId, + bidderRequestId: cannedValidBidRequests[0].bidderRequestId, + }); + }); + + describe('requestsPayload', function () { + const + currency = 'EUR', + debug = true; + + const payload = requestsPayload(debug, currency, cannedValidBidRequests, cannedBidderRequest); + + expect(payload).to.exist.and.have.all.keys( + 'akPbjsVersion', + 'bidRequests', + 'currency', + 'debug', + 'language', + 'refererInfo', + 'deviceInfo', + 'userAgent', + 'gdprApplies', + 'gdprConsent' + ); + + expect(payload.bidRequests[0]).to.exist.and.have.all.keys( + 'params', + 'crumbs', + 'sizes', + 'bidId', + 'bidderRequestId', + ); + + expect(payload.akPbjsVersion).to.deep.equal(ADAPTER_VERSION); + expect(payload.bidRequests[0].params).to.deep.equal(cannedValidBidRequests[0].params); + expect(payload.bidRequests[0].crumbs).to.deep.equal(cannedValidBidRequests[0].crumbs); + expect(payload.bidRequests[0].mediaTypes).to.deep.equal(cannedValidBidRequests[0].mediaTypes); + expect(payload.bidRequests[0].bidId).to.deep.equal(cannedValidBidRequests[0].bidId); + expect(payload.bidRequests[0].bidderRequestId).to.deep.equal(cannedValidBidRequests[0].bidderRequestId); + expect(payload.currency).to.deep.equal(currency); + expect(payload.debug).to.deep.equal(debug); + expect(payload.language).to.deep.equal(navigator.language); + expect(payload.deviceInfo).to.exist.and.have.all.keys( + 'browserWidth', + 'browserHeight', + 'deviceWidth', + 'deviceHeight', + 'documentWidth', + 'documentHeight', + 'webGL', + ); + expect(payload.userAgent).to.deep.equal(navigator.userAgent); + expect(payload.gdprApplies).to.deep.equal(cannedBidderRequest.gdprConsent.gdprApplies); + expect(payload.gdprConsent).to.deep.equal(cannedBidderRequest.gdprConsent.consentString); + }); + + describe('getViewDimensions', function () { + const window = { + innerWidth: 1024, + innerHeight: 768 + }; + const documentWithElement = { + documentElement: + { + clientWidth: 512, + clientHeight: 384 + } + }; + const documentWithBody = { + body: + { + clientWidth: 512, + clientHeight: 384 + } + }; + expect(getViewDimensions(window, documentWithElement)).to.deep.equal({ + width: 1024, + height: 768 + }); + expect(getViewDimensions(window, documentWithBody)).to.deep.equal({width: 1024, height: 768}); + expect(getViewDimensions(window, documentWithElement)).to.deep.equal({ + width: 1024, + height: 768 + }); + expect(getViewDimensions(window, documentWithBody)).to.deep.equal({width: 1024, height: 768}); + expect(getViewDimensions({}, documentWithElement)).to.deep.equal({width: 512, height: 384}); + expect(getViewDimensions({}, documentWithBody)).to.deep.equal({width: 512, height: 384}); + }); + + describe('getDeviceDimensions', function () { + const window = {screen: {width: 1024, height: 768}}; + expect(getDeviceDimensions(window)).to.deep.equal({width: 1024, height: 768}); + expect(getDeviceDimensions({})).to.deep.equal({width: '', height: ''}); + }); + + describe('getDocumentDimensions', function () { + expect(getDocumentDimensions({ + documentElement: { + clientWidth: 1, + clientHeight: 1, + offsetWidth: 0, + offsetHeight: 0, + scrollWidth: 0, + scrollHeight: 0, + }, + })).to.deep.equal({width: 1, height: 1}); + + expect(getDocumentDimensions({ + documentElement: { + clientWidth: 1, + clientHeight: 1, + offsetWidth: 0, + offsetHeight: 0, + scrollWidth: 0, + scrollHeight: 0, + }, + body: { + scrollHeight: 0, + offsetHeight: 0, + } + })).to.deep.equal({width: 1, height: 1}); + + expect(getDocumentDimensions({ + documentElement: { + clientWidth: 0, + clientHeight: 0, + offsetWidth: 1, + offsetHeight: 1, + scrollWidth: 0, + scrollHeight: 0, + }, + body: { + scrollHeight: 0, + offsetHeight: 0, + } + })).to.deep.equal({width: 1, height: 1}); + + expect(getDocumentDimensions({ + documentElement: { + clientWidth: 0, + clientHeight: 0, + offsetWidth: 0, + offsetHeight: 0, + scrollWidth: 1, + scrollHeight: 1, + }, + body: { + scrollHeight: 0, + offsetHeight: 0, + } + })).to.deep.equal({width: 1, height: 1}); + + expect(getDocumentDimensions({ + documentElement: { + clientWidth: undefined, + clientHeight: undefined, + offsetWidth: undefined, + offsetHeight: undefined, + scrollWidth: undefined, + scrollHeight: undefined, + }, + body: { + scrollHeight: undefined, + offsetHeight: undefined, + } + })).to.deep.equal({width: '', height: ''}); + }); + + // describe('isWebGLEnabled', function () { + // it('handles no webgl', function () { + // const + // document = new Document(), + // canvas = sinon.createStubInstance(HTMLCanvasElement); + // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); + // canvas.getContext.withArgs('webgl').returns(undefined); + // canvas.getContext.withArgs('experimental-webgl').returns(undefined); + // expect(isWebGLEnabled(document)).to.equal(false); + // }); + // + // it('handles webgl exception', function () { + // const + // document = new Document(), + // canvas = sinon.createStubInstance(HTMLCanvasElement); + // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); + // canvas.getContext.withArgs('webgl').throws(DOMException); + // expect(isWebGLEnabled(document)).to.equal(false); + // }); + // + // it('handles experimental webgl', function () { + // const + // document = new Document(), + // canvas = sinon.createStubInstance(HTMLCanvasElement); + // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); + // canvas.getContext.withArgs('webgl').returns(undefined); + // canvas.getContext.withArgs('experimental-webgl').returns(true); + // expect(isWebGLEnabled(document)).to.equal(true); + // }); + // + // it('handles experimental webgl exception', function () { + // const + // document = new Document(), + // canvas = sinon.createStubInstance(HTMLCanvasElement); + // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); + // canvas.getContext.withArgs('webgl').returns(undefined); + // canvas.getContext.withArgs('experimental-webgl').throws(DOMException); + // expect(isWebGLEnabled(document)).to.equal(false); + // }); + // + // it('handles webgl', function () { + // const + // document = new Document(), + // canvas = sinon.createStubInstance(HTMLCanvasElement); + // sinon.stub(document, 'createElement').withArgs('canvas').returns(canvas); + // canvas.getContext.withArgs('webgl').returns(true); + // expect(isWebGLEnabled(document)).to.equal(true); + // }); + // }); + + describe('getDeviceInfo', function () { + expect(getDeviceInfo( + {width: 1, height: 2}, + {width: 3, height: 4}, + {width: 5, height: 6}, + true + )).to.deep.equal({ + deviceWidth: 1, + deviceHeight: 2, + browserWidth: 3, + browserHeight: 4, + documentWidth: 5, + documentHeight: 6, + webGL: true + }); + }); + + describe('resolveEnv', function () { + it('defaults to production', function () { + expect(resolveEnv({}, null)).to.deep.equal(DEFAULT_ENV); + }); + expect(resolveEnv({}, PRODUCTION)).to.deep.equal(PRODUCTION); + expect(resolveEnv({}, STAGING)).to.deep.equal(STAGING); + expect(resolveEnv({}, DEVELOPMENT)).to.deep.equal(DEVELOPMENT); + expect(resolveEnv({emoteev: {env: PRODUCTION}}, null)).to.deep.equal(PRODUCTION); + expect(resolveEnv({emoteev: {env: STAGING}}, null)).to.deep.equal(STAGING); + expect(resolveEnv({emoteev: {env: DEVELOPMENT}}, null)).to.deep.equal(DEVELOPMENT); + it('prioritizes parameter over configuration', function () { + expect(resolveEnv({emoteev: {env: STAGING}}, DEVELOPMENT)).to.deep.equal(DEVELOPMENT); + }); + }); + + describe('resolveDebug', function () { + it('defaults to production', function () { + expect(resolveDebug({}, null)).to.deep.equal(false); + }); + expect(resolveDebug({}, 'false')).to.deep.equal(false); + expect(resolveDebug({}, 'true')).to.deep.equal(true); + expect(resolveDebug({debug: true}, null)).to.deep.equal(true); + it('prioritizes parameter over configuration', function () { + expect(resolveDebug({debug: true}, 'false')).to.deep.equal(false); + }); + }); + + describe('side effects', function () { + let triggerPixelSpy; + let getCookieSpy; + let getConfigSpy; + let getParameterByNameSpy; + beforeEach(function () { + triggerPixelSpy = sinon.spy(utils, 'triggerPixel'); + getCookieSpy = sinon.spy(pubCommonId, 'getCookie'); + getConfigSpy = sinon.spy(config, 'getConfig'); + getParameterByNameSpy = sinon.spy(utils, 'getParameterByName'); + }); + afterEach(function () { + triggerPixelSpy.restore(); + getCookieSpy.restore(); + getConfigSpy.restore(); + getParameterByNameSpy.restore(); + }); + + describe('isBidRequestValid', function () { + it('has intended side-effects', function () { + const validBidRequest = { + bidder: 'emoteev', + bidId: '23a45b4e3', + params: { + adSpaceId: 12345, + }, + mediaTypes: { + banner: { + sizes: [[750, 200]] + } + }, + }; + spec.isBidRequestValid(validBidRequest); + sinon.assert.notCalled(utils.triggerPixel); + sinon.assert.notCalled(pubCommonId.getCookie); + sinon.assert.notCalled(config.getConfig); + sinon.assert.notCalled(utils.getParameterByName); + }); + it('has intended side-effects', function () { + const invalidBidRequest = {}; + spec.isBidRequestValid(invalidBidRequest); + sinon.assert.notCalled(utils.triggerPixel); + sinon.assert.notCalled(pubCommonId.getCookie); + sinon.assert.notCalled(config.getConfig); + sinon.assert.notCalled(utils.getParameterByName); + }); + }); + describe('buildRequests', function () { + it('has intended side-effects', function () { + spec.buildRequests(cannedValidBidRequests, cannedBidderRequest); + sinon.assert.notCalled(utils.triggerPixel); + sinon.assert.notCalled(pubCommonId.getCookie); + sinon.assert.callCount(config.getConfig, 3); + sinon.assert.callCount(utils.getParameterByName, 2); + }); + }); + describe('interpretResponse', function () { + it('has intended side-effects', function () { + spec.interpretResponse(serverResponse); + sinon.assert.notCalled(utils.triggerPixel); + sinon.assert.notCalled(pubCommonId.getCookie); + sinon.assert.notCalled(config.getConfig); + sinon.assert.notCalled(utils.getParameterByName); + }); + }); + describe('onBidWon', function () { + it('has intended side-effects', function () { + const bidObject = serverResponse.body[0]; + spec.onBidWon(bidObject); + sinon.assert.calledOnce(utils.triggerPixel); + sinon.assert.calledOnce(pubCommonId.getCookie); + sinon.assert.calledOnce(config.getConfig); + sinon.assert.calledOnce(utils.getParameterByName); + }); + }); + describe('onTimeout', function () { + it('has intended side-effects', function () { + spec.onTimeout(cannedValidBidRequests[0]); + sinon.assert.calledOnce(utils.triggerPixel); + sinon.assert.notCalled(pubCommonId.getCookie); + sinon.assert.calledOnce(config.getConfig); + sinon.assert.calledOnce(utils.getParameterByName); + }); + }); + describe('getUserSyncs', function () { + it('has intended side-effects', function () { + spec.getUserSyncs({}); + sinon.assert.notCalled(utils.triggerPixel); + sinon.assert.notCalled(pubCommonId.getCookie); + sinon.assert.calledOnce(config.getConfig); + sinon.assert.calledOnce(utils.getParameterByName); + }); + }); + }); }); From c14f915e9529445effb1ba38cc7400c617e1872c Mon Sep 17 00:00:00 2001 From: Hendrik Iseke <39734979+hiseke@users.noreply.github.com> Date: Tue, 23 Apr 2019 20:56:00 +0200 Subject: [PATCH 020/146] fix handling of gdpr object (#3756) * initial orbidder version in personal github repo * use adUnits from orbidder_example.html * replace obsolete functions * forgot to commit the test * check if bidderRequest object is available * try to fix weird safari/ie issue * ebayK: add more params * update orbidderBidAdapter.md * use spec. instead of this. for consistency reasons * add bidfloor parameter to params object * fix gdpr object handling --- modules/orbidderBidAdapter.js | 8 +-- test/spec/modules/orbidderBidAdapter_spec.js | 57 ++++++++++---------- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/modules/orbidderBidAdapter.js b/modules/orbidderBidAdapter.js index e316f3ef212..fc5eecbab08 100644 --- a/modules/orbidderBidAdapter.js +++ b/modules/orbidderBidAdapter.js @@ -42,11 +42,11 @@ export const spec = { } }; spec.bidParams[bidRequest.bidId] = bidRequest.params; - if (bidRequest && bidRequest.gdprConsent) { + if (bidderRequest && bidderRequest.gdprConsent) { ret.data.gdprConsent = { - consentString: bidRequest.gdprConsent.consentString, - consentRequired: (typeof bidRequest.gdprConsent.gdprApplies === 'boolean') - ? bidRequest.gdprConsent.gdprApplies + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') + ? bidderRequest.gdprConsent.gdprApplies : true }; } diff --git a/test/spec/modules/orbidderBidAdapter_spec.js b/test/spec/modules/orbidderBidAdapter_spec.js index 0761ed8d31e..bc88090095b 100644 --- a/test/spec/modules/orbidderBidAdapter_spec.js +++ b/test/spec/modules/orbidderBidAdapter_spec.js @@ -20,14 +20,17 @@ describe('orbidderBidAdapter', () => { return JSON.parse(JSON.stringify(val)); }; - const buildRequest = function (buildRequest) { - return spec.buildRequests( - [buildRequest], - { - refererInfo: { - referer: 'http://localhost:9876/' - } - })[0]; + const buildRequest = (buildRequest, bidderRequest) => { + if (!Array.isArray(buildRequest)) { + buildRequest = [buildRequest]; + } + + return spec.buildRequests(buildRequest, { + ...bidderRequest || {}, + refererInfo: { + referer: 'http://localhost:9876/' + } + })[0]; }; describe('inherited functions', () => { @@ -101,30 +104,28 @@ describe('orbidderBidAdapter', () => { }); it('handles empty gdpr object', () => { - const bidRequest = deepClone(defaultBidRequest); - bidRequest.gdprConsent = {}; - - const request = buildRequest(bidRequest); + const request = buildRequest(defaultBidRequest, { + gdprConsent: {} + }); expect(request.data.gdprConsent.consentRequired).to.be.equal(true); }); it('handles non-existent gdpr object', () => { - const bidRequest = deepClone(defaultBidRequest); - bidRequest.gdprConsent = null; - - const request = buildRequest(bidRequest); + const request = buildRequest(defaultBidRequest, { + gdprConsent: null + }); expect(request.data.gdprConsent).to.be.undefined; }); it('handles properly filled gdpr object where gdpr applies', () => { const consentString = 'someWeirdString'; - const bidRequest = deepClone(defaultBidRequest); - bidRequest.gdprConsent = { - gdprApplies: true, - consentString: 'someWeirdString' - }; + const request = buildRequest(defaultBidRequest, { + gdprConsent: { + gdprApplies: true, + consentString: consentString + } + }); - const request = buildRequest(bidRequest); const gdprConsent = request.data.gdprConsent; expect(gdprConsent.consentRequired).to.be.equal(true); expect(gdprConsent.consentString).to.be.equal(consentString); @@ -132,13 +133,13 @@ describe('orbidderBidAdapter', () => { it('handles properly filled gdpr object where gdpr does not apply', () => { const consentString = 'someWeirdString'; - const bidRequest = deepClone(defaultBidRequest); - bidRequest.gdprConsent = { - gdprApplies: false, - consentString: 'someWeirdString' - }; + const request = buildRequest(defaultBidRequest, { + gdprConsent: { + gdprApplies: false, + consentString: consentString + } + }); - const request = buildRequest(bidRequest); const gdprConsent = request.data.gdprConsent; expect(gdprConsent.consentRequired).to.be.equal(false); expect(gdprConsent.consentString).to.be.equal(consentString); From b491a05ed49c9fa2c7febe161cc9d336223f16fe Mon Sep 17 00:00:00 2001 From: Vladislav Yatsun Date: Tue, 23 Apr 2019 22:58:45 +0400 Subject: [PATCH 021/146] Add NAF Digital Bidder Adapter (#3750) --- modules/nafdigitalBidAdapter.js | 246 +++++++++++++++ modules/nafdigitalBidAdapter.md | 38 +++ .../spec/modules/nafdigitalBidAdapter_spec.js | 283 ++++++++++++++++++ 3 files changed, 567 insertions(+) create mode 100644 modules/nafdigitalBidAdapter.js create mode 100644 modules/nafdigitalBidAdapter.md create mode 100644 test/spec/modules/nafdigitalBidAdapter_spec.js diff --git a/modules/nafdigitalBidAdapter.js b/modules/nafdigitalBidAdapter.js new file mode 100644 index 00000000000..7bbfd8b38dd --- /dev/null +++ b/modules/nafdigitalBidAdapter.js @@ -0,0 +1,246 @@ +import * as utils from '../src/utils'; +import * as url from '../src/url'; +import { registerBidder } from '../src/adapters/bidderFactory'; +import { BANNER } from '../src/mediaTypes'; +import { config } from '../src/config'; + +const BIDDER_CODE = 'nafdigital'; +const URL = 'https://nafdigitalbidder.com/hb'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + isBidRequestValid, + buildRequests, + interpretResponse, + getUserSyncs +}; + +function buildRequests(bidReqs, bidderRequest) { + try { + let referrer = ''; + if (bidderRequest && bidderRequest.refererInfo) { + referrer = bidderRequest.refererInfo.referer; + } + const nafdigitalImps = []; + const publisherId = utils.getBidIdParameter('publisherId', bidReqs[0].params); + utils._each(bidReqs, function (bid) { + bid.sizes = ((utils.isArray(bid.sizes) && utils.isArray(bid.sizes[0])) ? bid.sizes : [bid.sizes]); + bid.sizes = bid.sizes.filter(size => utils.isArray(size)); + const processedSizes = bid.sizes.map(size => ({w: parseInt(size[0], 10), h: parseInt(size[1], 10)})); + + const element = document.getElementById(bid.adUnitCode); + const minSize = _getMinSize(processedSizes); + const viewabilityAmount = _isViewabilityMeasurable(element) + ? _getViewability(element, utils.getWindowTop(), minSize) + : 'na'; + const viewabilityAmountRounded = isNaN(viewabilityAmount) ? viewabilityAmount : Math.round(viewabilityAmount); + + const imp = { + id: bid.bidId, + banner: { + format: processedSizes, + ext: { + viewability: viewabilityAmountRounded + } + }, + tagid: String(bid.adUnitCode) + }; + const bidFloor = utils.getBidIdParameter('bidFloor', bid.params); + if (bidFloor) { + imp.bidfloor = bidFloor; + } + nafdigitalImps.push(imp); + }); + const nafdigitalBidReq = { + id: utils.getUniqueIdentifierStr(), + imp: nafdigitalImps, + site: { + domain: url.parse(referrer).host, + page: referrer, + publisher: { + id: publisherId + } + }, + device: { + devicetype: _getDeviceType(), + w: screen.width, + h: screen.height + }, + tmax: config.getConfig('bidderTimeout') + }; + + return { + method: 'POST', + url: URL, + data: JSON.stringify(nafdigitalBidReq), + options: {contentType: 'text/plain', withCredentials: false} + }; + } catch (e) { + utils.logError(e, {bidReqs, bidderRequest}); + } +} + +function isBidRequestValid(bid) { + if (bid.bidder !== BIDDER_CODE || typeof bid.params === 'undefined') { + return false; + } + + if (typeof bid.params.publisherId === 'undefined') { + return false; + } + + return true; +} + +function interpretResponse(serverResponse) { + if (!serverResponse.body || typeof serverResponse.body != 'object') { + utils.logWarn('NAF digital server returned empty/non-json response: ' + JSON.stringify(serverResponse.body)); + return []; + } + const { body: {id, seatbid} } = serverResponse; + try { + const nafdigitalBidResponses = []; + if (id && + seatbid && + seatbid.length > 0 && + seatbid[0].bid && + seatbid[0].bid.length > 0) { + seatbid[0].bid.map(nafdigitalBid => { + nafdigitalBidResponses.push({ + requestId: nafdigitalBid.impid, + cpm: parseFloat(nafdigitalBid.price), + width: parseInt(nafdigitalBid.w), + height: parseInt(nafdigitalBid.h), + creativeId: nafdigitalBid.crid || nafdigitalBid.id, + currency: 'USD', + netRevenue: true, + mediaType: BANNER, + ad: _getAdMarkup(nafdigitalBid), + ttl: 60 + }); + }); + } + return nafdigitalBidResponses; + } catch (e) { + utils.logError(e, {id, seatbid}); + } +} + +// Don't do user sync for now +function getUserSyncs(syncOptions, responses, gdprConsent) { + return []; +} + +function _isMobile() { + return (/(ios|ipod|ipad|iphone|android)/i).test(navigator.userAgent); +} + +function _isConnectedTV() { + return (/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(navigator.userAgent); +} + +function _getDeviceType() { + return _isMobile() ? 1 : _isConnectedTV() ? 3 : 2; +} + +function _getAdMarkup(bid) { + let adm = bid.adm; + if ('nurl' in bid) { + adm += utils.createTrackPixelHtml(bid.nurl); + } + return adm; +} + +function _isViewabilityMeasurable(element) { + return !_isIframe() && element !== null; +} + +function _getViewability(element, topWin, { w, h } = {}) { + return utils.getWindowTop().document.visibilityState === 'visible' + ? _getPercentInView(element, topWin, { w, h }) + : 0; +} + +function _isIframe() { + try { + return utils.getWindowSelf() !== utils.getWindowTop(); + } catch (e) { + return true; + } +} + +function _getMinSize(sizes) { + return sizes.reduce((min, size) => size.h * size.w < min.h * min.w ? size : min); +} + +function _getBoundingBox(element, { w, h } = {}) { + let { width, height, left, top, right, bottom } = element.getBoundingClientRect(); + + if ((width === 0 || height === 0) && w && h) { + width = w; + height = h; + right = left + w; + bottom = top + h; + } + + return { width, height, left, top, right, bottom }; +} + +function _getIntersectionOfRects(rects) { + const bbox = { + left: rects[0].left, + right: rects[0].right, + top: rects[0].top, + bottom: rects[0].bottom + }; + + for (let i = 1; i < rects.length; ++i) { + bbox.left = Math.max(bbox.left, rects[i].left); + bbox.right = Math.min(bbox.right, rects[i].right); + + if (bbox.left >= bbox.right) { + return null; + } + + bbox.top = Math.max(bbox.top, rects[i].top); + bbox.bottom = Math.min(bbox.bottom, rects[i].bottom); + + if (bbox.top >= bbox.bottom) { + return null; + } + } + + bbox.width = bbox.right - bbox.left; + bbox.height = bbox.bottom - bbox.top; + + return bbox; +} + +function _getPercentInView(element, topWin, { w, h } = {}) { + const elementBoundingBox = _getBoundingBox(element, { w, h }); + + // Obtain the intersection of the element and the viewport + const elementInViewBoundingBox = _getIntersectionOfRects([ { + left: 0, + top: 0, + right: topWin.innerWidth, + bottom: topWin.innerHeight + }, elementBoundingBox ]); + + let elementInViewArea, elementTotalArea; + + if (elementInViewBoundingBox !== null) { + // Some or all of the element is in view + elementInViewArea = elementInViewBoundingBox.width * elementInViewBoundingBox.height; + elementTotalArea = elementBoundingBox.width * elementBoundingBox.height; + + return ((elementInViewArea / elementTotalArea) * 100); + } + + // No overlap between element and the viewport; therefore, the element + // lies completely out of view + return 0; +} + +registerBidder(spec); diff --git a/modules/nafdigitalBidAdapter.md b/modules/nafdigitalBidAdapter.md new file mode 100644 index 00000000000..b17b1f13e1e --- /dev/null +++ b/modules/nafdigitalBidAdapter.md @@ -0,0 +1,38 @@ +# Overview + +``` +Module Name: NAF Digital Bid Adapter +Module Type: Bidder Adapter +Maintainer: vyatsun@gmail.com +``` + +# Description + +NAF Digital adapter integration to the Prebid library. + +# Test Parameters + +``` +var adUnits = [ + { + code: 'test-leaderboard', + sizes: [[728, 90]], + bids: [{ + bidder: 'nafdigital', + params: { + publisherId: 2141020, + bidFloor: 0.01 + } + }] + }, { + code: 'test-banner', + sizes: [[300, 250]], + bids: [{ + bidder: 'nafdigital', + params: { + publisherId: 2141020 + } + }] + } +] +``` diff --git a/test/spec/modules/nafdigitalBidAdapter_spec.js b/test/spec/modules/nafdigitalBidAdapter_spec.js new file mode 100644 index 00000000000..ca486c632c7 --- /dev/null +++ b/test/spec/modules/nafdigitalBidAdapter_spec.js @@ -0,0 +1,283 @@ +import { expect } from 'chai'; +import * as utils from 'src/utils'; +import { spec } from 'modules/nafdigitalBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +const URL = 'https://nafdigitalbidder.com/hb'; + +describe('nafdigitalBidAdapter', function() { + const adapter = newBidder(spec); + let element, win; + let bidRequests; + let sandbox; + + beforeEach(function() { + element = { + x: 0, + y: 0, + + width: 0, + height: 0, + + getBoundingClientRect: () => { + return { + width: element.width, + height: element.height, + + left: element.x, + top: element.y, + right: element.x + element.width, + bottom: element.y + element.height + }; + } + }; + win = { + document: { + visibilityState: 'visible' + }, + + innerWidth: 800, + innerHeight: 600 + }; + bidRequests = [{ + 'bidder': 'nafdigital', + 'params': { + 'publisherId': 1234567 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '5fb26ac22bde4', + 'bidderRequestId': '4bf93aeb730cb9', + 'auctionId': 'ffe9a1f7-7b67-4bda-a8e0-9ee5dc9f442e' + }]; + + sandbox = sinon.sandbox.create(); + sandbox.stub(document, 'getElementById').withArgs('adunit-code').returns(element); + sandbox.stub(utils, 'getWindowTop').returns(win); + sandbox.stub(utils, 'getWindowSelf').returns(win); + }); + + afterEach(function() { + sandbox.restore(); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'nafdigital', + 'params': { + 'publisherId': 1234567 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '5fb26ac22bde4', + 'bidderRequestId': '4bf93aeb730cb9', + 'auctionId': 'ffe9a1f7-7b67-4bda-a8e0-9ee5dc9f442e', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when tagid not passed correctly', function () { + bid.params.publisherId = undefined; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when require params are not passed', function () { + let bid = Object.assign({}, bid); + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + it('sends bid request to our endpoint via POST', function () { + const request = spec.buildRequests(bidRequests); + expect(request.method).to.equal('POST'); + }); + + it('request url should match our endpoint url', function () { + const request = spec.buildRequests(bidRequests); + expect(request.url).to.equal(URL); + }); + + it('sets the proper banner object', function() { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); + }); + + it('accepts a single array as a size', function() { + bidRequests[0].sizes = [300, 250]; + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}]); + }); + + it('sends bidfloor param if present', function () { + bidRequests[0].params.bidFloor = 0.05; + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].bidfloor).to.equal(0.05); + }); + + it('sends tagid', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].tagid).to.equal('adunit-code'); + }); + + it('sends publisher id', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.site.publisher.id).to.equal(1234567); + }); + + context('when element is fully in view', function() { + it('returns 100', function() { + Object.assign(element, { width: 600, height: 400 }); + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.ext.viewability).to.equal(100); + }); + }); + + context('when element is out of view', function() { + it('returns 0', function() { + Object.assign(element, { x: -300, y: 0, width: 207, height: 320 }); + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.ext.viewability).to.equal(0); + }); + }); + + context('when element is partially in view', function() { + it('returns percentage', function() { + Object.assign(element, { width: 800, height: 800 }); + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.ext.viewability).to.equal(75); + }); + }); + + context('when width or height of the element is zero', function() { + it('try to use alternative values', function() { + Object.assign(element, { width: 0, height: 0 }); + bidRequests[0].sizes = [[800, 2400]]; + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.ext.viewability).to.equal(25); + }); + }); + + context('when nested iframes', function() { + it('returns \'na\'', function() { + Object.assign(element, { width: 600, height: 400 }); + + utils.getWindowTop.restore(); + utils.getWindowSelf.restore(); + sandbox.stub(utils, 'getWindowTop').returns(win); + sandbox.stub(utils, 'getWindowSelf').returns({}); + + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.ext.viewability).to.equal('na'); + }); + }); + + context('when tab is inactive', function() { + it('returns 0', function() { + Object.assign(element, { width: 600, height: 400 }); + + utils.getWindowTop.restore(); + win.document.visibilityState = 'hidden'; + sandbox.stub(utils, 'getWindowTop').returns(win); + + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.ext.viewability).to.equal(0); + }); + }); + }); + + describe('interpretResponse', function () { + let response; + beforeEach(function () { + response = { + body: { + 'id': '37386aade21a71', + 'seatbid': [{ + 'bid': [{ + 'id': '376874781', + 'impid': '283a9f4cd2415d', + 'price': 0.35743275, + 'nurl': '', + 'adm': '', + 'w': 300, + 'h': 250 + }] + }] + } + }; + }); + + it('should get the correct bid response', function () { + let expectedResponse = [{ + 'requestId': '283a9f4cd2415d', + 'cpm': 0.35743275, + 'width': 300, + 'height': 250, + 'creativeId': '376874781', + 'currency': 'USD', + 'netRevenue': true, + 'mediaType': 'banner', + 'ad': `
`, + 'ttl': 60 + }]; + + let result = spec.interpretResponse(response); + expect(result[0]).to.deep.equal(expectedResponse[0]); + }); + + it('crid should default to the bid id if not on the response', function () { + let expectedResponse = [{ + 'requestId': '283a9f4cd2415d', + 'cpm': 0.35743275, + 'width': 300, + 'height': 250, + 'creativeId': response.body.seatbid[0].bid[0].id, + 'currency': 'USD', + 'netRevenue': true, + 'mediaType': 'banner', + 'ad': `
`, + 'ttl': 60 + }]; + + let result = spec.interpretResponse(response); + expect(result[0]).to.deep.equal(expectedResponse[0]); + }); + + it('handles empty bid response', function () { + let response = { + body: '' + }; + let result = spec.interpretResponse(response); + expect(result.length).to.equal(0); + }); + }); + + describe('getUserSyncs ', () => { + let syncOptions = {iframeEnabled: true, pixelEnabled: true}; + + it('should not return', () => { + let returnStatement = spec.getUserSyncs(syncOptions, []); + expect(returnStatement).to.be.empty; + }); + }); +}); From 49afe637a9a0a194955500252682481bd65f99fd Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Tue, 23 Apr 2019 15:04:47 -0400 Subject: [PATCH 022/146] Automated functional tests for longform testpages (#3659) * initial changes for longform e2e testing * added old safari browsers back, test spec file, and updated test pages * add retries settings to e2e tests, lower timeout thresholds * remove commented code * additional clean-up * update browser versions for more stable e2e tests * support host param in gulp command and other updates * refactor how host param was handled * add ie11 to browsers but remove it for e2e tests * update gulp task name to e2e-test * update e2e variable --- browsers.json | 40 +- gulpHelpers.js | 78 +- gulpfile.js | 77 +- .../basic_w_custom_adserver_translation.html | 132 ++ .../basic_w_requireExactDuration.html | 130 ++ .../basic_wo_brandCategoryExclusion.html | 130 ++ .../basic_wo_requireExactDuration.html | 131 ++ .../longform/custom_adserver_translation.json | 1180 +++++++++++++++++ .../longform/longformTestUtils.js | 66 + .../longform/longform_testpages_style.css | 34 + karma.conf.maker.js | 3 +- package.json | 8 +- test/helpers/testing-utils.js | 4 + test/spec/e2e/common/globals.js | 9 - test/spec/e2e/common/utils.js | 16 - test/spec/e2e/custom-assertions/first.js | 64 - test/spec/e2e/custom-reporter/junit.xml.ejs | 32 - .../e2e/custom-reporter/pbjs-html-reporter.js | 113 -- .../all_bidders_instant_load.html | 830 ------------ test/spec/e2e/gpt-examples/e2e_default.html | 117 -- test/spec/e2e/gpt-examples/gpt_default.html | 684 ---------- test/spec/e2e/gpt-examples/gpt_outstream.html | 250 ---- test/spec/e2e/gpt-examples/gpt_yieldbot.html | 241 ---- .../dom-group/allbidders_dom_spec.js | 125 -- test/spec/e2e/testcase1/dom-group/dom_spec.js | 51 - .../pbjsapi-group/adservertargeting_spec.js | 62 - .../pbjsapi-group/getbidresponses_spec.js | 34 - ...asic_w_custom_adserver_translation.spec.js | 68 + .../basic_w_requireExactDuration.spec.js | 68 + .../basic_wo_brandCategoryExclusion.spec.js | 63 + .../basic_wo_requireExactDuration.spec.js | 68 + wdio.conf.js | 55 + 32 files changed, 2197 insertions(+), 2766 deletions(-) create mode 100644 integrationExamples/longform/basic_w_custom_adserver_translation.html create mode 100644 integrationExamples/longform/basic_w_requireExactDuration.html create mode 100644 integrationExamples/longform/basic_wo_brandCategoryExclusion.html create mode 100644 integrationExamples/longform/basic_wo_requireExactDuration.html create mode 100644 integrationExamples/longform/custom_adserver_translation.json create mode 100644 integrationExamples/longform/longformTestUtils.js create mode 100644 integrationExamples/longform/longform_testpages_style.css create mode 100644 test/helpers/testing-utils.js delete mode 100644 test/spec/e2e/common/globals.js delete mode 100644 test/spec/e2e/common/utils.js delete mode 100644 test/spec/e2e/custom-assertions/first.js delete mode 100644 test/spec/e2e/custom-reporter/junit.xml.ejs delete mode 100644 test/spec/e2e/custom-reporter/pbjs-html-reporter.js delete mode 100644 test/spec/e2e/gpt-examples/all_bidders_instant_load.html delete mode 100644 test/spec/e2e/gpt-examples/e2e_default.html delete mode 100644 test/spec/e2e/gpt-examples/gpt_default.html delete mode 100644 test/spec/e2e/gpt-examples/gpt_outstream.html delete mode 100644 test/spec/e2e/gpt-examples/gpt_yieldbot.html delete mode 100644 test/spec/e2e/testcase1/dom-group/allbidders_dom_spec.js delete mode 100644 test/spec/e2e/testcase1/dom-group/dom_spec.js delete mode 100644 test/spec/e2e/testcase1/pbjsapi-group/adservertargeting_spec.js delete mode 100644 test/spec/e2e/testcase1/pbjsapi-group/getbidresponses_spec.js create mode 100644 test/spec/lfe2e/specs/basic_w_custom_adserver_translation.spec.js create mode 100644 test/spec/lfe2e/specs/basic_w_requireExactDuration.spec.js create mode 100644 test/spec/lfe2e/specs/basic_wo_brandCategoryExclusion.spec.js create mode 100644 test/spec/lfe2e/specs/basic_wo_requireExactDuration.spec.js create mode 100644 wdio.conf.js diff --git a/browsers.json b/browsers.json index 703bf44d41d..8604e44a7b8 100644 --- a/browsers.json +++ b/browsers.json @@ -1,9 +1,17 @@ { - "bs_ie_14_windows_10": { + "bs_edge_16_windows_10": { "base": "BrowserStack", "os_version": "10", "browser": "edge", - "browser_version": "14.0", + "browser_version": "16.0", + "device": null, + "os": "Windows" + }, + "bs_edge_17_windows_10": { + "base": "BrowserStack", + "os_version": "10", + "browser": "edge", + "browser_version": "17.0", "device": null, "os": "Windows" }, @@ -15,51 +23,51 @@ "device": null, "os": "Windows" }, - "bs_chrome_62_windows_10": { + "bs_chrome_72_windows_10": { "base": "BrowserStack", "os_version": "10", "browser": "chrome", - "browser_version": "62.0", + "browser_version": "72.0", "device": null, "os": "Windows" }, - "bs_chrome_61_windows_10": { + "bs_chrome_71_windows_10": { "base": "BrowserStack", "os_version": "10", "browser": "chrome", - "browser_version": "61.0", + "browser_version": "71.0", "device": null, "os": "Windows" }, - "bs_firefox_58_windows_10": { + "bs_firefox_65_windows_10": { "base": "BrowserStack", "os_version": "10", "browser": "firefox", - "browser_version": "58.0", + "browser_version": "65.0", "device": null, "os": "Windows" }, - "bs_firefox_57_windows_10": { + "bs_firefox_64_windows_10": { "base": "BrowserStack", "os_version": "10", "browser": "firefox", - "browser_version": "57.0", + "browser_version": "64.0", "device": null, "os": "Windows" }, - "bs_safari_9.1_mac_elcapitan": { + "bs_safari_11_mac_high_sierra": { "base": "BrowserStack", - "os_version": "El Capitan", + "os_version": "High Sierra", "browser": "safari", - "browser_version": "9.1", + "browser_version": "11.1", "device": null, "os": "OS X" }, - "bs_safari_8_mac_yosemite": { + "bs_safari_12_mac_mojave": { "base": "BrowserStack", - "os_version": "Yosemite", + "os_version": "Mojave", "browser": "safari", - "browser_version": "8.0", + "browser_version": "12.0", "device": null, "os": "OS X" } diff --git a/gulpHelpers.js b/gulpHelpers.js index 33169da3773..f20a2673ade 100644 --- a/gulpHelpers.js +++ b/gulpHelpers.js @@ -3,7 +3,6 @@ const fs = require('fs.extra'); const path = require('path'); const argv = require('yargs').argv; const MANIFEST = 'package.json'; -const exec = require('child_process').exec; const through = require('through2'); const _ = require('lodash'); const gutil = require('gulp-util'); @@ -13,7 +12,6 @@ const BUILD_PATH = './build/dist'; const DEV_PATH = './build/dev'; const ANALYTICS_PATH = '../analytics'; - // get only subdirectories that contain package.json with 'main' property function isModuleDirectory(filePath) { try { @@ -22,8 +20,7 @@ function isModuleDirectory(filePath) { const module = require(manifestPath); return module && module.main; } - } - catch (error) {} + } catch (error) {} } module.exports = { @@ -38,8 +35,8 @@ module.exports = { jsonifyHTML: function (str) { console.log(arguments); return str.replace(/\n/g, '') - .replace(/<\//g, '<\\/') - .replace(/\/>/g, '\\/>'); + .replace(/<\//g, '<\\/') + .replace(/\/>/g, '\\/>'); }, getArgModules() { var modules = (argv.modules || '').split(',').filter(module => !!module); @@ -52,7 +49,7 @@ module.exports = { fs.readFileSync(moduleFile, 'utf8') ); } - } catch(e) { + } catch (e) { throw new gutil.PluginError({ plugin: 'modules', message: 'failed reading: ' + argv.modules @@ -72,19 +69,19 @@ module.exports = { var moduleName = file.split(new RegExp('[.\\' + path.sep + ']'))[0]; var modulePath = path.join(absoluteModulePath, file); if (fs.lstatSync(modulePath).isDirectory()) { - modulePath = path.join(modulePath, "index.js") + modulePath = path.join(modulePath, 'index.js') } memo[modulePath] = moduleName; return memo; }, {}); - } catch(err) { + } catch (err) { internalModules = {}; } return Object.assign(externalModules.reduce((memo, module) => { try { var modulePath = require.resolve(module); memo[modulePath] = module; - } catch(err) { + } catch (err) { // do something } return memo; @@ -93,7 +90,7 @@ module.exports = { getBuiltModules: function(dev, externalModules) { var modules = this.getModuleNames(externalModules); - if(Array.isArray(externalModules)) { + if (Array.isArray(externalModules)) { modules = _.intersection(modules, externalModules); } return modules.map(name => path.join(__dirname, dev ? DEV_PATH : BUILD_PATH, name + '.js')); @@ -128,7 +125,7 @@ module.exports = { * Returns an array of source files for inclusion in build process */ getAnalyticsSources: function() { - if (!argv.analytics) {return [];} // empty arrays won't affect a standard build + if (!argv.analytics) { return []; } // empty arrays won't affect a standard build const directoryContents = fs.readdirSync(ANALYTICS_PATH); return directoryContents @@ -155,62 +152,5 @@ module.exports = { } return options; - }, - - createEnd2EndTestReport : function(targetDestinationDir) { - var browsers = require('./browsers.json'); - var env = []; - var input = 'bs'; - for(var key in browsers) { - if(key.substring(0, input.length) === input && browsers[key].browser !== 'iphone') { - env.push(key); - } - } - - //create new directory structure - fs.rmrfSync(targetDestinationDir); - env.forEach(item => { - fs.mkdirpSync(targetDestinationDir + '/' + item); - }); - - //move xml files to newly created directory - var walker = fs.walk('./build/coverage/e2e/reports'); - walker.on("file", function (root, stat, next) { - env.forEach(item => { - if(stat.name.search(item) !== -1) { - var src = root + '/' + stat.name; - var dest = targetDestinationDir + '/' + item + '/' + stat.name; - fs.copy(src, dest, {replace: true}, function(err) { - if(err) { - throw err; - } - }); - } - }); - next(); - }); - - //run junit-viewer to read xml and create html - env.forEach(item => { - //junit-viewer --results="./custom-reports/chrome51" --save="./chrome.html" - var cmd = 'junit-viewer --results="' + targetDestinationDir + '/' + item + '" --save="' + targetDestinationDir + '/' + item +'.html"'; - exec(cmd); - }); - - //create e2e-results.html - var html = 'End to End Testing Result
Note: Refresh in 2-3 seconds if it says "Cannot get ....."
'; - var li = ''; - var tabs = ''; - env.forEach(function(item,i) { - i++; - li = li + '
  • '+item+'
  • '; - tabs = tabs + '
    '; - }); - html = html + '
      ' + li + '
    ' + tabs; - html = html + '
    '; - - var filepath = targetDestinationDir + '/results.html'; - fs.openSync(filepath, 'w+'); - fs.writeFileSync(filepath, html); } }; diff --git a/gulpfile.js b/gulpfile.js index d3b29aa66a7..0170df80c62 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -26,6 +26,8 @@ var sourcemaps = require('gulp-sourcemaps'); var through = require('through2'); var fs = require('fs'); var jsEscape = require('gulp-js-escape'); +const path = require('path'); +const execa = require('execa'); var prebid = require('./package.json'); var dateString = 'Updated : ' + (new Date()).toISOString().substring(0, 10); @@ -51,22 +53,6 @@ function clean() { .pipe(gulpClean()); } -function e2etestReport() { - var reportPort = 9010; - var targetDestinationDir = './e2etest-report'; - helpers.createEnd2EndTestReport(targetDestinationDir); - connect.server({ - port: reportPort, - root: './', - livereload: true - }); - - setTimeout(function() { - opens('http://localhost:' + reportPort + '/' + targetDestinationDir.slice(2) + '/results.html'); - }, 5000); -}; -e2etestReport.displayName = 'e2etest-report'; - // Dependant task for building postbid. It escapes postbid-config file. function escapePostbidConfig() { gulp.src('./integrationExamples/postbid/oas/postbid-config.js') @@ -92,13 +78,14 @@ function lint(done) { // View the code coverage report in the browser. function viewCoverage(done) { var coveragePort = 1999; + var mylocalhost = (argv.host) ? argv.host : 'localhost'; connect.server({ port: coveragePort, root: 'build/coverage/karma_html', livereload: false }); - opens('http://localhost:' + coveragePort); + opens('http://' + mylocalhost + ':' + coveragePort); done(); }; @@ -244,6 +231,13 @@ function newKarmaCallback(done) { function test(done) { if (argv.notest) { done(); + } else if (argv.e2e) { + let wdioCmd = path.join(__dirname, 'node_modules/.bin/wdio'); + let wdioConf = path.join(__dirname, 'wdio.conf.js'); + let wdioOpts = [ + wdioConf + ]; + return execa(wdioCmd, wdioOpts, { stdio: 'inherit' }); } else { var karmaConf = karmaConfMaker(false, argv.browserstack, argv.watch, argv.file); @@ -268,35 +262,6 @@ function coveralls() { // 2nd arg is a dependency: 'test' must be finished .pipe(shell('cat build/coverage/lcov.info | node_modules/coveralls/bin/coveralls.js')); } -function e2eTest() { - var cmdQueue = []; - if (argv.browserstack) { - var browsers = require('./browsers.json'); - delete browsers['bs_ie_9_windows_7']; - - var cmdStr = ' --config nightwatch.conf.js'; - if (argv.group) { - cmdStr = cmdStr + ' --group ' + argv.group; - } - cmdStr = cmdStr + ' --reporter ./test/spec/e2e/custom-reporter/pbjs-html-reporter.js'; - - var startWith = 'bs'; - - Object.keys(browsers).filter(function(v) { - return v.substring(0, startWith.length) === startWith && browsers[v].browser !== 'iphone'; - }).map(function(v, i, arr) { - var newArr = (i % 2 === 0) ? arr.slice(i, i + 2) : null; - if (newArr) { - var cmd = 'nightwatch --env ' + newArr.join(',') + cmdStr; - cmdQueue.push(cmd); - } - }); - } - - return gulp.src('') - .pipe(shell(cmdQueue.join(';'))); -} - // This task creates postbid.js. Postbid setup is different from prebid.js // More info can be found here http://prebid.org/overview/what-is-post-bid.html @@ -308,6 +273,21 @@ function buildPostbid() { .pipe(gulp.dest('build/postbid/')); } +function setupE2e(done) { + if (!argv.host) { + throw new gutil.PluginError({ + plugin: 'E2E test', + message: gutil.colors.red('Host should be defined e.g. ap.localhost, anlocalhost. localhost cannot be used as safari browserstack is not able to connect to localhost') + }); + } + process.env.TEST_SERVER_HOST = argv.host; + if (argv.https) { + process.env.TEST_SERVER_PROTOCOL = argv.https; + } + argv.e2e = true; + done(); +} + // support tasks gulp.task(lint); gulp.task(watch); @@ -333,12 +313,9 @@ gulp.task('build-postbid', gulp.series(escapePostbidConfig, buildPostbid)); gulp.task('serve', gulp.series(clean, lint, gulp.parallel('build-bundle-dev', watch, test))); gulp.task('default', gulp.series(clean, makeWebpackPkg)); -gulp.task(e2etestReport); -gulp.task('e2etest', gulp.series(clean, gulp.parallel(makeDevpackPkg, makeWebpackPkg), e2eTest)); - +gulp.task('e2e-test', gulp.series(clean, setupE2e, gulp.parallel('build-bundle-dev', watch), test)) // other tasks gulp.task(bundleToStdout); gulp.task('bundle', gulpBundle.bind(null, false)); // used for just concatenating pre-built files with no build step -gulp.task('serve-nw', gulp.parallel(lint, watch, 'e2etest')); module.exports = nodeBundle; diff --git a/integrationExamples/longform/basic_w_custom_adserver_translation.html b/integrationExamples/longform/basic_w_custom_adserver_translation.html new file mode 100644 index 00000000000..995ea822da4 --- /dev/null +++ b/integrationExamples/longform/basic_w_custom_adserver_translation.html @@ -0,0 +1,132 @@ + + + + + Prebid Freewheel Integration Demo + + + + + + + + + + + + + + + + + + +

    Prebid Freewheel Integration Demo

    +

    custom adserver translation file

    +
    +
    + +
    +
    +
    +
    +

    + +

    +
    +
    +
    + // bids +
    +
    +
    +
    +
    +

    + +

    +
    +
    +
    + // bids +
    +
    +
    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/integrationExamples/longform/basic_w_requireExactDuration.html b/integrationExamples/longform/basic_w_requireExactDuration.html new file mode 100644 index 00000000000..016b51d09c4 --- /dev/null +++ b/integrationExamples/longform/basic_w_requireExactDuration.html @@ -0,0 +1,130 @@ + + + + + Prebid Freewheel Integration Demo + + + + + + + + + + + + + + + + + + +

    Prebid Freewheel Test Page

    +

    requireExactDuration = true

    +
    +
    + +
    +
    +
    +
    +

    + +

    +
    +
    +
    + // bids +
    +
    +
    +
    +
    +

    + +

    +
    +
    +
    + // bids +
    +
    +
    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/integrationExamples/longform/basic_wo_brandCategoryExclusion.html b/integrationExamples/longform/basic_wo_brandCategoryExclusion.html new file mode 100644 index 00000000000..c8af5801406 --- /dev/null +++ b/integrationExamples/longform/basic_wo_brandCategoryExclusion.html @@ -0,0 +1,130 @@ + + + + + Prebid Freewheel Integration Demo + + + + + + + + + + + + + + + + + + +

    Prebid Freewheel Test Page

    +

    brandCategoryExclusion = false

    +
    +
    + +
    +
    +
    +
    +

    + +

    +
    +
    +
    + // bids +
    +
    +
    +
    +
    +

    + +

    +
    +
    +
    + // bids +
    +
    +
    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/integrationExamples/longform/basic_wo_requireExactDuration.html b/integrationExamples/longform/basic_wo_requireExactDuration.html new file mode 100644 index 00000000000..215138479cc --- /dev/null +++ b/integrationExamples/longform/basic_wo_requireExactDuration.html @@ -0,0 +1,131 @@ + + + + + Prebid Freewheel Integration Demo + + + + + + + + + + + + + + + + + + +

    Prebid Freewheel Test Page

    +

    requireExactDuration = false

    +
    +
    + +
    +
    +
    +
    +

    + +

    +
    +
    +
    + // bids +
    +
    +
    +
    +
    +

    + +

    +
    +
    +
    + // bids +
    +
    +
    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/integrationExamples/longform/custom_adserver_translation.json b/integrationExamples/longform/custom_adserver_translation.json new file mode 100644 index 00000000000..377fe9cdeda --- /dev/null +++ b/integrationExamples/longform/custom_adserver_translation.json @@ -0,0 +1,1180 @@ +{ + "mapping":{ + "IAB1-1":{ + "id":404, + "name":"Publishing" + }, + "IAB1-2":{ + "id":392, + "name":"Entertainment" + }, + "IAB1-5":{ + "id":419, + "name":"Filmed Entertainment" + }, + "IAB1-6":{ + "id":392, + "name":"Entertainment" + }, + "IAB1-7":{ + "id":392, + "name":"Entertainment" + }, + "IAB2-1":{ + "id":399, + "name":"Automotive" + }, + "IAB2-2":{ + "id":399, + "name":"Automotive" + }, + "IAB2-3":{ + "id":399, + "name":"Automotive" + }, + "IAB2-4":{ + "id":399, + "name":"Automotive" + }, + "IAB2-5":{ + "id":399, + "name":"Automotive" + }, + "IAB2-6":{ + "id":399, + "name":"Automotive" + }, + "IAB2-7":{ + "id":399, + "name":"Automotive" + }, + "IAB2-8":{ + "id":399, + "name":"Automotive" + }, + "IAB2-9":{ + "id":399, + "name":"Automotive" + }, + "IAB2-10":{ + "id":399, + "name":"Automotive" + }, + "IAB2-11":{ + "id":399, + "name":"Automotive" + }, + "IAB2-12":{ + "id":399, + "name":"Automotive" + }, + "IAB2-13":{ + "id":399, + "name":"Automotive" + }, + "IAB2-14":{ + "id":399, + "name":"Automotive" + }, + "IAB2-15":{ + "id":399, + "name":"Automotive" + }, + "IAB2-16":{ + "id":399, + "name":"Automotive" + }, + "IAB2-17":{ + "id":399, + "name":"Automotive" + }, + "IAB2-18":{ + "id":399, + "name":"Automotive" + }, + "IAB2-19":{ + "id":399, + "name":"Automotive" + }, + "IAB2-20":{ + "id":399, + "name":"Automotive" + }, + "IAB2-21":{ + "id":399, + "name":"Automotive" + }, + "IAB2-22":{ + "id":399, + "name":"Automotive" + }, + "IAB2-23":{ + "id":399, + "name":"Automotive" + }, + "IAB3-1":{ + "id":393, + "name":"Business Services" + }, + "IAB3-2":{ + "id":393, + "name":"Business Services" + }, + "IAB3-3":{ + "id":393, + "name":"Business Services" + }, + "IAB3-4":{ + "id":409, + "name":"Computing Product" + }, + "IAB3-5":{ + "id":393, + "name":"Business Services" + }, + "IAB3-6":{ + "id":393, + "name":"Business Services" + }, + "IAB3-7":{ + "id":398, + "name":"Government/Municipal" + }, + "IAB3-8":{ + "id":393, + "name":"Business Services" + }, + "IAB3-9":{ + "id":393, + "name":"Business Services" + }, + "IAB3-10":{ + "id":393, + "name":"Business Services" + }, + "IAB3-11":{ + "id":393, + "name":"Business Services" + }, + "IAB3-12":{ + "id":393, + "name":"Business Services" + }, + "IAB4-1":{ + "id":393, + "name":"Business Services" + }, + "IAB4-2":{ + "id":405, + "name":"Educational Services" + }, + "IAB4-3":{ + "id":405, + "name":"Educational Services" + }, + "IAB4-4":{ + "id":393, + "name":"Business Services" + }, + "IAB4-5":{ + "id":393, + "name":"Business Services" + }, + "IAB4-6":{ + "id":393, + "name":"Business Services" + }, + "IAB4-7":{ + "id":406, + "name":"Health Care Services" + }, + "IAB4-8":{ + "id":405, + "name":"Educational Services" + }, + "IAB4-9":{ + "id":417, + "name":"Telecommunications" + }, + "IAB4-10":{ + "id":429, + "name":"Military" + }, + "IAB4-11":{ + "id":393, + "name":"Business Services" + }, + "IAB5-1":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-2":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-3":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-4":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-5":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-6":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-7":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-8":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-9":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-10":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-11":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-12":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-13":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-14":{ + "id":405, + "name":"Educational Services" + }, + "IAB5-15":{ + "id":405, + "name":"Educational Services" + }, + "IAB7-1":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-2":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-3":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-4":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-5":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-6":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-7":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-8":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-9":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-10":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-11":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-12":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-13":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-14":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-15":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-16":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-17":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-18":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-19":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-20":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-21":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-22":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-23":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-24":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-25":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-26":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-27":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-28":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-29":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-30":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-31":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-32":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-33":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-34":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-35":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-36":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-37":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-38":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-39":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-40":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-41":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-42":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-43":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-44":{ + "id":406, + "name":"Health Care Services" + }, + "IAB7-45":{ + "id":406, + "name":"Health Care Services" + }, + "IAB8-1":{ + "id":394, + "name":"Food" + }, + "IAB8-2":{ + "id":394, + "name":"Food" + }, + "IAB8-3":{ + "id":394, + "name":"Food" + }, + "IAB8-4":{ + "id":394, + "name":"Food" + }, + "IAB8-5":{ + "id":400, + "name":"Beer/Wine/Liquor" + }, + "IAB8-6":{ + "id":401, + "name":"Beverages" + }, + "IAB8-7":{ + "id":394, + "name":"Food" + }, + "IAB8-8":{ + "id":394, + "name":"Food" + }, + "IAB8-9":{ + "id":407, + "name":"Restaurant/Fast Food" + }, + "IAB8-10":{ + "id":394, + "name":"Food" + }, + "IAB8-11":{ + "id":394, + "name":"Food" + }, + "IAB8-12":{ + "id":394, + "name":"Food" + }, + "IAB8-13":{ + "id":394, + "name":"Food" + }, + "IAB8-14":{ + "id":394, + "name":"Food" + }, + "IAB8-15":{ + "id":394, + "name":"Food" + }, + "IAB8-16":{ + "id":394, + "name":"Food" + }, + "IAB8-17":{ + "id":394, + "name":"Food" + }, + "IAB8-18":{ + "id":400, + "name":"Beer/Wine/Liquor" + }, + "IAB9-1":{ + "id":392, + "name":"Entertainment" + }, + "IAB9-3":{ + "id":418, + "name":"Jewelry" + }, + "IAB9-5":{ + "id":413, + "name":"Gaming" + }, + "IAB9-6":{ + "id":412, + "name":"Household Products" + }, + "IAB9-9":{ + "id":426, + "name":"Tobacco" + }, + "IAB9-11":{ + "id":404, + "name":"Publishing" + }, + "IAB9-15":{ + "id":404, + "name":"Publishing" + }, + "IAB9-16":{ + "id":392, + "name":"Entertainment" + }, + "IAB9-18":{ + "id":393, + "name":"Business Services" + }, + "IAB9-19":{ + "id":418, + "name":"Jewelry" + }, + "IAB9-23":{ + "id":424, + "name":"Photographic Equipment" + }, + "IAB9-24":{ + "id":392, + "name":"Entertainment" + }, + "IAB9-25":{ + "id":392, + "name":"Entertainment" + }, + "IAB9-30":{ + "id":392, + "name":"Entertainment" + }, + "IAB10-1":{ + "id":415, + "name":"Appliances" + }, + "IAB10-5":{ + "id":434, + "name":"Home Furnishings" + }, + "IAB10-6":{ + "id":434, + "name":"Home Furnishings" + }, + "IAB10-7":{ + "id":434, + "name":"Home Furnishings" + }, + "IAB10-8":{ + "id":393, + "name":"Business Services" + }, + "IAB10-9":{ + "id":434, + "name":"Home Furnishings" + }, + "IAB11-1":{ + "id":398, + "name":"Government/Municipal" + }, + "IAB11-2":{ + "id":398, + "name":"Government/Municipal" + }, + "IAB11-3":{ + "id":398, + "name":"Government/Municipal" + }, + "IAB11-4":{ + "id":398, + "name":"Government/Municipal" + }, + "IAB11-5":{ + "id":398, + "name":"Government/Municipal" + }, + "IAB12-1":{ + "id":438, + "name":"News" + }, + "IAB12-2":{ + "id":438, + "name":"News" + }, + "IAB12-3":{ + "id":438, + "name":"News" + }, + "IAB13-1":{ + "id":393, + "name":"Business Services" + }, + "IAB13-2":{ + "id":393, + "name":"Business Services" + }, + "IAB13-3":{ + "id":438, + "name":"News" + }, + "IAB13-4":{ + "id":391, + "name":"Financial Services" + }, + "IAB13-5":{ + "id":393, + "name":"Business Services" + }, + "IAB13-6":{ + "id":436, + "name":"Insurance" + }, + "IAB13-7":{ + "id":393, + "name":"Business Services" + }, + "IAB13-8":{ + "id":393, + "name":"Business Services" + }, + "IAB13-9":{ + "id":393, + "name":"Business Services" + }, + "IAB13-10":{ + "id":393, + "name":"Business Services" + }, + "IAB13-11":{ + "id":393, + "name":"Business Services" + }, + "IAB13-12":{ + "id":393, + "name":"Business Services" + }, + "IAB16-1":{ + "id":423, + "name":"Pet Food/Supplies" + }, + "IAB16-2":{ + "id":423, + "name":"Pet Food/Supplies" + }, + "IAB16-3":{ + "id":423, + "name":"Pet Food/Supplies" + }, + "IAB16-4":{ + "id":423, + "name":"Pet Food/Supplies" + }, + "IAB16-5":{ + "id":423, + "name":"Pet Food/Supplies" + }, + "IAB16-6":{ + "id":423, + "name":"Pet Food/Supplies" + }, + "IAB16-7":{ + "id":423, + "name":"Pet Food/Supplies" + }, + "IAB17-1":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-2":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-3":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-4":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-5":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-6":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-7":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-8":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-9":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-10":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-11":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-12":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-13":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-14":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-15":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-16":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-17":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-18":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-19":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-20":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-21":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-22":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-23":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-24":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-25":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-26":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-27":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-28":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-29":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-30":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-31":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-32":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-33":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-34":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-35":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-36":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-37":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-38":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-39":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-40":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-41":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-42":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-43":{ + "id":425, + "name":"Professional Sports" + }, + "IAB17-44":{ + "id":425, + "name":"Professional Sports" + }, + "IAB18-1":{ + "id":411, + "name":"Cosmetics/Toiletries" + }, + "IAB18-2":{ + "id":397, + "name":"Apparel" + }, + "IAB18-3":{ + "id":397, + "name":"Apparel" + }, + "IAB18-4":{ + "id":418, + "name":"Jewelry" + }, + "IAB18-5":{ + "id":397, + "name":"Apparel" + }, + "IAB18-6":{ + "id":397, + "name":"Apparel" + }, + "IAB19-2":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-3":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-4":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-5":{ + "id":424, + "name":"Photographic Equipment" + }, + "IAB19-6":{ + "id":417, + "name":"Telecommunications" + }, + "IAB19-7":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-8":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-9":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-10":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-11":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-12":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-13":{ + "id":404, + "name":"Publishing" + }, + "IAB19-14":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-15":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-16":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-17":{ + "id":419, + "name":"Filmed Entertainment" + }, + "IAB19-18":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-19":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-20":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-21":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-22":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-23":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-24":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-25":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-26":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-27":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-28":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-29":{ + "id":392, + "name":"Entertainment" + }, + "IAB19-30":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-31":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-32":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-33":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-34":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-35":{ + "id":409, + "name":"Computing Product" + }, + "IAB19-36":{ + "id":409, + "name":"Computing Product" + }, + "IAB20-1":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-2":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-3":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-4":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-5":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-6":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-7":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-8":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-9":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-10":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-11":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-12":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-13":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-14":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-15":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-16":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-17":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-18":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-19":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-20":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-21":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-22":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-23":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-24":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-25":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-26":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB20-27":{ + "id":395, + "name":"Travel/Hotels/Airlines" + }, + "IAB21-1":{ + "id":416, + "name":"Real Estate" + }, + "IAB21-2":{ + "id":416, + "name":"Real Estate" + }, + "IAB21-3":{ + "id":416, + "name":"Real Estate" + }, + "IAB22-1":{ + "id":403, + "name":"Retail Stores/Chains" + }, + "IAB22-2":{ + "id":403, + "name":"Retail Stores/Chains" + }, + "IAB22-3":{ + "id":403, + "name":"Retail Stores/Chains" + } + } +} \ No newline at end of file diff --git a/integrationExamples/longform/longformTestUtils.js b/integrationExamples/longform/longformTestUtils.js new file mode 100644 index 00000000000..096be91bb77 --- /dev/null +++ b/integrationExamples/longform/longformTestUtils.js @@ -0,0 +1,66 @@ +var prebidTestUtils = prebidTestUtils || {}; + +function getIndustry(id) { + var mapping = window.localStorage.getItem('iabToFwMappingkey'); + if (mapping) { + try { + mapping = JSON.parse(mapping); + } catch (error) { + // + } + var industry; + mapping = mapping['mapping']; + for (var v in mapping) { + if (mapping[v]['id'] == id) { + industry = mapping[v]['name']; + break; + } + } + return industry; + } +} + +prebidTestUtils.loadKv = function (targetingArr) { + var div = document.getElementById('collapseThree').children[0]; + var html = ''; + Object.keys(targetingArr).forEach(function(adUnitCode) { + targetingArr[adUnitCode].forEach(function (targeting) { + Object.keys(targeting).forEach(function (key) { + html += '' + }); + }); + }); + html += '
    ' + key + '' + targeting[key] + '
    '; + div.innerHTML = html; +} + +prebidTestUtils.loadBids = function (targetingArr, brandCatExclusion) { + var div = document.getElementById('collapseTwo').children[0]; + var html = ''; + var index = 1; + Object.keys(targetingArr).forEach(function(adUnitCode) { + targetingArr[adUnitCode].forEach(function (targeting) { + Object.keys(targeting).forEach(function (key) { + if (key !== 'hb_cache_id') { + var result = targeting[key].split('_'); + html += ''; + html += ''; + html += ''; + if (brandCatExclusion) { + html += ''; + html += ''; + } else { + html += ''; + html += ''; + } + html += ''; + html += ''; + html += ''; + index++; + } + }); + }); + }); + html += '
    #CPMIndustryDurationStatusComm Break #
    ' + index + '' + result[0] + '' + getIndustry(result[1]) + '' + result[2] + '' + result[1] + '
    '; + div.innerHTML = html; +} diff --git a/integrationExamples/longform/longform_testpages_style.css b/integrationExamples/longform/longform_testpages_style.css new file mode 100644 index 00000000000..fe8105edcfa --- /dev/null +++ b/integrationExamples/longform/longform_testpages_style.css @@ -0,0 +1,34 @@ +#videoPlayer { + height: 480px; + width: 100%; + background-color: #000; +} + +.outer { + position: relative; +} + +#skip { + position: absolute; + right: 20px; + bottom: 20px; + display: none; + color: white; + background-color: black; + border: 1px solid gray; +} + +#adCount { + color: white; + position: absolute; + left: 20px; + bottom: 20px; + display: none; +} + +#showad { + color: white; + position: absolute; + left: 20px; + top: 20px; +} \ No newline at end of file diff --git a/karma.conf.maker.js b/karma.conf.maker.js index 649a13a16fc..9aa416375b5 100644 --- a/karma.conf.maker.js +++ b/karma.conf.maker.js @@ -85,7 +85,8 @@ function setBrowsers(karmaConf, browserstack) { if (browserstack) { karmaConf.browserStack = { username: process.env.BROWSERSTACK_USERNAME, - accessKey: process.env.BROWSERSTACK_ACCESS_KEY + accessKey: process.env.BROWSERSTACK_ACCESS_KEY, + build: 'Prebidjs Unit Tests ' + new Date().toLocaleString() } if (process.env.TRAVIS) { karmaConf.browserStack.startTunnel = false; diff --git a/package.json b/package.json index c3b4e5cd042..1e1c130b6fe 100755 --- a/package.json +++ b/package.json @@ -31,13 +31,13 @@ "chai": "^4.2.0", "coveralls": "^3.0.2", "documentation": "^5.2.2", - "ejs": "^2.5.1", "es5-shim": "^4.5.2", "eslint-config-standard": "^10.2.1", "eslint-plugin-import": "^2.2.0", "eslint-plugin-node": "^5.1.0", "eslint-plugin-promise": "^3.5.0", "eslint-plugin-standard": "^3.0.1", + "execa": "^1.0.0", "faker": "^3.1.0", "fs.extra": "^1.3.2", "gulp": "^4.0.0", @@ -78,12 +78,16 @@ "karma-webpack": "^3.0.5", "lodash": "^4.17.4", "mocha": "^5.0.0", - "nightwatch": "^1.0.6", "opn": "^5.4.0", "querystringify": "0.0.3", "sinon": "^4.1.3", "through2": "^2.0.3", "url-parse": "^1.0.5", + "wdio-browserstack-service": "^0.1.17", + "wdio-concise-reporter": "^0.1.2", + "wdio-mocha-framework": "^0.6.3", + "wdio-spec-reporter": "^0.1.5", + "webdriverio": "^4.13.2", "webpack": "^3.0.0", "webpack-stream": "^3.2.0", "yargs": "^1.3.1" diff --git a/test/helpers/testing-utils.js b/test/helpers/testing-utils.js new file mode 100644 index 00000000000..21d5992873e --- /dev/null +++ b/test/helpers/testing-utils.js @@ -0,0 +1,4 @@ +module.exports = { + host: (process.env.TEST_SERVER_HOST) ? process.env.TEST_SERVER_HOST : 'localhost', + protocol: (process.env.TEST_SERVER_PROTOCOL) ? 'https' : 'http' +} diff --git a/test/spec/e2e/common/globals.js b/test/spec/e2e/common/globals.js deleted file mode 100644 index 3e781d5fa21..00000000000 --- a/test/spec/e2e/common/globals.js +++ /dev/null @@ -1,9 +0,0 @@ -var HtmlReporter = require('nightwatch-html-reporter'); -var reporter = new HtmlReporter({ - openBrowser: true, - reportsDirectory: __dirname + '/reports', - themeName: 'cover', -}); -module.exports = { - reporter: reporter.fn -}; diff --git a/test/spec/e2e/common/utils.js b/test/spec/e2e/common/utils.js deleted file mode 100644 index 2c9fee68eda..00000000000 --- a/test/spec/e2e/common/utils.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = { - findIframeInDiv: function(divid) { - var div = document.getElementById(divid); - var iframes = div.getElementsByTagName('iframe'); - console.log(iframes.length); - try { - if (iframes.length === 1 && iframes[0].contentWindow.document.body.innerHTML === '') { - return false; - } else { - return true; - } - } catch (e) { - return true; - } - } -}; diff --git a/test/spec/e2e/custom-assertions/first.js b/test/spec/e2e/custom-assertions/first.js deleted file mode 100644 index e393e8c15a0..00000000000 --- a/test/spec/e2e/custom-assertions/first.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Checks if the given attribute of an element has the expected value. - * - * ``` - * this.demoTest = function (client) { - * browser.assert.attributeEquals('body', 'data-attr', 'some value'); - * }; - * ``` - * - * @method attributeEquals - * @param {string} selector The selector (CSS / Xpath) used to locate the element. - * @param {string} attribute The attribute name - * @param {string} expected The expected value of the attribute to check. - * @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default. - * @api assertions - */ - -var util = require('util'); -exports.assertion = function(expected, msg) { - var DEFAULT_MSG = 'Testing if attribute %s of <%s> equals "%s".'; - - this.message = msg; - - this.expected = function() { - return expected; - }; - - this.pass = function(value) { - return value === expected; - }; - - this.failure = function(result) { - var failed = false; - return failed; - }; - - this.value = function(result) { - console.log('**********'); - console.log(result); - return result.value; - }; - - this.command = function(callback) { - var _this = this; - var execcallback = function(result) { - // console.log(_this); - console.log('**********'); - console.log(result); - console.log(callback.toString()); - if (callback) { - return callback.call(_this, result.value); - } - }; - - this.api.execute(function() { - // cusotm logic - return 'hello'; - }, [], execcallback); - - // var result = {'value':'hello'}; - - return this; - }; -}; diff --git a/test/spec/e2e/custom-reporter/junit.xml.ejs b/test/spec/e2e/custom-reporter/junit.xml.ejs deleted file mode 100644 index f03e0ee38e5..00000000000 --- a/test/spec/e2e/custom-reporter/junit.xml.ejs +++ /dev/null @@ -1,32 +0,0 @@ - - - - - <% for (var item in module.completed) { - var testcase = module.completed[item]; - var assertions = testcase.assertions %> - <% - for (var i = 0; i < assertions.length; i++) { %><% if (assertions[i].failure) { %> <%= assertions[i].stackTrace %><% } %> -<% if (assertions[i].screenshots && assertions[i].screenshots.length > 0) { %><% for (var j = 0; j < assertions[i].screenshots.length; j++) { %>[[ATTACHMENT|<%= assertions[i].screenshots[j] %>]]<% } %><% } %> - <% } - if (testcase.failed > 0 && testcase.stackTrace) { - %><%= testcase.stackTrace %><% } %> - <% if (systemerr != '') {%> - - <%= systemerr %> - <% } %> - <% } %> - <% if (module.skipped && (module.skipped.length > 0)) { %> - <% for (var j = 0; j < module.skipped.length; j++) { %> - - - - <% } %> - <% } %> - - diff --git a/test/spec/e2e/custom-reporter/pbjs-html-reporter.js b/test/spec/e2e/custom-reporter/pbjs-html-reporter.js deleted file mode 100644 index 85be2a397a5..00000000000 --- a/test/spec/e2e/custom-reporter/pbjs-html-reporter.js +++ /dev/null @@ -1,113 +0,0 @@ -var fs = require('fs.extra'); -var path = require('path'); -var ejs = require('ejs'); - -module.exports = new function() { - var tmpl = __dirname + '/junit.xml.ejs'; - var tmplData; - var globalResults; - - function loadTemplate(cb) { - if (tmplData) { - cb(tmplData); - return; - } - fs.readFile(tmpl, function (err, data) { - if (err) { - throw err; - } - tmplData = data.toString(); - cb(tmplData); - }); - } - - function adaptAssertions(module) { - Object.keys(module.completed).forEach(function(item) { - var testcase = module.completed[item]; - var assertions = testcase.assertions; - for (var i = 0; i < assertions.length; i++) { - if (assertions[i].stackTrace) { - assertions[i].stackTrace = stackTraceFilter(assertions[i].stackTrace.split('\n')); - } - } - - if (testcase.failed > 0 && testcase.stackTrace) { - var stackParts = testcase.stackTrace.split('\n'); - var errorMessage = stackParts.shift(); - testcase.stackTrace = stackTraceFilter(stackParts); - testcase.message = errorMessage; - } - }); - } - - function writeReport(moduleKey, data, opts, callback) { - var module = globalResults.modules[moduleKey]; - var pathParts = moduleKey.split(path.sep); - var moduleName = pathParts.pop(); - var output_folder = opts.output_folder; - adaptAssertions(module); - - var rendered = ejs.render(data, { - module: module, - moduleName: moduleName, - systemerr: globalResults.errmessages.join('\n'), - }); - - if (pathParts.length) { - output_folder = path.join(output_folder, pathParts.join(path.sep)); - fs.mkdirpSync(output_folder); - } - - var filename = path.join(output_folder, opts.filename_prefix + moduleName + '.xml'); - fs.writeFile(filename, rendered, function(err) { - callback(err); - globalResults.errmessages.length = 0; - }); - } - - function stackTraceFilter(parts) { - var stack = parts.reduce(function(list, line) { - if (contains(line, [ - 'node_modules', - '(node.js:', - '(events.js:' - ])) { - return list; - } - - list.push(line); - return list; - }, []); - - return stack.join('\n'); - } - - function contains(str, text) { - if (Object.prototype.toString.call(text) === '[object Array]') { - for (var i = 0; i < text.length; i++) { - if (contains(str, text[i])) { - return true; - } - } - } - return str.indexOf(text) > -1; - } - - this.write = function(results, options, callback) { - options.filename_prefix = process.env.__NIGHTWATCH_ENV + '_'; - globalResults = results; - var keys = Object.keys(results.modules); - - loadTemplate(function createReport(data) { - var moduleKey = keys.shift(); - - writeReport(moduleKey, data, options, function(err) { - if (err || (keys.length === 0)) { - callback(err); - } else { - createReport(data); - } - }); - }); - }; -}(); diff --git a/test/spec/e2e/gpt-examples/all_bidders_instant_load.html b/test/spec/e2e/gpt-examples/all_bidders_instant_load.html deleted file mode 100644 index bf31b769431..00000000000 --- a/test/spec/e2e/gpt-examples/all_bidders_instant_load.html +++ /dev/null @@ -1,830 +0,0 @@ - - - - - - - - - -

    Prebid.js Test3

    - -

    adequant

    -
    -

    No response

    - -
    - -

    adform

    -
    -

    No response

    - -
    - - -

    aol

    -
    -

    No response

    - -
    - -

    appnexus

    -
    -

    No response

    - -
    - -

    indexExchange

    -
    -

    No response

    - -
    - -

    openx

    -
    -

    No response

    - -
    - -

    pubmatic

    -
    -

    No response

    - -
    - -

    pulsepoint

    -
    -

    No response

    - -
    - -

    rubicon

    -
    -

    No response

    - -
    - -

    sonobi

    -
    -

    No response

    - -
    - -

    sovrn

    -
    -

    No response

    - -
    - -

    springserve

    -
    -

    No response

    - -
    - -

    triplelift

    -
    -

    No response

    - -
    - -

    yieldbot

    -
    -

    No response

    - -
    - -

    nginad

    -
    -

    No response

    - -
    - -

    brightcom

    -
    -

    No response

    - -
    - -

    sekindo

    -
    -

    No response

    - -
    - -

    kruxlink

    -
    -

    No response

    - -
    - -

    AdMedia

    -
    -

    No response

    - -
    - - - - - - - - - - diff --git a/test/spec/e2e/gpt-examples/e2e_default.html b/test/spec/e2e/gpt-examples/e2e_default.html deleted file mode 100644 index 9e0437d6275..00000000000 --- a/test/spec/e2e/gpt-examples/e2e_default.html +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - -

    Prebid.js TestCase1

    - - -
    - -
    -
    - -
    - - - \ No newline at end of file diff --git a/test/spec/e2e/gpt-examples/gpt_default.html b/test/spec/e2e/gpt-examples/gpt_default.html deleted file mode 100644 index 93ec054b59a..00000000000 --- a/test/spec/e2e/gpt-examples/gpt_default.html +++ /dev/null @@ -1,684 +0,0 @@ - - - - - - - - - - -

    Prebid.js Test3

    - - -
    - -
    - -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - - -
    - -
    - -
    - -
    -
    - -
    -
    - -
    - - - -
    - -
    - - - - - - - - - diff --git a/test/spec/e2e/gpt-examples/gpt_outstream.html b/test/spec/e2e/gpt-examples/gpt_outstream.html deleted file mode 100644 index 42ba48c98e7..00000000000 --- a/test/spec/e2e/gpt-examples/gpt_outstream.html +++ /dev/null @@ -1,250 +0,0 @@ - - - - - Prebid.js outstream video example - - - - - -

    Prebid Outstream Video Ads

    - -
    -

    -In scelerisque sem sed tortor posuere sagittis. Fusce scelerisque odio at tincidunt ultricies. Fusce egestas, erat non finibus dictum, nulla arcu viverra nibh, at bibendum ligula nisi egestas magna. Nulla eu finibus nulla. Pellentesque at mi eget turpis consequat scelerisque. Sed lacinia, nisi sit amet egestas vestibulum, elit odio iaculis leo, et lacinia risus enim non lacus. Cras nec neque eget nunc gravida maximus. Ut hendrerit convallis sollicitudin. Donec cursus erat vel metus gravida, et pretium justo iaculis. Curabitur condimentum blandit augue, quis interdum leo. Vivamus dapibus est nec dui efficitur, eu imperdiet nulla sollicitudin. Suspendisse laoreet velit vitae arcu mollis, ac interdum lorem venenatis. Aenean nec purus varius, accumsan ex at, luctus arcu. Quisque consectetur tortor eros, placerat lacinia eros aliquam a. Proin non porttitor libero. -

    -

    -Proin eget vulputate est. Nunc sit amet neque a tortor ullamcorper suscipit non eu neque. Quisque at massa in metus feugiat rutrum. Nulla et orci orci. Aliquam erat volutpat. Cras tincidunt metus lectus, sed suscipit augue mollis vitae. Sed quis condimentum tortor, sit amet consectetur erat. Nulla pellentesque turpis lacus, eu venenatis massa fringilla at. Duis sed pharetra turpis. Maecenas vel porttitor neque. Praesent quis felis sapien. Donec suscipit euismod dui, vitae fermentum nisi ornare in. -

    -

    -Suspendisse tempor felis accumsan orci finibus, imperdiet mollis arcu imperdiet. In eu dolor condimentum, pulvinar nisl a, sollicitudin nunc. Ut vel lectus libero. Praesent rhoncus leo tortor, at mollis nulla sagittis eget. Quisque tempus tempor augue sed rutrum. Sed vitae volutpat quam. Proin vestibulum eros metus, a luctus erat condimentum eu. Vivamus ullamcorper ultricies dui, ac malesuada leo finibus semper. Cras diam augue, imperdiet sed efficitur id, aliquam sed purus. Praesent eget turpis quis sapien interdum sagittis. Vivamus placerat nunc a tempus fermentum. Praesent laoreet leo at tellus porta, ut viverra tortor pharetra. Quisque elit velit, eleifend eget imperdiet vel, suscipit ac nisi. Aliquam egestas mauris ut massa fringilla laoreet. -

    -

    -Quisque ac luctus nisi, vitae ornare arcu. Proin fermentum sapien vitae odio vestibulum porta. Suspendisse faucibus sapien enim, et faucibus urna tempus et. Integer porttitor justo sed faucibus blandit. Morbi semper lectus vitae semper facilisis. Quisque molestie accumsan arcu, eget bibendum dui euismod et. Sed in mattis lacus, nec lacinia sem. Fusce sed tortor posuere, iaculis justo varius, elementum est. -

    -

    -Etiam condimentum, eros commodo semper tristique, lorem leo pharetra massa, eget cursus justo enim id urna. Sed imperdiet mauris vitae ante bibendum elementum. Etiam eu dui porttitor leo imperdiet cursus. Maecenas consequat, neque a dapibus viverra, nunc velit volutpat nibh, ut cursus sem tortor ac arcu. Praesent convallis lacus vel nisi aliquam, in posuere libero scelerisque. Curabitur et lacinia nisl. Nunc id ligula neque. Phasellus non eros et leo ultrices ultricies. Nulla facilisi. Donec ut augue urna. Suspendisse sodales nisi at ex faucibus, et tempus magna fermentum. Proin non arcu interdum, pulvinar est at, vehicula odio. Morbi nec maximus sem. Ut eu tristique urna. -

    -

    -Pellentesque eget quam sem. Nam interdum eleifend leo, mattis sagittis metus ornare tristique. Cras pretium odio lectus, vitae viverra massa consequat eget. Suspendisse porttitor pretium lectus in scelerisque. Phasellus euismod porta lectus eget pharetra. Ut et viverra mi, ut imperdiet lacus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nunc tempus sapien sit amet tortor rhoncus dignissim. Sed at augue et sem lacinia feugiat. Nulla vitae convallis urna. Morbi scelerisque erat quis nibh pretium, non elementum elit consectetur. Proin in feugiat nisl. -

    -

    -Morbi et ipsum purus. Integer ut pulvinar metus. Fusce maximus ex nec purus sollicitudin gravida. Vivamus dapibus volutpat erat nec tristique. Aliquam mi dolor, pretium non elementum quis, viverra non est. Pellentesque egestas, lectus a posuere imperdiet, nisi sem elementum neque, eu volutpat arcu turpis venenatis magna. Curabitur non neque consectetur, vulputate urna sed, vestibulum lacus. Aenean mollis, risus non pulvinar egestas, lectus lectus finibus dui, sit amet pretium metus mauris vitae nibh. In non ultricies odio. -

    -

    -Donec dictum sem ac risus molestie lobortis. Maecenas at justo vehicula, iaculis orci eget, eleifend nunc. In non justo imperdiet, blandit leo in, interdum mi. Proin feugiat libero et erat dictum efficitur. Nunc auctor lacus feugiat erat euismod cursus. Sed vehicula ante vel quam pretium blandit. Maecenas congue quis mauris vitae efficitur. Cras sit amet justo at sem dictum ornare vitae eu ex. Nunc ornare odio nec leo consectetur cursus. Mauris eu dolor tellus. Etiam dignissim ut nunc et mollis. Cras at pulvinar velit, ut tincidunt velit. Cras vitae fermentum ante. Aenean interdum dolor in scelerisque consectetur. -

    -

    -Curabitur auctor leo sit amet massa faucibus ultrices. Maecenas dignissim libero ac cursus cursus. Curabitur eget sapien leo. Phasellus pretium blandit facilisis. Proin egestas urna a sagittis tempus. Donec in nibh ex. Vestibulum efficitur felis aliquam urna ultrices, at gravida nibh pretium. Morbi dictum vulputate pretium. Donec at nisi rutrum, pharetra nunc a, placerat felis. Quisque rhoncus congue fermentum. Quisque pharetra est at nisl sagittis suscipit. Maecenas scelerisque porta eleifend. Mauris nulla leo, consectetur at eros vel, elementum pretium diam. -

    -

    -In nisi libero, porta ut ullamcorper a, dapibus nec velit. Vestibulum congue rhoncus congue. Nulla a libero sit amet risus feugiat hendrerit id placerat ex. In hac habitasse platea dictumst. Pellentesque ut ullamcorper risus. Nunc et ipsum nisi. Vivamus a interdum diam, hendrerit pellentesque orci. -

    -

    -Vestibulum ut massa blandit, maximus sem vitae, vulputate mauris. Nam condimentum velit a facilisis dignissim. Nunc venenatis pharetra dapibus. Praesent ullamcorper risus sit amet molestie consectetur. Cras mauris felis, consequat et enim a, ultricies pretium enim. Nulla porttitor nunc mi, sed posuere magna venenatis non. Donec lobortis consectetur mauris, fermentum auctor dui dignissim sed. Sed vel venenatis urna. Donec velit velit, imperdiet non vulputate non, eleifend sed nisi. -

    -

    -Proin et turpis velit. Donec tempus dictum dolor, eget eleifend lacus. Donec eu felis in ante iaculis ultrices. Mauris varius, turpis quis venenatis convallis, enim dolor ornare justo, in dictum ipsum purus quis dolor. Ut condimentum feugiat lectus ut auctor. Maecenas luctus consequat erat, nec pretium urna pulvinar in. Donec gravida rhoncus aliquet. Cras aliquet odio eget orci hendrerit, non posuere velit dignissim. Nunc tempus aliquam iaculis. Nunc viverra lobortis mauris et malesuada. Donec congue suscipit mauris. Phasellus efficitur, leo at mollis maximus, lorem mauris pretium urna, nec scelerisque ante neque eu erat. Nam rhoncus malesuada velit nec ultricies. -

    -
    -

    Prebid Outstream Video Ad

    - -
    - -

    -Proin blandit in arcu sed porttitor. Morbi in erat vel risus mollis interdum. Proin vel odio semper, porttitor risus sed, tristique odio. Donec viverra massa et dui scelerisque, ac sagittis odio viverra. Aliquam lacinia enim sit amet dapibus ultrices. Nulla mollis, massa eget interdum egestas, lectus eros pretium eros, vel consectetur velit odio vel odio. Proin euismod aliquam finibus. Phasellus facilisis mollis est, non consequat lectus volutpat nec. -

    -

    -Ut vel ultricies erat. Pellentesque non ipsum quis odio ornare tempus in cursus ex. In a turpis non quam pulvinar tincidunt. Maecenas tortor neque, dapibus a quam aliquet, dictum pellentesque leo. Sed aliquet tellus est, in tempus magna congue ut. Phasellus at tincidunt lorem, id fringilla risus. Nunc vel pulvinar massa. Aliquam erat volutpat. Phasellus semper interdum justo at eleifend. Curabitur feugiat quam sed mollis facilisis. -

    -

    -Quisque consectetur sem a elit aliquet facilisis. Quisque dignissim velit at quam rhoncus dignissim. Proin feugiat sem at turpis ultrices imperdiet. Integer vel eros vel ante ultricies dapibus vitae eget dui. Fusce sollicitudin semper tortor at molestie. Pellentesque nec metus sed mauris aliquet iaculis. Mauris malesuada tortor nec mi dictum, feugiat euismod enim gravida. Vestibulum dapibus ut nulla vel euismod. Nunc lobortis, mauris at pretium faucibus, ligula diam venenatis nulla, in mattis sapien arcu feugiat dolor. In at dui leo. Cras elementum condimentum turpis. -

    -

    -Donec eget dolor ac nulla lobortis bibendum. Praesent commodo accumsan ligula eget commodo. Suspendisse sit amet dignissim metus. Sed ut eros viverra, viverra lectus eget, ornare eros. Mauris elementum lacinia dapibus. Donec magna nisl, suscipit quis mattis eget, tincidunt sed sapien. Curabitur elementum nulla eget lorem gravida dapibus. Nunc vel dolor et libero pretium interdum vitae eget mauris. Vestibulum et erat in nulla sollicitudin luctus ut quis nulla. -

    -

    -Maecenas iaculis pellentesque quam at fringilla. Donec dui elit, suscipit eget varius id, suscipit efficitur metus. Nulla rutrum ultrices tempor. Vivamus hendrerit justo ac fermentum euismod. Vestibulum tempus pulvinar tempus. Curabitur congue neque luctus dolor vehicula, non efficitur metus fringilla. Nam a imperdiet ex. Integer at est hendrerit, rutrum justo eu, ornare odio. Etiam convallis sapien a purus vehicula, eget gravida purus semper. Fusce ex enim, volutpat ac feugiat et, rhoncus vel tortor. Aenean ultrices libero sed neque fermentum tempor. Morbi tincidunt dui turpis, non mollis est dignissim at. Suspendisse molestie convallis interdum. Donec sit amet fermentum purus. -

    -

    -Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam et ex orci. Vivamus rutrum est vel porta imperdiet. Cras ultricies tortor dolor, nec mollis felis ullamcorper vel. Praesent scelerisque vehicula sem, nec feugiat mauris tempus ac. Donec at enim non sem commodo sodales. Ut sit amet risus sit amet ante viverra venenatis. Aliquam sodales mollis est eget ultricies. Etiam pulvinar sapien et ipsum elementum pharetra. -

    -

    -Nam blandit metus erat, sit amet congue ipsum cursus sed. In a commodo ante, sit amet tincidunt quam. Aenean lobortis et nibh in venenatis. Aliquam faucibus purus quis neque consectetur, quis consequat risus maximus. Proin mollis imperdiet felis, eget tempus ipsum scelerisque ut. Sed euismod interdum augue sed varius. Sed volutpat tellus ut risus porta accumsan. -

    -

    -Mauris aliquet eu arcu sed pharetra. Duis nec leo volutpat libero finibus malesuada in eget velit. Aliquam facilisis urna mauris, et aliquam ipsum dictum finibus. Donec eget mi fermentum, vehicula odio at, molestie orci. In a hendrerit lectus. Aenean congue ipsum ac imperdiet suscipit. Maecenas eleifend pretium metus id mollis. -

    -
    -

    Prebid Outstream Video Ad

    - -
    - -

    -Ut vel ultricies erat. Pellentesque non ipsum quis odio ornare tempus in cursus ex. In a turpis non quam pulvinar tincidunt. Maecenas tortor neque, dapibus a quam aliquet, dictum pellentesque leo. Sed aliquet tellus est, in tempus magna congue ut. Phasellus at tincidunt lorem, id fringilla risus. Nunc vel pulvinar massa. Aliquam erat volutpat. Phasellus semper interdum justo at eleifend. Curabitur feugiat quam sed mollis facilisis. -

    -

    -Quisque consectetur sem a elit aliquet facilisis. Quisque dignissim velit at quam rhoncus dignissim. Proin feugiat sem at turpis ultrices imperdiet. Integer vel eros vel ante ultricies dapibus vitae eget dui. Fusce sollicitudin semper tortor at molestie. Pellentesque nec metus sed mauris aliquet iaculis. Mauris malesuada tortor nec mi dictum, feugiat euismod enim gravida. Vestibulum dapibus ut nulla vel euismod. Nunc lobortis, mauris at pretium faucibus, ligula diam venenatis nulla, in mattis sapien arcu feugiat dolor. In at dui leo. Cras elementum condimentum turpis. -

    -

    -Donec eget dolor ac nulla lobortis bibendum. Praesent commodo accumsan ligula eget commodo. Suspendisse sit amet dignissim metus. Sed ut eros viverra, viverra lectus eget, ornare eros. Mauris elementum lacinia dapibus. Donec magna nisl, suscipit quis mattis eget, tincidunt sed sapien. Curabitur elementum nulla eget lorem gravida dapibus. Nunc vel dolor et libero pretium interdum vitae eget mauris. Vestibulum et erat in nulla sollicitudin luctus ut quis nulla. -

    -

    -Maecenas iaculis pellentesque quam at fringilla. Donec dui elit, suscipit eget varius id, suscipit efficitur metus. Nulla rutrum ultrices tempor. Vivamus hendrerit justo ac fermentum euismod. Vestibulum tempus pulvinar tempus. Curabitur congue neque luctus dolor vehicula, non efficitur metus fringilla. Nam a imperdiet ex. Integer at est hendrerit, rutrum justo eu, ornare odio. Etiam convallis sapien a purus vehicula, eget gravida purus semper. Fusce ex enim, volutpat ac feugiat et, rhoncus vel tortor. Aenean ultrices libero sed neque fermentum tempor. Morbi tincidunt dui turpis, non mollis est dignissim at. Suspendisse molestie convallis interdum. Donec sit amet fermentum purus. -

    - -
    - - - diff --git a/test/spec/e2e/gpt-examples/gpt_yieldbot.html b/test/spec/e2e/gpt-examples/gpt_yieldbot.html deleted file mode 100644 index 12766c537f6..00000000000 --- a/test/spec/e2e/gpt-examples/gpt_yieldbot.html +++ /dev/null @@ -1,241 +0,0 @@ - - - - - - - - - - -
    -
    - Yieldbot integration test mode: - -
      -
    • START (i.e.force bids to be returned)
    • -
    • STOP
    • -
    -
    - -

    Prebid.js Yieldbot Adapter Test

    -
    -
    - -
    -

    Lorem ipsum dolor. Sit amet proin. Integer cursus mi mus curabitur euismod vel quos duis bibendum nec interdum porta dolor a viverra nisl fusce. Volutpat sit at. Donec nisl taciti. Eget eu lobortis. Excepteur diam orci lacus nibh pharetra. Justo neque maecenas. Viverra molestie dolor ante rutrum vivamus libero urna suscipit leo praesent ultricies. In dignissim qui ante bibendum in. Habitasse ac arcu non nulla augue. Felis lectus non tempus in aliquam. Sit porttitor nec. Sodales non sit eu duis.

    -
    - -
    -

    Donec feugiat ornare a amet optio. Vitae sit sapien. Vitae nec justo. Fusce ac in semper ligula duis eget vel sit. Augue mauris sit. A adipisicing orci est augue dapibus ullamcorper faucibus fermentum. Et phasellus in tempus vivamus praesent. Nisl dui porttitor. Iaculis vulputate eros ut interdum eu. Lacus quis magna varius in quis. Congue erat porttitor sit eu vitae pharetra scelerisque nec. Dolor dui vel ut velit vestibulum. Lectus ullamcorper mi. Curabitur ipsum pellentesque sed erat est est sapien in tempor sodales viverra. Dui volutpat morbi eleifend fringilla quis. Neque erat erat. Rhoncus sed posuere. Dapibus fusce ut lacus mus est pede sed quisquam. Quis aliquam pellentesque. Wisi ac odio eu wisi amet ut ipsum a erat aliquam nunc.

    -

    Dis vitae penatibus. Mollis mauris bibendum. Porta orci amet dolor sed felis in neque per cursus molestie pellentesque. Quis accusamus vel sed dapibus orci congue ut eros. Nec faucibus inceptos. Hendrerit in eget nulla tellus lacinia. Fusce ut ut mattis.

    -

    Vivamus mauris metus. Ridiculus habitant lorem in nulla in quam eros ut. Libero aliquam platea. In enim consectetuer eget mattis accumsan aenean faucibus tincidunt. Amet donec vitae wisi pellentesque magna non lacinia qui. In erat in maecenas amet dui. Aliquam elit vel. Ligula sodales lacus. Nisl a purus. Pharetra velit porttitor vel vel non turpis viverra fringilla lorem arcu pellentesque sed aliquet nonummy quisque dapibus ullamcorper. Mollis ipsum nulla. Tempor tempus vitae. Luctus amet vel. Suspendisse sagittis vestibulum fusce eu.

    -

    Urna et felis bibendum felis sit vestibulum wisi pharetra quisque ac quis cursus suspendisse quisque aenean luctus curabitur. Eget nec leo. Mi placerat cras nulla et integer eget in sed. Non magni parturient. Egestas iaculis malesuada. Nec a duis. Pede condimentum ullamcorper. Augue arcu tellus. In velit in in duis odio dictum wisi proin quis eget sit. Felis tempus inceptos. Turpis risus eu. Mi vivamus consequat. Lectus dui imperdiet amet orci vehicula in vel pellentesque habitant suscipit aliquam. Proin porttitor vitae ultricies a in. Est duis pede. Tristique velit vestibulum odio sodales morbi magna ut vitae. Elementum imperdiet sodales ultrices tortor mollis vehicula lorem varius pellentesque mi ut sit turpis feugiat. In convallis urna. Justo aliquam sed quis scelerisque nonummy. Lobortis rhoncus ornare. Pellentesque leo quam at beatae in. Erat lorem tempus. Molestie faucibus a id mauris montes. Sed orci vulputate. Libero justo curabitur. Amet orci ante. Non felis est erat cras purus id at id nibh nunc facilisis amet metus sagittis pellentesque eros amet. Vitae sit nulla vitae wisi diam. Tincidunt ipsum eleifend semper tortor non. Tellus amet aliquet.

    -

    Dis vitae penatibus. Mollis mauris bibendum. Porta orci amet dolor sed felis in neque per cursus molestie pellentesque. Quis accusamus vel sed dapibus orci congue ut eros. Nec faucibus inceptos. Hendrerit in eget nulla tellus lacinia. Fusce ut ut mattis.

    -

    Vivamus mauris metus. Ridiculus habitant lorem in nulla in quam eros ut. Libero aliquam platea. In enim consectetuer eget mattis accumsan aenean faucibus tincidunt. Amet donec vitae wisi pellentesque magna non lacinia qui. In erat in maecenas amet dui. Aliquam elit vel. Ligula sodales lacus. Nisl a purus. Pharetra velit porttitor vel vel non turpis viverra fringilla lorem arcu pellentesque sed aliquet nonummy quisque dapibus ullamcorper. Mollis ipsum nulla. Tempor tempus vitae. Luctus amet vel. Suspendisse sagittis vestibulum fusce eu.

    -

    Urna et felis bibendum felis sit vestibulum wisi pharetra quisque ac quis cursus suspendisse quisque aenean luctus curabitur. Eget nec leo. Mi placerat cras nulla et integer eget in sed. Non magni parturient. Egestas iaculis malesuada. Nec a duis. Pede condimentum ullamcorper. Augue arcu tellus. In velit in in duis odio dictum wisi proin quis eget sit. Felis tempus inceptos. Turpis risus eu. Mi vivamus consequat. Lectus dui imperdiet amet orci vehicula in vel pellentesque habitant suscipit aliquam. Proin porttitor vitae ultricies a in. Est duis pede. Tristique velit vestibulum odio sodales morbi magna ut vitae. Elementum imperdiet sodales ultrices tortor mollis vehicula lorem varius pellentesque mi ut sit turpis feugiat. In convallis urna. Justo aliquam sed quis scelerisque nonummy. Lobortis rhoncus ornare. Pellentesque leo quam at beatae in. Erat lorem tempus. Molestie faucibus a id mauris montes. Sed orci vulputate. Libero justo curabitur. Amet orci ante. Non felis est erat cras purus id at id nibh nunc facilisis amet metus sagittis pellentesque eros amet. Vitae sit nulla vitae wisi diam. Tincidunt ipsum eleifend semper tortor non. Tellus amet aliquet.

    -
    -
    - -
    -
    - -
    -
    -

    Dis vitae penatibus. Mollis mauris bibendum. Porta orci amet dolor sed felis in neque per cursus molestie pellentesque. Quis accusamus vel sed dapibus orci congue ut eros. Nec faucibus inceptos. Hendrerit in eget nulla tellus lacinia. Fusce ut ut mattis.

    -
    - - diff --git a/test/spec/e2e/testcase1/dom-group/allbidders_dom_spec.js b/test/spec/e2e/testcase1/dom-group/allbidders_dom_spec.js deleted file mode 100644 index 5803ad5ceb3..00000000000 --- a/test/spec/e2e/testcase1/dom-group/allbidders_dom_spec.js +++ /dev/null @@ -1,125 +0,0 @@ -// var verify = require('verify'); -var util = require('../../common/utils.js'); - -module.exports = { - 'adequant ad rendering': function (browser) { - browser - .url('http://an.localhost:9999/test/spec/e2e/gpt-examples/all_bidders_instant_load.html') - .waitForElementVisible('body', 5000) - .pause(7000) - .execute(util.findIframeInDiv, ['div-1'], function(result) { - this.verify.equal(result.value, true, 'adequant ad not rendered'); - }); - }, - 'adform ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-2'], function(result) { - this.verify.equal(result.value, true, 'adform ad not rendered'); - }); - }, - 'aol ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-3'], function(result) { - this.verify.equal(result.value, true, 'aol ad not rendered'); - }); - }, - 'appnexus ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-4'], function(result) { - this.verify.equal(result.value, true, 'appnexus ad not rendered'); - }); - }, - 'indexExchange ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-5'], function(result) { - this.verify.equal(result.value, true, 'indexExchange ad not rendered'); - }); - }, - 'openx ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-6'], function(result) { - this.verify.equal(result.value, true, 'openx ad not rendered'); - }); - }, - 'pubmatic ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-7'], function(result) { - this.verify.equal(result.value, true, 'pubmatic ad not rendered'); - }); - }, - 'pulsepoint ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-8'], function(result) { - this.verify.equal(result.value, true, 'pulsepoint ad not rendered'); - }); - }, - 'rubicon ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-9'], function(result) { - this.verify.equal(result.value, true, 'rubicon ad not rendered'); - }); - }, - 'sonobi ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-10'], function(result) { - this.verify.equal(result.value, true, 'sonobi ad not rendered'); - }); - }, - 'sovrn ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-11'], function(result) { - this.verify.equal(result.value, true, 'sovrn ad not rendered'); - }); - }, - 'springserve ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-12'], function(result) { - this.verify.equal(result.value, true, 'springserve ad not rendered'); - }); - }, - 'triplelift ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-13'], function(result) { - this.verify.equal(result.value, true, 'triplelift ad not rendered'); - }); - }, - 'yieldbot ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-14'], function(result) { - this.verify.equal(result.value, true, 'yieldbot ad not rendered'); - }); - }, - 'nginad ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-15'], function(result) { - this.verify.equal(result.value, true, 'nginad ad not rendered'); - }); - }, - 'brightcom ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-16'], function(result) { - this.verify.equal(result.value, true, 'brightcom ad not rendered'); - }); - }, - 'sekindo ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-17'], function(result) { - this.verify.equal(result.value, true, 'sekindo ad not rendered'); - }); - }, - 'kruxlink ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-18'], function(result) { - this.verify.equal(result.value, true, 'kruxlink ad not rendered'); - }); - }, - 'AdMedia ad rendering': function (browser) { - browser - .execute(util.findIframeInDiv, ['div-19'], function(result) { - this.verify.equal(result.value, true, 'AdMedia ad not rendered'); - }); - }, - after: function(browser) { - browser.end(); - } -}; diff --git a/test/spec/e2e/testcase1/dom-group/dom_spec.js b/test/spec/e2e/testcase1/dom-group/dom_spec.js deleted file mode 100644 index a58030c32b7..00000000000 --- a/test/spec/e2e/testcase1/dom-group/dom_spec.js +++ /dev/null @@ -1,51 +0,0 @@ -// var assert = require('assert'); - -module.exports = { - - 'Test rendering ad div-2': function (browser) { - var checkAdRendering2 = function() { - var div = document.getElementById('div-2'); - var iframes = div.getElementsByTagName('iframe'); - try { - if (iframes.length == 1 && iframes[0].contentWindow.document.body.innerHTML == '') { - return false; - } else { - return true; - } - } catch (e) { - return true; - } - } - - browser - .url('http://an.localhost:9999/test/spec/e2e/gpt-examples/e2e_default.html') - .waitForElementVisible('body', 3000) - .pause(3000) - .execute(checkAdRendering2, [], function(result) { - this.assert.equal(result.value, true, 'Ad of div-2 not rendered'); - }); - }, - 'Test rendering ad div-1': function (browser) { - var checkAdRendering = function() { - var div = document.getElementById('div-1'); - var iframes = div.getElementsByTagName('iframe'); - try { - if (iframes.length == 1 && iframes[0].contentWindow.document.body.innerHTML == '') { - return false; - } else { - return true; - } - } catch (e) { - return true; - } - } - - browser - .execute(checkAdRendering, [], function(result) { - this.assert.equal(result.value, true, 'Ad of div-1 not rendered'); - }); - }, - after: function(browser) { - browser.end(); - } -}; diff --git a/test/spec/e2e/testcase1/pbjsapi-group/adservertargeting_spec.js b/test/spec/e2e/testcase1/pbjsapi-group/adservertargeting_spec.js deleted file mode 100644 index 796707641cd..00000000000 --- a/test/spec/e2e/testcase1/pbjsapi-group/adservertargeting_spec.js +++ /dev/null @@ -1,62 +0,0 @@ -var assert = require('assert'); -var utils = require('util'); - -module.exports = { - 'AdserverTargeting Test Case 1': function (browser) { - browser - .url('http://localhost:9999/test/spec/e2e/gpt-examples/gpt_default.html') - .waitForElementVisible('body', 3000) - .pause(3000) - .execute(function() { - if (typeof window.pbjs.bidderSettings === 'undefined') { - var pbjsBidderSettingsObject = [ - 'hb_bidder', - 'hb_adid', - 'hb_pb', - 'hb_size' - ]; - } else { - var pbjsBidderSettings = window.pbjs.bidderSettings; - var pbjsBidderSettingsObject = {}; - Object.keys(pbjsBidderSettings).forEach(function (prop) { - // if(prop == 'standard') return; - var value = pbjsBidderSettings[prop]; - var bs = value.adserverTargeting.map(function(item) { - return item.key; - }); - pbjsBidderSettings.standard.adserverTargeting.map(function(value) { - if (bs.indexOf(value.key) == -1) { - bs.push(value.key) - } - }); - pbjsBidderSettingsObject[prop] = bs; - }); - } - - var adserverTargetingObject = {}; - var adserverTargeting = window.pbjs.getAdserverTargeting(); - Object.keys(adserverTargeting).forEach(function(value) { - if (Object.keys(adserverTargeting[value]).length == 0) return; - adserverTargetingObject[adserverTargeting[value].hb_bidder] = Object.keys(adserverTargeting[value]) - }); - - return [pbjsBidderSettingsObject, adserverTargetingObject]; - }, [], function(result) { - Object.keys(result.value[1]).forEach(function(key) { - if (utils.isArray(result.value[0])) { - assert.deepEqual(result.value[0].sort(), result.value[1][key].sort()); - } else { - if (result.value[0].hasOwnProperty(key)) { - var obj1 = result.value[0][key].sort(); - } else { - var obj1 = result.value[0]['standard'].sort(); - } - assert.deepEqual(obj1, result.value[1][key].sort()); - } - }); - }); - }, - after: function(browser) { - browser.end(); - } -}; diff --git a/test/spec/e2e/testcase1/pbjsapi-group/getbidresponses_spec.js b/test/spec/e2e/testcase1/pbjsapi-group/getbidresponses_spec.js deleted file mode 100644 index c7921709c0f..00000000000 --- a/test/spec/e2e/testcase1/pbjsapi-group/getbidresponses_spec.js +++ /dev/null @@ -1,34 +0,0 @@ -// var assert = require('assert'); -var assert = require('chai').assert; -var utils = require('util'); - -module.exports = { - 'bidReceived not empty': function(browser) { - browser - .url('http://localhost:9999/test/spec/e2e/gpt-examples/gpt_default.html') - .waitForElementVisible('body', 3000) - .pause(5000) - .execute(function() { - return window.pbjs._bidsReceived.length; - }, [], function(result) { - // browser.assert.first(false, 'Bid response empty'); - assert.isOk(result.value, 'Bid response empty'); - }); - }, - 'check keys': function(browser) { - browser - .execute(function() { - return window.pbjs._bidsReceived; - }, [], function(result) { - // minimum expected keys in bid received - var expected = ['bidderCode', 'width', 'height', 'adId', 'cpm', 'requestId', 'bidder', 'adUnitCode', 'timeToRespond']; - Object.keys(result.value).forEach(function(key) { - var compare = Object.keys(result.value[key]); - assert.includeMembers(compare, expected, 'include members'); - }); - }); - }, - after: function(browser) { - browser.end(); - } -}; diff --git a/test/spec/lfe2e/specs/basic_w_custom_adserver_translation.spec.js b/test/spec/lfe2e/specs/basic_w_custom_adserver_translation.spec.js new file mode 100644 index 00000000000..82310738246 --- /dev/null +++ b/test/spec/lfe2e/specs/basic_w_custom_adserver_translation.spec.js @@ -0,0 +1,68 @@ +const includes = require('core-js/library/fn/array/includes'); +const expect = require('chai').expect; +const testServer = require('../../../helpers/testing-utils'); + +const host = testServer.host; +const protocol = testServer.protocol; + +const validDurations = ['15s', '30s']; +const validCats = ['Food', 'Retail Stores/Chains', 'Pet Food/Supplies', 'Travel/Hotels/Airlines', 'Automotive', 'Health Care Services']; +const validCpms = ['15.00', '14.00', '13.00', '10.00']; +const customKeyRegex = /\d{2}\.\d{2}_\d{1,3}_\d{2}s/; +const uuidRegex = /(\d|\w){8}-((\d|\w){4}-){3}(\d|\w){12}/; + +describe('longform ads using custom adserver translation file', function() { + this.retries(3); + it('process the bids successfully', function() { + browser + .url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_w_custom_adserver_translation.html?pbjs_debug=true') + .pause(10000); + + const loadPrebidBtnXpath = '//*[@id="loadPrebidRequestBtn"]'; + browser.waitForExist(loadPrebidBtnXpath); + $(loadPrebidBtnXpath).click(); + browser.pause(3000); + + const listOfCpmsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[2]'; + const listOfCategoriesXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[3]'; + const listOfDurationsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[4]'; + + browser.waitForExist(listOfCpmsXpath); + + let listOfCpms = $$(listOfCpmsXpath); + let listOfCats = $$(listOfCategoriesXpath); + let listOfDuras = $$(listOfDurationsXpath); + + expect(listOfCpms.length).to.equal(listOfCats.length).and.to.equal(listOfDuras.length); + for (let i = 0; i < listOfCpms.length; i++) { + let cpm = listOfCpms[i].getText(); + let cat = listOfCats[i].getText(); + let dura = listOfDuras[i].getText(); + expect(includes(validCpms, cpm), `Could not find CPM ${cpm} in accepted list`).to.equal(true); + expect(includes(validCats, cat), `Could not find Category ${cat} in accepted list`).to.equal(true); + expect(includes(validDurations, dura), `Could not find Duration ${dura} in accepted list`).to.equal(true); + } + }); + + it('formats the targeting keys properly', function () { + const listOfKeyElementsXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[1]'; + const listOfKeyValuesXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[2]'; + browser.waitForExist(listOfKeyElementsXpath); + browser.waitForExist(listOfKeyValuesXpath); + + let listOfKeyElements = $$(listOfKeyElementsXpath); + let listOfKeyValues = $$(listOfKeyValuesXpath); + + let firstKey = listOfKeyElements[0].getText(); + expect(firstKey).to.equal('hb_pb_cat_dur'); + + let firstKeyValue = listOfKeyValues[0].getText(); + expect(firstKeyValue).match(customKeyRegex); + + let lastKey = listOfKeyElements[listOfKeyElements.length - 1].getText(); + expect(lastKey).to.equal('hb_cache_id'); + + let lastKeyValue = listOfKeyValues[listOfKeyValues.length - 1].getText(); + expect(lastKeyValue).to.match(uuidRegex); + }); +}) diff --git a/test/spec/lfe2e/specs/basic_w_requireExactDuration.spec.js b/test/spec/lfe2e/specs/basic_w_requireExactDuration.spec.js new file mode 100644 index 00000000000..224ff1cbc34 --- /dev/null +++ b/test/spec/lfe2e/specs/basic_w_requireExactDuration.spec.js @@ -0,0 +1,68 @@ +const includes = require('core-js/library/fn/array/includes'); +const expect = require('chai').expect; +const testServer = require('../../../helpers/testing-utils'); + +const host = testServer.host; +const protocol = testServer.protocol; + +const validDurations = ['15s', '30s']; +const validCats = ['Food', 'Retail Stores/Chains', 'Pet Food/Supplies', 'Travel/Hotels/Airlines', 'Automotive', 'Health Care Services']; +const validCpms = ['15.00', '14.00', '13.00', '10.00']; +const customKeyRegex = /\d{2}\.\d{2}_\d{1,3}_\d{2}s/; +const uuidRegex = /(\d|\w){8}-((\d|\w){4}-){3}(\d|\w){12}/; + +describe('longform ads using requireExactDuration field', function() { + this.retries(3); + it('process the bids successfully', function() { + browser + .url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_w_requireExactDuration.html?pbjs_debug=true') + .pause(10000); + + const loadPrebidBtnXpath = '//*[@id="loadPrebidRequestBtn"]'; + browser.waitForExist(loadPrebidBtnXpath); + $(loadPrebidBtnXpath).click(); + browser.pause(3000); + + const listOfCpmsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[2]'; + const listOfCategoriesXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[3]'; + const listOfDurationsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[4]'; + + browser.waitForExist(listOfCpmsXpath); + + let listOfCpms = $$(listOfCpmsXpath); + let listOfCats = $$(listOfCategoriesXpath); + let listOfDuras = $$(listOfDurationsXpath); + + expect(listOfCpms.length).to.equal(listOfCats.length).and.to.equal(listOfDuras.length); + for (let i = 0; i < listOfCpms.length; i++) { + let cpm = listOfCpms[i].getText(); + let cat = listOfCats[i].getText(); + let dura = listOfDuras[i].getText(); + expect(includes(validCpms, cpm), `Could not find CPM ${cpm} in accepted list`).to.equal(true); + expect(includes(validCats, cat), `Could not find Category ${cat} in accepted list`).to.equal(true); + expect(includes(validDurations, dura), `Could not find Duration ${dura} in accepted list`).to.equal(true); + } + }); + + it('formats the targeting keys properly', function () { + const listOfKeyElementsXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[1]'; + const listOfKeyValuesXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[2]'; + browser.waitForExist(listOfKeyElementsXpath); + browser.waitForExist(listOfKeyValuesXpath); + + let listOfKeyElements = $$(listOfKeyElementsXpath); + let listOfKeyValues = $$(listOfKeyValuesXpath); + + let firstKey = listOfKeyElements[0].getText(); + expect(firstKey).to.equal('hb_pb_cat_dur'); + + let firstKeyValue = listOfKeyValues[0].getText(); + expect(firstKeyValue).match(customKeyRegex); + + let lastKey = listOfKeyElements[listOfKeyElements.length - 1].getText(); + expect(lastKey).to.equal('hb_cache_id'); + + let lastKeyValue = listOfKeyValues[listOfKeyValues.length - 1].getText(); + expect(lastKeyValue).to.match(uuidRegex); + }); +}) diff --git a/test/spec/lfe2e/specs/basic_wo_brandCategoryExclusion.spec.js b/test/spec/lfe2e/specs/basic_wo_brandCategoryExclusion.spec.js new file mode 100644 index 00000000000..95237366d0e --- /dev/null +++ b/test/spec/lfe2e/specs/basic_wo_brandCategoryExclusion.spec.js @@ -0,0 +1,63 @@ +const includes = require('core-js/library/fn/array/includes'); +const expect = require('chai').expect; +const testServer = require('../../../helpers/testing-utils'); + +const host = testServer.host; +const protocol = testServer.protocol; + +const validDurations = ['15s', '30s']; +const validCpms = ['15.00', '14.00', '13.00', '10.00']; +const customKeyRegex = /\d{2}\.\d{2}_\d{2}s/; +const uuidRegex = /(\d|\w){8}-((\d|\w){4}-){3}(\d|\w){12}/; + +describe('longform ads without using brandCategoryExclusion', function() { + this.retries(3); + it('process the bids successfully', function() { + browser + .url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_wo_brandCategoryExclusion.html?pbjs_debug=true') + .pause(10000); + + const loadPrebidBtnXpath = '//*[@id="loadPrebidRequestBtn"]'; + browser.waitForExist(loadPrebidBtnXpath); + $(loadPrebidBtnXpath).click(); + browser.pause(3000); + + const listOfCpmsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[2]'; + const listOfDurationsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[4]'; + + browser.waitForExist(listOfCpmsXpath); + + let listOfCpms = $$(listOfCpmsXpath); + let listOfDuras = $$(listOfDurationsXpath); + + expect(listOfCpms.length).to.equal(listOfDuras.length); + for (let i = 0; i < listOfCpms.length; i++) { + let cpm = listOfCpms[i].getText(); + let dura = listOfDuras[i].getText(); + expect(includes(validCpms, cpm), `Could not find CPM ${cpm} in accepted list`).to.equal(true); + expect(includes(validDurations, dura), `Could not find Duration ${dura} in accepted list`).to.equal(true); + } + }); + + it('formats the targeting keys properly', function () { + const listOfKeyElementsXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[1]'; + const listOfKeyValuesXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[2]'; + browser.waitForExist(listOfKeyElementsXpath); + browser.waitForExist(listOfKeyValuesXpath); + + let listOfKeyElements = $$(listOfKeyElementsXpath); + let listOfKeyValues = $$(listOfKeyValuesXpath); + + let firstKey = listOfKeyElements[0].getText(); + expect(firstKey).to.equal('hb_pb_cat_dur'); + + let firstKeyValue = listOfKeyValues[0].getText(); + expect(firstKeyValue).match(customKeyRegex); + + let lastKey = listOfKeyElements[listOfKeyElements.length - 1].getText(); + expect(lastKey).to.equal('hb_cache_id'); + + let lastKeyValue = listOfKeyValues[listOfKeyValues.length - 1].getText(); + expect(lastKeyValue).to.match(uuidRegex); + }); +}) diff --git a/test/spec/lfe2e/specs/basic_wo_requireExactDuration.spec.js b/test/spec/lfe2e/specs/basic_wo_requireExactDuration.spec.js new file mode 100644 index 00000000000..6b628067138 --- /dev/null +++ b/test/spec/lfe2e/specs/basic_wo_requireExactDuration.spec.js @@ -0,0 +1,68 @@ +const includes = require('core-js/library/fn/array/includes'); +const expect = require('chai').expect; +const testServer = require('../../../helpers/testing-utils'); + +const host = testServer.host; +const protocol = testServer.protocol; + +const validDurations = ['15s', '30s']; +const validCats = ['Food', 'Retail Stores/Chains', 'Pet Food/Supplies', 'Travel/Hotels/Airlines', 'Automotive', 'Health Care Services']; +const validCpms = ['15.00', '14.00', '13.00', '10.00']; +const customKeyRegex = /\d{2}\.\d{2}_\d{1,3}_\d{2}s/; +const uuidRegex = /(\d|\w){8}-((\d|\w){4}-){3}(\d|\w){12}/; + +describe('longform ads not using requireExactDuration field', function() { + this.retries(3); + it('process the bids successfully', function() { + browser + .url(protocol + '://' + host + ':9999/integrationExamples/longform/basic_wo_requireExactDuration.html?pbjs_debug=true') + .pause(10000); + + const loadPrebidBtnXpath = '//*[@id="loadPrebidRequestBtn"]'; + browser.waitForExist(loadPrebidBtnXpath); + $(loadPrebidBtnXpath).click(); + browser.pause(3000); + + const listOfCpmsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[2]'; + const listOfCategoriesXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[3]'; + const listOfDurationsXpath = '/html/body/div[1]/div/div/div/div[1]/div[2]/div/table/tbody/tr/td[4]'; + + browser.waitForExist(listOfCpmsXpath); + + let listOfCpms = $$(listOfCpmsXpath); + let listOfCats = $$(listOfCategoriesXpath); + let listOfDuras = $$(listOfDurationsXpath); + + expect(listOfCpms.length).to.equal(listOfCats.length).and.to.equal(listOfDuras.length); + for (let i = 0; i < listOfCpms.length; i++) { + let cpm = listOfCpms[i].getText(); + let cat = listOfCats[i].getText(); + let dura = listOfDuras[i].getText(); + expect(includes(validCpms, cpm), `Could not find CPM ${cpm} in accepted list`).to.equal(true); + expect(includes(validCats, cat), `Could not find Category ${cat} in accepted list`).to.equal(true); + expect(includes(validDurations, dura), `Could not find Duration ${dura} in accepted list`).to.equal(true); + } + }); + + it('formats the targeting keys properly', function () { + const listOfKeyElementsXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[1]'; + const listOfKeyValuesXpath = '/html/body/div[1]/div/div/div/div[2]/div[2]/div/table/tbody/tr/td[2]'; + browser.waitForExist(listOfKeyElementsXpath); + browser.waitForExist(listOfKeyValuesXpath); + + let listOfKeyElements = $$(listOfKeyElementsXpath); + let listOfKeyValues = $$(listOfKeyValuesXpath); + + let firstKey = listOfKeyElements[0].getText(); + expect(firstKey).to.equal('hb_pb_cat_dur'); + + let firstKeyValue = listOfKeyValues[0].getText(); + expect(firstKeyValue).match(customKeyRegex); + + let lastKey = listOfKeyElements[listOfKeyElements.length - 1].getText(); + expect(lastKey).to.equal('hb_cache_id'); + + let lastKeyValue = listOfKeyValues[listOfKeyValues.length - 1].getText(); + expect(lastKeyValue).to.match(uuidRegex); + }); +}) diff --git a/wdio.conf.js b/wdio.conf.js new file mode 100644 index 00000000000..4e50e68af2e --- /dev/null +++ b/wdio.conf.js @@ -0,0 +1,55 @@ +const browsers = require('./browsers.json'); + +function getCapabilities() { + function getPlatform(os) { + const platformMap = { + 'Windows': 'WINDOWS', + 'OS X': 'MAC', + } + return platformMap[os]; + } + + // remove the IE11 browser from functional tests + delete browsers['bs_ie_11_windows_10']; + + let capabilities = [] + Object.keys(browsers).forEach(key => { + let browser = browsers[key]; + capabilities.push({ + browserName: browser.browser, + platform: getPlatform(browser.os), + version: browser.browser_version, + acceptSslCerts: true, + 'browserstack.networkLogs': true, + 'browserstack.console': 'verbose', + build: 'Prebidjs E2E ' + new Date().toLocaleString() + }); + }); + return capabilities; +} + +exports.config = { + specs: [ + './test/spec/lfe2e/specs/*.js' + ], + services: ['browserstack'], + user: process.env.BROWSERSTACK_USERNAME, + key: process.env.BROWSERSTACK_ACCESS_KEY, + browserstackLocal: true, + // Do not increase this, since we have only 5 parallel tests in browserstack account + maxInstances: 5, + capabilities: getCapabilities(), + logLevel: 'silent', // Level of logging verbosity: silent | verbose | command | data | result | error + coloredLogs: true, + waitforTimeout: 60000, // Default timeout for all waitFor* commands. + connectionRetryTimeout: 60000, // Default timeout in milliseconds for request if Selenium Grid doesn't send response + connectionRetryCount: 3, // Default request retries count + framework: 'mocha', + mochaOpts: { + ui: 'bdd', + timeout: 60000, + compilers: ['js:babel-register'], + }, + // if you see error, update this to spec reporter and logLevel above to get detailed report. + reporters: ['concise'] +}; From 449fc72e3459701d5c1077b04931f025dd53546c Mon Sep 17 00:00:00 2001 From: Jaimin Panchal Date: Tue, 23 Apr 2019 16:56:44 -0400 Subject: [PATCH 023/146] Prebid 2.12.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1e1c130b6fe..a26453b0e67 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.12.0-pre", + "version": "2.12.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From b42627b4b5205d0b11bd92b123abe44363d41060 Mon Sep 17 00:00:00 2001 From: Jaimin Panchal Date: Tue, 23 Apr 2019 17:03:22 -0400 Subject: [PATCH 024/146] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a26453b0e67..1419f99c65f 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.12.0", + "version": "2.13.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 2741f95389d59320e41bd4eaaed5498ebdc074e9 Mon Sep 17 00:00:00 2001 From: aprakash-sovrn Date: Thu, 25 Apr 2019 07:15:29 -0600 Subject: [PATCH 025/146] Sovrn Analytics Adapter (#3761) * Sovrn Analytics Adapter * unit test fix * use relative paths for imports --- modules/sovrnAnalyticsAdapter.js | 277 ++++++++++ modules/sovrnAnalyticsAdapter.md | 23 + .../modules/sovrnAnalyticsAdapter_spec.js | 472 ++++++++++++++++++ 3 files changed, 772 insertions(+) create mode 100644 modules/sovrnAnalyticsAdapter.js create mode 100644 modules/sovrnAnalyticsAdapter.md create mode 100644 test/spec/modules/sovrnAnalyticsAdapter_spec.js diff --git a/modules/sovrnAnalyticsAdapter.js b/modules/sovrnAnalyticsAdapter.js new file mode 100644 index 00000000000..3113f7ff5af --- /dev/null +++ b/modules/sovrnAnalyticsAdapter.js @@ -0,0 +1,277 @@ +import adapter from '../src/AnalyticsAdapter' +import adaptermanager from '../src/adapterManager' +import CONSTANTS from '../src/constants.json' +import {ajaxBuilder} from '../src/ajax' +import * as utils from '../src/utils' +import {config} from '../src/config' +import find from 'core-js/library/fn/array/find' +import includes from 'core-js/library/fn/array/includes' + +const ajax = ajaxBuilder(0) + +const { + EVENTS: { + AUCTION_END, + BID_REQUESTED, + BID_ADJUSTMENT, + BID_RESPONSE, + BID_WON + } +} = CONSTANTS + +let pbaUrl = 'https://pba.aws.lijit.com/analytics' +let currentAuctions = {}; +const analyticsType = 'endpoint' + +let sovrnAnalyticsAdapter = Object.assign(adapter({url: pbaUrl, analyticsType}), { + track({ eventType, args }) { + try { + if (eventType === BID_WON) { + new BidWinner(this.sovrnId, args).send(); + return + } + if (args && args.auctionId && currentAuctions[args.auctionId] && currentAuctions[args.auctionId].status === 'complete') { + throw new Error('Event Received after Auction Close Auction Id ' + args.auctionId) + } + if (args && args.auctionId && currentAuctions[args.auctionId] === undefined) { + currentAuctions[args.auctionId] = new AuctionData(this.sovrnId, args.auctionId) + } + switch (eventType) { + case BID_REQUESTED: + currentAuctions[args.auctionId].bidRequested(args) + break + case BID_ADJUSTMENT: + currentAuctions[args.auctionId].originalBid(args) + break + case BID_RESPONSE: + currentAuctions[args.auctionId].adjustedBid(args) + break + case AUCTION_END: + currentAuctions[args.auctionId].send(); + break + } + } catch (e) { + new LogError(e, this.sovrnId, {eventType, args}).send() + } + }, +}) + +sovrnAnalyticsAdapter.getAuctions = function () { + return currentAuctions; +}; + +sovrnAnalyticsAdapter.originEnableAnalytics = sovrnAnalyticsAdapter.enableAnalytics; + +// override enableAnalytics so we can get access to the config passed in from the page +sovrnAnalyticsAdapter.enableAnalytics = function (config) { + let sovrnId = '' + if (config && config.options && (config.options.sovrnId || config.options.affiliateId)) { + sovrnId = config.options.sovrnId || config.options.affiliateId; + } else { + utils.logError('Need Sovrn Id to log auction results. Please contact a Sovrn representative if you do not know your Sovrn Id.') + return + } + sovrnAnalyticsAdapter.sovrnId = sovrnId; + if (config.options.pbaUrl) { + pbaUrl = config.options.pbaUrl; + } + sovrnAnalyticsAdapter.originEnableAnalytics(config) // call the base class function +}; + +adaptermanager.registerAnalyticsAdapter({ + adapter: sovrnAnalyticsAdapter, + code: 'sovrn' +}); + +/** Class Representing a Winning Bid */ +class BidWinner { + /** + * Creates a new bid winner + * @param {string} sovrnId - the affiliate id from the analytics config + * @param {*} event - the args object from the auction event + */ + constructor(sovrnId, event) { + this.body = {} + this.body.prebidVersion = $$REPO_AND_VERSION$$ + this.body.sovrnId = sovrnId + this.body.winningBid = JSON.parse(JSON.stringify(event)) + this.body.url = utils.getTopWindowLocation().href + this.body.payload = 'winner' + delete this.body.winningBid.ad + } + + /** + * Sends the auction to the the ingest server + */ + send() { + this.body.ts = utils.timestamp() + ajax( + pbaUrl, + null, + JSON.stringify(this.body), + { + contentType: 'application/json', + method: 'POST', + } + ) + } +} + +/** Class representing an Auction */ +class AuctionData { + /** + * Create a new auction data collector + * @param {string} sovrnId - the affiliate id from the analytics config + * @param {string} auctionId - the auction id from the auction event + */ + constructor(sovrnId, auctionId) { + this.auction = {} + this.auction.prebidVersion = $$REPO_AND_VERSION$$ + this.auction.sovrnId = sovrnId + this.auction.auctionId = auctionId + this.auction.payload = 'auction' + this.auction.timeouts = { + buffer: config.getConfig('timeoutBuffer'), + bidder: config.getConfig('bidderTimeout'), + } + this.auction.priceGranularity = config.getConfig('priceGranularity') + this.auction.url = utils.getTopWindowLocation().href + this.auction.requests = [] + this.auction.unsynced = [] + this.dropBidFields = ['auctionId', 'ad', 'requestId', 'bidderCode'] + + setTimeout(function(id) { + delete currentAuctions[id] + }, 300000, this.auction.auctionId) + } + + /** + * Record a bid request event + * @param {*} event - the args object from the auction event + */ + bidRequested(event) { + const eventCopy = JSON.parse(JSON.stringify(event)) + delete eventCopy.doneCbCallCount + delete eventCopy.auctionId + this.auction.requests.push(eventCopy) + } + + /** + * Finds the bid from the auction that the event is associated with + * @param {*} event - the args object from the auction event + * @return {*} - the bid + */ + findBid(event) { + const bidder = find(this.auction.requests, r => (r.bidderCode === event.bidderCode)) + if (!bidder) { + this.auction.unsynced.push(JSON.parse(JSON.stringify(event))) + } + let bid = find(bidder.bids, b => (b.bidId === event.requestId)) + + if (!bid) { + event.unmatched = true + bidder.bids.push(JSON.parse(JSON.stringify(event))) + } + return bid + } + + /** + * Records the original bid before any adjustments have been made + * @param {*} event - the args object from the auction event + * NOTE: the bid adjustment occurs before the bid response + * the bid adjustment seems to be the bid ready to be adjusted + */ + originalBid(event) { + let bid = this.findBid(event) + if (bid) { + Object.assign(bid, JSON.parse(JSON.stringify(event))) + this.dropBidFields.forEach((f) => delete bid[f]) + } + } + + /** + * Replaces original values with adjusted values and records the original values for changed values + * in bid.originalValues + * @param {*} event - the args object from the auction event + */ + adjustedBid(event) { + let bid = this.findBid(event) + if (bid) { + bid.originalValues = Object.keys(event).reduce((o, k) => { + if (JSON.stringify(bid[k]) !== JSON.stringify(event[k]) && !includes(this.dropBidFields, k)) { + o[k] = bid[k] + bid[k] = event[k] + } + return o + }, {}) + } + } + + /** + * Sends the auction to the the ingest server + */ + send() { + let maxbid = {cpm: 0} + this.auction.requests.forEach(request => { + request.bids.forEach(bid => { + if (bid.cpm > maxbid.cpm) { + maxbid = bid + } + }) + }) + maxbid.isAuctionWinner = true + this.auction.ts = utils.timestamp() + ajax( + pbaUrl, + () => { + currentAuctions[this.auction.auctionId] = {status: 'complete', auctionId: this.auction.auctionId} + }, + JSON.stringify(this.auction), + { + contentType: 'application/json', + method: 'POST', + } + ) + } +} +class LogError { + constructor(e, sovrnId, data) { + this.error = {} + this.error.payload = 'error' + this.error.message = e.message + this.error.stack = e.stack + this.error.data = data + this.error.prebidVersion = $$REPO_AND_VERSION$$ + this.error.sovrnId = sovrnId + this.error.url = utils.getTopWindowLocation().href + this.error.userAgent = navigator.userAgent + } + send() { + if (this.error.data && this.error.data.requests) { + this.error.data.requests.forEach(request => { + if (request.bids) { + request.bids.forEach(bid => { + if (bid.ad) { + delete bid.ad + } + }) + } + }) + } + if (ErrorEvent.data && error.data.ad) { + delete error.data.ad + } + this.error.ts = utils.timestamp() + ajax( + pbaUrl, + null, + JSON.stringify(this.error), + { + contentType: 'application/json', + method: 'POST', + } + ) + } +} + +export default sovrnAnalyticsAdapter; diff --git a/modules/sovrnAnalyticsAdapter.md b/modules/sovrnAnalyticsAdapter.md new file mode 100644 index 00000000000..80bc6d7f6b1 --- /dev/null +++ b/modules/sovrnAnalyticsAdapter.md @@ -0,0 +1,23 @@ +# Overview + +``` +Module Name: Sovrn Analytics Adapter +Module Type: Analytics Adapter +Maintainer: jrosendahl@sovrn.com +``` + +# Description + +Sovrn's analytics adaptor allows you to view detailed auction information in Meridian. + +For more information, visit Sovrn.com. + +# Test Parameters +``` +{ + provider: 'sovrn', + options: { + sovrnId: 'xxxxx', // Sovrn ID (required) you can get this by contacting Sovrn support. + } +} +``` diff --git a/test/spec/modules/sovrnAnalyticsAdapter_spec.js b/test/spec/modules/sovrnAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..404833f0177 --- /dev/null +++ b/test/spec/modules/sovrnAnalyticsAdapter_spec.js @@ -0,0 +1,472 @@ +import sovrnAnalyticsAdapter from '../../../modules/sovrnAnalyticsAdapter' +import { expect } from 'chai' +import {config} from 'src/config' +import adaptermanager from 'src/adapterManager' +var assert = require('assert'); + +let events = require('src/events'); +let constants = require('src/constants.json'); + +/** + * Emit analytics events + * @param {array} eventArr - array of objects to define the events that will fire + * @param {object} eventObj - key is eventType, value is event + * @param {string} auctionId - the auction id to attached to the events + */ +function emitEvent(eventType, event, auctionId) { + event.auctionId = auctionId; + events.emit(constants.EVENTS[eventType], event); +} + +let auctionStartTimestamp = Date.now(); +let timeout = 3000; +let auctionInit = { + timestamp: auctionStartTimestamp, + timeout: timeout +}; +let bidderCode = 'sovrn'; +let bidderRequestId = '123bri'; +let adUnitCode = 'div'; +let bidId = 'bidid'; +let tId = '7aafa3ee-a80a-46d7-a4a0-cbcba463d97a'; +let bidRequested = { + auctionStart: auctionStartTimestamp, + bidderCode: bidderCode, + bidderRequestId: bidderRequestId, + bids: [ + { + adUnitCode: adUnitCode, + bidId: bidId, + bidder: bidderCode, + bidderRequestId: '10340af0c7dc72', + sizes: [[300, 250]], + startTime: auctionStartTimestamp + 100, + transactionId: tId + } + ], + doneCbCallCount: 1, + start: auctionStartTimestamp, + timeout: timeout +}; +let bidResponse = { + bidderCode: bidderCode, + width: 300, + height: 250, + statusMessage: 'Bid available', + adId: '3870e27a5752fb', + mediaType: 'banner', + source: 'client', + requestId: bidId, + cpm: 0.8584999918937682, + creativeId: 'cridprebidrtb', + dealId: null, + currency: 'USD', + netRevenue: true, + ad: '
    divvy mcdiv
    ', + ttl: 60000, + responseTimestamp: auctionStartTimestamp + 150, + requestTimestamp: auctionStartTimestamp + 100, + bidder: bidderCode, + adUnitCode: adUnitCode, + timeToRespond: 50, + pbLg: '0.50', + pbMg: '0.80', + pbHg: '0.85', + pbAg: '0.85', + pbDg: '0.85', + pbCg: '', + size: '300x250', + adserverTargeting: { + hb_bidder: bidderCode, + hb_adid: '3870e27a5752fb', + hb_pb: '0.85' + }, + status: 'rendered' +}; +let bidAdjustment = {}; +for (var k in bidResponse) bidAdjustment[k] = bidResponse[k]; +bidAdjustment.cpm = 0.8; +let bidAdjustmentNoMatchingRequest = { + bidderCode: 'not-sovrn', + width: 300, + height: 250, + statusMessage: 'Bid available', + adId: '1', + mediaType: 'banner', + source: 'client', + requestId: '1', + cpm: 0.10, + creativeId: '', + dealId: null, + currency: 'USD', + netRevenue: true, + ad: '
    divvy mcdiv
    ', + ttl: 60000, + responseTimestamp: auctionStartTimestamp + 150, + requestTimestamp: auctionStartTimestamp + 100, + bidder: 'not-sovrn', + adUnitCode: '', + timeToRespond: 50, + pbLg: '0.00', + pbMg: '0.10', + pbHg: '0.10', + pbAg: '0.10', + pbDg: '0.10', + pbCg: '', + size: '300x250', + adserverTargeting: { + hb_bidder: 'not-sovrn', + hb_adid: '1', + hb_pb: '0.10' + }, +}; +let bidResponseNoMatchingRequest = bidAdjustmentNoMatchingRequest; + +describe('Sovrn Analytics Adapter', function () { + let xhr; + let requests; + beforeEach(() => { + xhr = sinon.useFakeXMLHttpRequest(); + xhr.onCreate = request => requests.push(request); + requests = []; + sinon.stub(events, 'getEvents').returns([]); + }); + afterEach(() => { + xhr.restore(); + events.getEvents.restore(); + }); + + describe('enableAnalytics ', function () { + beforeEach(() => { + sinon.spy(sovrnAnalyticsAdapter, 'track'); + }); + afterEach(() => { + sovrnAnalyticsAdapter.disableAnalytics(); + sovrnAnalyticsAdapter.track.restore(); + }); + + it('should catch all events if affiliate id present', function () { + adaptermanager.enableAnalytics({ + provider: 'sovrn', + options: { + sovrnId: 123 + } + }); + + events.emit(constants.EVENTS.AUCTION_INIT, {}); + events.emit(constants.EVENTS.AUCTION_END, {}); + events.emit(constants.EVENTS.BID_REQUESTED, {}); + events.emit(constants.EVENTS.BID_RESPONSE, {}); + events.emit(constants.EVENTS.BID_WON, {}); + + sinon.assert.callCount(sovrnAnalyticsAdapter.track, 5); + }); + + it('should catch no events if no affiliate id', function () { + adaptermanager.enableAnalytics({ + provider: 'sovrn', + options: { + } + }); + + events.emit(constants.EVENTS.AUCTION_INIT, {}); + events.emit(constants.EVENTS.AUCTION_END, {}); + events.emit(constants.EVENTS.BID_REQUESTED, {}); + events.emit(constants.EVENTS.BID_RESPONSE, {}); + events.emit(constants.EVENTS.BID_WON, {}); + + sinon.assert.callCount(sovrnAnalyticsAdapter.track, 0); + }); + }); + + describe('sovrnAnalyticsAdapter ', function() { + beforeEach(() => { + sovrnAnalyticsAdapter.enableAnalytics({ + provider: 'sovrn', + options: { + sovrnId: 123 + } + }); + sinon.spy(sovrnAnalyticsAdapter, 'track'); + }); + afterEach(() => { + sovrnAnalyticsAdapter.disableAnalytics(); + sovrnAnalyticsAdapter.track.restore(); + }); + it('should have correct type', function () { + assert.equal(sovrnAnalyticsAdapter.getAdapterType(), 'endpoint') + }) + }); + + describe('auction data collector ', function() { + beforeEach(() => { + sovrnAnalyticsAdapter.enableAnalytics({ + provider: 'sovrn', + options: { + sovrnId: 123 + } + }); + sinon.spy(sovrnAnalyticsAdapter, 'track'); + }); + afterEach(() => { + sovrnAnalyticsAdapter.disableAnalytics(); + sovrnAnalyticsAdapter.track.restore(); + }); + it('should create auctiondata record from init ', function () { + let auctionId = '123.123.123.123'; + emitEvent('AUCTION_INIT', auctionInit, auctionId); + + let auctionData = sovrnAnalyticsAdapter.getAuctions(); + let currentAuction = auctionData[auctionId]; + assert(currentAuction); + let expectedTimeOutData = { + buffer: config.getConfig('timeoutBuffer'), + bidder: config.getConfig('bidderTimeout'), + }; + expect(currentAuction.auction.timeouts).to.deep.equal(expectedTimeOutData); + assert.equal(currentAuction.auction.payload, 'auction'); + assert.equal(currentAuction.auction.priceGranularity, config.getConfig('priceGranularity')) + assert.equal(currentAuction.auction.auctionId, auctionId); + assert.equal(currentAuction.auction.sovrnId, 123); + }); + it('should create a bidrequest object ', function() { + let auctionId = '234.234.234.234'; + emitEvent('AUCTION_INIT', auctionInit, auctionId); + emitEvent('BID_REQUESTED', bidRequested, auctionId); + + let auctionData = sovrnAnalyticsAdapter.getAuctions(); + let currentAuction = auctionData[auctionId]; + assert(currentAuction); + let requests = currentAuction.auction.requests; + assert(requests); + assert.equal(requests.length, 1); + assert.equal(requests[0].bidderCode, bidderCode); + assert.equal(requests[0].bidderRequestId, bidderRequestId); + assert.equal(requests[0].timeout, timeout); + let bids = requests[0].bids; + assert(bids); + assert.equal(bids.length, 1); + assert.equal(bids[0].bidId, bidId); + assert.equal(bids[0].bidder, bidderCode); + assert.equal(bids[0].transactionId, tId); + assert.equal(bids[0].sizes.length, 1); + assert.equal(bids[0].sizes[0][0], 300); + assert.equal(bids[0].sizes[0][1], 250); + expect(requests[0]).to.not.have.property('doneCbCallCount'); + expect(requests[0]).to.not.have.property('auctionId'); + }); + it('should add results to the bid with response ', function () { + let auctionId = '345.345.345.345'; + emitEvent('AUCTION_INIT', auctionInit, auctionId); + emitEvent('BID_REQUESTED', bidRequested, auctionId); + emitEvent('BID_RESPONSE', bidResponse, auctionId); + + let auctionData = sovrnAnalyticsAdapter.getAuctions(); + let currentAuction = auctionData[auctionId]; + let returnedBid = currentAuction.auction.requests[0].bids[0]; + assert.equal(returnedBid.bidId, bidId); + assert.equal(returnedBid.bidder, bidderCode); + assert.equal(returnedBid.transactionId, tId); + assert.equal(returnedBid.sizes.length, 1); + assert.equal(returnedBid.sizes[0][0], 300); + assert.equal(returnedBid.sizes[0][1], 250); + assert.equal(returnedBid.adserverTargeting.hb_adid, '3870e27a5752fb'); + assert.equal(returnedBid.adserverTargeting.hb_bidder, bidderCode); + assert.equal(returnedBid.adserverTargeting.hb_pb, '0.85'); + assert.equal(returnedBid.cpm, 0.8584999918937682); + }); + it('should add new unsynced bid if no request exists for response ', function () { + let auctionId = '456.456.456.456'; + emitEvent('AUCTION_INIT', auctionInit, auctionId); + emitEvent('BID_REQUESTED', bidRequested, auctionId); + emitEvent('BID_RESPONSE', bidResponseNoMatchingRequest, auctionId); + + let auctionData = sovrnAnalyticsAdapter.getAuctions(); + let currentAuction = auctionData[auctionId]; + let requests = currentAuction.auction.requests; + assert(requests); + assert.equal(requests.length, 1); + let bidRequest = requests[0].bids[0]; + expect(bidRequest).to.not.have.property('adserverTargeting'); + expect(bidRequest).to.not.have.property('cpm'); + expect(currentAuction.auction.unsynced[0]).to.deep.equal(bidResponseNoMatchingRequest); + }); + it('should adjust the bid ', function () { + let auctionId = '567.567.567.567'; + emitEvent('AUCTION_INIT', auctionInit, auctionId); + emitEvent('BID_REQUESTED', bidRequested, auctionId); + emitEvent('BID_ADJUSTMENT', bidResponse, auctionId); + emitEvent('BID_RESPONSE', bidAdjustment, auctionId); + + let auctionData = sovrnAnalyticsAdapter.getAuctions(); + let currentAuction = auctionData[auctionId]; + let returnedBid = currentAuction.auction.requests[0].bids[0]; + assert.equal(returnedBid.cpm, 0.8); + assert.equal(returnedBid.originalValues.cpm, 0.8584999918937682); + }); + }); + describe('auction data send ', function() { + let expectedPostBody = { + sovrnId: 123, + auctionId: '678.678.678.678', + payload: 'auction', + priceGranularity: 'medium', + }; + let expectedRequests = { + bidderCode: 'sovrn', + bidderRequestId: '123bri', + timeout: 3000 + }; + let expectedBids = { + adUnitCode: 'div', + bidId: 'bidid', + bidder: 'sovrn', + bidderRequestId: '10340af0c7dc72', + transactionId: '7aafa3ee-a80a-46d7-a4a0-cbcba463d97a', + width: 300, + height: 250, + statusMessage: 'Bid available', + adId: '3870e27a5752fb', + mediaType: 'banner', + source: 'client', + cpm: 0.8584999918937682, + creativeId: 'cridprebidrtb', + dealId: null, + currency: 'USD', + netRevenue: true, + ttl: 60000, + timeToRespond: 50, + size: '300x250', + status: 'rendered', + isAuctionWinner: true + }; + let expectedAdServerTargeting = { + hb_bidder: 'sovrn', + hb_adid: '3870e27a5752fb', + hb_pb: '0.85' + }; + beforeEach(() => { + sovrnAnalyticsAdapter.enableAnalytics({ + provider: 'sovrn', + options: { + sovrnId: 123 + } + }); + sinon.spy(sovrnAnalyticsAdapter, 'track'); + }); + afterEach(() => { + sovrnAnalyticsAdapter.disableAnalytics(); + sovrnAnalyticsAdapter.track.restore(); + }); + it('should send auction data ', function () { + let auctionId = '678.678.678.678'; + emitEvent('AUCTION_INIT', auctionInit, auctionId); + emitEvent('BID_REQUESTED', bidRequested, auctionId); + emitEvent('BID_RESPONSE', bidResponse, auctionId); + emitEvent('AUCTION_END', {}, auctionId); + let requestBody = JSON.parse(requests[0].requestBody); + let requestsFromRequestBody = requestBody.requests[0]; + let bidsFromRequests = requestsFromRequestBody.bids[0]; + expect(requestBody).to.deep.include(expectedPostBody); + expect(requestBody.timeouts).to.deep.equal({buffer: 400, bidder: 3000}); + expect(requestsFromRequestBody).to.deep.include(expectedRequests); + expect(bidsFromRequests).to.deep.include(expectedBids); + expect(bidsFromRequests.adserverTargeting).to.deep.include(expectedAdServerTargeting); + }); + }); + describe('bid won data send ', function() { + let auctionId = '789.789.789.789'; + let creativeId = 'cridprebidrtb'; + let requestId = 'requestId69'; + let bidWonEvent = { + ad: 'html', + adId: 'adId', + adUnitCode: adUnitCode, + auctionId: auctionId, + bidder: bidderCode, + bidderCode: bidderCode, + cpm: 1.01, + creativeId: creativeId, + currency: 'USD', + height: 250, + mediaType: 'banner', + requestId: requestId, + size: '300x250', + source: 'client', + status: 'rendered', + statusMessage: 'Bid available', + timeToRespond: 421, + ttl: 60, + width: 300 + }; + let expectedBidWonBody = { + sovrnId: 123, + payload: 'winner' + }; + let expectedWinningBid = { + bidderCode: bidderCode, + width: 300, + height: 250, + statusMessage: 'Bid available', + adId: 'adId', + mediaType: 'banner', + source: 'client', + requestId: requestId, + cpm: 1.01, + creativeId: creativeId, + currency: 'USD', + ttl: 60, + auctionId: auctionId, + bidder: bidderCode, + adUnitCode: adUnitCode, + timeToRespond: 421, + size: '300x250', + }; + beforeEach(() => { + sovrnAnalyticsAdapter.enableAnalytics({ + provider: 'sovrn', + options: { + sovrnId: 123 + } + }); + sinon.spy(sovrnAnalyticsAdapter, 'track'); + }); + afterEach(() => { + sovrnAnalyticsAdapter.disableAnalytics(); + sovrnAnalyticsAdapter.track.restore(); + }); + it('should send bid won data ', function () { + emitEvent('AUCTION_INIT', auctionInit, auctionId); + emitEvent('BID_WON', bidWonEvent, auctionId); + let requestBody = JSON.parse(requests[0].requestBody); + expect(requestBody).to.deep.include(expectedBidWonBody); + expect(requestBody.winningBid).to.deep.include(expectedWinningBid); + }); + }); + describe('Error Tracking', function() { + beforeEach(() => { + sovrnAnalyticsAdapter.enableAnalytics({ + provider: 'sovrn', + options: { + sovrnId: 123 + } + }); + sinon.spy(sovrnAnalyticsAdapter, 'track'); + }); + afterEach(() => { + sovrnAnalyticsAdapter.disableAnalytics() + sovrnAnalyticsAdapter.track.restore() + }); + it('should send an error message when a bid is received for a closed auction', function() { + let auctionId = '678.678.678.678'; + emitEvent('AUCTION_INIT', auctionInit, auctionId) + emitEvent('BID_REQUESTED', bidRequested, auctionId) + emitEvent('AUCTION_END', {}, auctionId) + requests[0].respond(200) + emitEvent('BID_RESPONSE', bidResponse, auctionId) + let requestBody = JSON.parse(requests[1].requestBody) + expect(requestBody.payload).to.equal('error') + expect(requestBody.message).to.include('Event Received after Auction Close Auction Id') + }) + }) +}) From f1aeb85671b527e654ace19ee559872fdeeb2576 Mon Sep 17 00:00:00 2001 From: Michael Kuryshev Date: Thu, 25 Apr 2019 15:31:26 +0200 Subject: [PATCH 026/146] Update VIS.X bid adapter (#3777) * update VIS.X bid adapter to support identical uids in the parameters * added wrapperType and wrapperVersion parameters in the ad request for VIS.X bid adapter * added second iteration of the response processing to ignore requested sizes in VIS.X bid adapter --- modules/visxBidAdapter.js | 76 ++++++- test/spec/modules/visxBidAdapter_spec.js | 272 +++++++++++++++++++++-- 2 files changed, 312 insertions(+), 36 deletions(-) diff --git a/modules/visxBidAdapter.js b/modules/visxBidAdapter.js index 574061b6ce3..740c08111bc 100755 --- a/modules/visxBidAdapter.js +++ b/modules/visxBidAdapter.js @@ -25,6 +25,8 @@ export const spec = { buildRequests: function(validBidRequests, bidderRequest) { const auids = []; const bidsMap = {}; + const slotsMapByUid = {}; + const sizeMap = {}; const bids = validBidRequests || []; const currency = config.getConfig(`currency.bidderCurrencyDefault.${BIDDER_CODE}`) || @@ -33,21 +35,46 @@ export const spec = { let reqId; bids.forEach(bid => { - if (!bidsMap[bid.params.uid]) { - bidsMap[bid.params.uid] = [bid]; - auids.push(bid.params.uid); + reqId = bid.bidderRequestId; + const {params: {uid}, adUnitCode} = bid; + auids.push(uid); + const sizesId = utils.parseSizesInput(bid.sizes); + + if (!slotsMapByUid[uid]) { + slotsMapByUid[uid] = {}; + } + const slotsMap = slotsMapByUid[uid]; + if (!slotsMap[adUnitCode]) { + slotsMap[adUnitCode] = {adUnitCode, bids: [bid], parents: []}; } else { - bidsMap[bid.params.uid].push(bid); + slotsMap[adUnitCode].bids.push(bid); } - reqId = bid.bidderRequestId; + const slot = slotsMap[adUnitCode]; + + sizesId.forEach((sizeId) => { + sizeMap[sizeId] = true; + if (!bidsMap[uid]) { + bidsMap[uid] = {}; + } + + if (!bidsMap[uid][sizeId]) { + bidsMap[uid][sizeId] = [slot]; + } else { + bidsMap[uid][sizeId].push(slot); + } + slot.parents.push({parent: bidsMap[uid], key: sizeId, uid}); + }); }); const payload = { u: utils.getTopWindowUrl(), pt: 'net', auids: auids.join(','), + sizes: utils.getKeys(sizeMap).join(','), r: reqId, cur: currency, + wrapperType: 'Prebid_js', + wrapperVersion: '$prebid.version$' }; if (bidderRequest && bidderRequest.gdprConsent) { @@ -69,6 +96,7 @@ export const spec = { interpretResponse: function(serverResponse, bidRequest) { serverResponse = serverResponse && serverResponse.body; const bidResponses = []; + const bidsWithoutSizeMatching = []; const bidsMap = bidRequest.bidsMap; const currency = bidRequest.data.cur; @@ -81,7 +109,10 @@ export const spec = { if (!errorMessage && serverResponse.seatbid) { serverResponse.seatbid.forEach(respItem => { - _addBidResponse(_getBidFromResponse(respItem), bidsMap, currency, bidResponses); + _addBidResponse(_getBidFromResponse(respItem), bidsMap, currency, bidResponses, bidsWithoutSizeMatching); + }); + bidsWithoutSizeMatching.forEach(serverBid => { + _addBidResponse(serverBid, bidsMap, currency, bidResponses); }); } if (errorMessage) utils.logError(errorMessage); @@ -117,7 +148,7 @@ function _getBidFromResponse(respItem) { return respItem && respItem.bid && respItem.bid[0]; } -function _addBidResponse(serverBid, bidsMap, currency, bidResponses) { +function _addBidResponse(serverBid, bidsMap, currency, bidResponses, bidsWithoutSizeMatching) { if (!serverBid) return; let errorMessage; if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); @@ -125,9 +156,14 @@ function _addBidResponse(serverBid, bidsMap, currency, bidResponses) { else { const awaitingBids = bidsMap[serverBid.auid]; if (awaitingBids) { - awaitingBids.forEach(bid => { - const bidResponse = { + const sizeId = bidsWithoutSizeMatching ? `${serverBid.w}x${serverBid.h}` : Object.keys(awaitingBids)[0]; + if (awaitingBids[sizeId]) { + const slot = awaitingBids[sizeId][0]; + + const bid = slot.bids.shift(); + bidResponses.push({ requestId: bid.bidId, + bidderCode: spec.code, cpm: serverBid.price, width: serverBid.w, height: serverBid.h, @@ -137,9 +173,25 @@ function _addBidResponse(serverBid, bidsMap, currency, bidResponses) { ttl: TIME_TO_LIVE, ad: serverBid.adm, dealId: serverBid.dealid - }; - bidResponses.push(bidResponse); - }); + }); + + if (!slot.bids.length) { + slot.parents.forEach(({parent, key, uid}) => { + const index = parent[key].indexOf(slot); + if (index > -1) { + parent[key].splice(index, 1); + } + if (!parent[key].length) { + delete parent[key]; + if (!utils.getKeys(parent).length) { + delete bidsMap[uid]; + } + } + }); + } + } else { + bidsWithoutSizeMatching && bidsWithoutSizeMatching.push(serverBid); + } } else { errorMessage = LOG_ERROR_MESS.noPlacementCode + serverBid.auid; } diff --git a/test/spec/modules/visxBidAdapter_spec.js b/test/spec/modules/visxBidAdapter_spec.js index 93dbe5626c2..aa9b2b553ed 100755 --- a/test/spec/modules/visxBidAdapter_spec.js +++ b/test/spec/modules/visxBidAdapter_spec.js @@ -58,7 +58,7 @@ describe('VisxAdapter', function () { 'uid': '903535' }, 'adUnitCode': 'adunit-code-2', - 'sizes': [[728, 90]], + 'sizes': [[728, 90], [300, 250]], 'bidId': '3150ccb55da321', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', @@ -83,17 +83,19 @@ describe('VisxAdapter', function () { expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); expect(payload).to.have.property('auids', '903535'); + expect(payload).to.have.property('sizes', '300x250,300x600'); expect(payload).to.have.property('r', '22edbae2733bf6'); expect(payload).to.have.property('cur', 'EUR'); }); - it('auids must not be duplicated', function () { + it('sizes must not be duplicated', function () { const request = spec.buildRequests(bidRequests); const payload = request.data; expect(payload).to.be.an('object'); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '903535,903536'); + expect(payload).to.have.property('auids', '903535,903535,903536'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); expect(payload).to.have.property('r', '22edbae2733bf6'); expect(payload).to.have.property('cur', 'EUR'); }); @@ -105,12 +107,12 @@ describe('VisxAdapter', function () { expect(payload).to.be.an('object'); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '903535,903536'); + expect(payload).to.have.property('auids', '903535,903535,903536'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); expect(payload).to.have.property('r', '22edbae2733bf6'); expect(payload).to.have.property('cur', 'EUR'); delete bidRequests[1].params.priceType; }); - it('pt parameter must be "net" if params.priceType === "net"', function () { bidRequests[1].params.priceType = 'net'; const request = spec.buildRequests(bidRequests); @@ -118,7 +120,8 @@ describe('VisxAdapter', function () { expect(payload).to.be.an('object'); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '903535,903536'); + expect(payload).to.have.property('auids', '903535,903535,903536'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); expect(payload).to.have.property('r', '22edbae2733bf6'); expect(payload).to.have.property('cur', 'EUR'); delete bidRequests[1].params.priceType; @@ -131,7 +134,8 @@ describe('VisxAdapter', function () { expect(payload).to.be.an('object'); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '903535,903536'); + expect(payload).to.have.property('auids', '903535,903535,903536'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); expect(payload).to.have.property('r', '22edbae2733bf6'); expect(payload).to.have.property('cur', 'EUR'); delete bidRequests[1].params.priceType; @@ -145,7 +149,8 @@ describe('VisxAdapter', function () { expect(payload).to.be.an('object'); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '903535,903536'); + expect(payload).to.have.property('auids', '903535,903535,903536'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); expect(payload).to.have.property('r', '22edbae2733bf6'); expect(payload).to.have.property('cur', 'JPY'); getConfigStub.restore(); @@ -159,7 +164,8 @@ describe('VisxAdapter', function () { expect(payload).to.be.an('object'); expect(payload).to.have.property('u').that.is.a('string'); expect(payload).to.have.property('pt', 'net'); - expect(payload).to.have.property('auids', '903535,903536'); + expect(payload).to.have.property('auids', '903535,903535,903536'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); expect(payload).to.have.property('r', '22edbae2733bf6'); expect(payload).to.have.property('cur', 'USD'); getConfigStub.restore(); @@ -193,9 +199,10 @@ describe('VisxAdapter', function () { describe('interpretResponse', function () { const responses = [ {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 903535, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 903536, 'h': 90, 'w': 728}], 'seat': '1'}, - {'bid': [{'price': 0, 'auid': 903536, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0, 'adm': '
    test content 4
    ', 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 903536, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
    test content 3
    ', 'auid': 903535, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'price': 0, 'auid': 903537, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0, 'adm': '
    test content 5
    ', 'h': 250, 'w': 300}], 'seat': '1'}, undefined, {'bid': [], 'seat': '1'}, {'seat': '1'}, @@ -225,6 +232,7 @@ describe('VisxAdapter', function () { 'width': 300, 'height': 250, 'ad': '
    test content 1
    ', + 'bidderCode': 'visx', 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, @@ -281,37 +289,40 @@ describe('VisxAdapter', function () { 'width': 300, 'height': 250, 'ad': '
    test content 1
    ', + 'bidderCode': 'visx', 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, }, { - 'requestId': '5703af74d0472a', - 'cpm': 1.15, - 'creativeId': 903535, + 'requestId': '4dff80cc4ee346', + 'cpm': 0.5, + 'creativeId': 903536, 'dealId': undefined, 'width': 300, - 'height': 250, - 'ad': '
    test content 1
    ', + 'height': 600, + 'ad': '
    test content 2
    ', + 'bidderCode': 'visx', 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, }, { - 'requestId': '4dff80cc4ee346', - 'cpm': 0.5, - 'creativeId': 903536, + 'requestId': '5703af74d0472a', + 'cpm': 0.15, + 'creativeId': 903535, 'dealId': undefined, 'width': 728, 'height': 90, - 'ad': '
    test content 2
    ', + 'ad': '
    test content 3
    ', + 'bidderCode': 'visx', 'currency': 'EUR', 'netRevenue': true, 'ttl': 360, } ]; - const result = spec.interpretResponse({'body': {'seatbid': [responses[0], responses[1]]}}, request); + const result = spec.interpretResponse({'body': {'seatbid': responses.slice(0, 3)}}, request); expect(result).to.deep.equal(expectedResponse); }); @@ -340,6 +351,7 @@ describe('VisxAdapter', function () { 'width': 300, 'height': 250, 'ad': '
    test content 1
    ', + 'bidderCode': 'visx', 'currency': 'JPY', 'netRevenue': true, 'ttl': 360, @@ -356,7 +368,7 @@ describe('VisxAdapter', function () { { 'bidder': 'visx', 'params': { - 'uid': '903536' + 'uid': '903537' }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], @@ -388,8 +400,220 @@ describe('VisxAdapter', function () { } ]; const request = spec.buildRequests(bidRequests); - const result = spec.interpretResponse({'body': {'seatbid': responses.slice(2)}}, request); + const result = spec.interpretResponse({'body': {'seatbid': responses.slice(3)}}, request); expect(result.length).to.equal(0); }); + + it('complicated case', function () { + const fullResponse = [ + {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 903535, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 903536, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
    test content 3
    ', 'auid': 903535, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'price': 0.15, 'adm': '
    test content 4
    ', 'auid': 903535, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 5
    ', 'auid': 903536, 'h': 600, 'w': 350}], 'seat': '1'}, + ]; + const bidRequests = [ + { + 'bidder': 'visx', + 'params': { + 'uid': '903535' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '2164be6358b9', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'visx', + 'params': { + 'uid': '903535' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '326bde7fbf69', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'visx', + 'params': { + 'uid': '903536' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '4e111f1b66e4', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'visx', + 'params': { + 'uid': '903535' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '26d6f897b516', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + }, + { + 'bidder': 'visx', + 'params': { + 'uid': '903536' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '1751cd90161', + 'bidderRequestId': '106efe3247', + 'auctionId': '32a1f276cb87cb8', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '2164be6358b9', + 'cpm': 1.15, + 'creativeId': 903535, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 1
    ', + 'bidderCode': 'visx', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '4e111f1b66e4', + 'cpm': 0.5, + 'creativeId': 903536, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'ad': '
    test content 2
    ', + 'bidderCode': 'visx', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '26d6f897b516', + 'cpm': 0.15, + 'creativeId': 903535, + 'dealId': undefined, + 'width': 728, + 'height': 90, + 'ad': '
    test content 3
    ', + 'bidderCode': 'visx', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '326bde7fbf69', + 'cpm': 0.15, + 'creativeId': 903535, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'ad': '
    test content 4
    ', + 'bidderCode': 'visx', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '1751cd90161', + 'cpm': 0.5, + 'creativeId': 903536, + 'dealId': undefined, + 'width': 350, + 'height': 600, + 'ad': '
    test content 5
    ', + 'bidderCode': 'visx', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); + expect(result).to.deep.equal(expectedResponse); + }); + + it('dublicate uids and sizes in one slot', function () { + const fullResponse = [ + {'bid': [{'price': 1.15, 'adm': '
    test content 1
    ', 'auid': 903535, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 0.5, 'adm': '
    test content 2
    ', 'auid': 903535, 'h': 250, 'w': 300}], 'seat': '1'}, + ]; + const bidRequests = [ + { + 'bidder': 'visx', + 'params': { + 'uid': '903535' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '5126e301f4be', + 'bidderRequestId': '171c5405a390', + 'auctionId': '35bcbc0f7e79c', + }, + { + 'bidder': 'visx', + 'params': { + 'uid': '903535' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '57b2ebe70e16', + 'bidderRequestId': '171c5405a390', + 'auctionId': '35bcbc0f7e79c', + }, + { + 'bidder': 'visx', + 'params': { + 'uid': '903535' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '225fcd44b18c', + 'bidderRequestId': '171c5405a390', + 'auctionId': '35bcbc0f7e79c', + } + ]; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '5126e301f4be', + 'cpm': 1.15, + 'creativeId': 903535, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 1
    ', + 'bidderCode': 'visx', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 360, + }, + { + 'requestId': '57b2ebe70e16', + 'cpm': 0.5, + 'creativeId': 903535, + 'dealId': undefined, + 'width': 300, + 'height': 250, + 'ad': '
    test content 2
    ', + 'bidderCode': 'visx', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 360, + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); + expect(result).to.deep.equal(expectedResponse); + }); }); }); From 16b46ae3ff5e6c73356d0d7aaec86357863d5f5b Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Fri, 26 Apr 2019 06:58:12 -0700 Subject: [PATCH 027/146] Debug Unit Test Issue in CircleCI (#3754) * add skip to last test * change skip to describe block * updated tests to be more stable by removing prebid global calls in tests --- test/spec/modules/userId_spec.js | 184 +++++++++++++------------------ 1 file changed, 79 insertions(+), 105 deletions(-) diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 6acab4d2b6c..c7901106538 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -1,16 +1,12 @@ import { init, syncDelay, - submodules, pubCommonIdSubmodule, unifiedIdSubmodule, requestBidsHook } from 'modules/userId'; import {config} from 'src/config'; import * as utils from 'src/utils'; -import * as auctionModule from 'src/auction'; -import {getAdUnits} from 'test/fixtures/fixtures'; -import {registerBidder} from 'src/adapters/bidderFactory'; let assert = require('chai').assert; let expect = require('chai').expect; @@ -22,6 +18,15 @@ describe('User ID', function() { return { name: name, storage: { name: key, type: type, expires: expires } } } + function createAdUnit(code = 'adUnit-code') { + return { + code, + mediaTypes: {banner: {}, native: {}}, + sizes: [[300, 200], [300, 600]], + bids: [{bidder: 'sampleBidder', params: {placementId: 'banner-only-bidder'}}] + }; + } + before(function() { utils.setCookie('_pubcid_optout', '', EXPIRED_COOKIE_DATE); }); @@ -43,8 +48,8 @@ describe('User ID', function() { }); it('Check same cookie behavior', function () { - let adUnits1 = getAdUnits(); - let adUnits2 = getAdUnits(); + let adUnits1 = [createAdUnit()]; + let adUnits2 = [createAdUnit()]; let innerAdUnits1; let innerAdUnits2; @@ -54,23 +59,23 @@ describe('User ID', function() { init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); config.setConfig({ usersync: { syncDelay: 0, userIds: [ createStorageConfig() ] } }); - requestBidsHook((config) => { innerAdUnits1 = config.adUnits }, {adUnits: adUnits1}); + requestBidsHook(config => { innerAdUnits1 = config.adUnits }, {adUnits: adUnits1}); pubcid = utils.getCookie('pubcid'); // cookies is created after requestbidHook - innerAdUnits1.forEach((unit) => { - unit.bids.forEach((bid) => { + innerAdUnits1.forEach(unit => { + unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.pubcid'); expect(bid.userId.pubcid).to.equal(pubcid); }); }); - requestBidsHook((config) => { innerAdUnits2 = config.adUnits }, {adUnits: adUnits2}); + requestBidsHook(config => { innerAdUnits2 = config.adUnits }, {adUnits: adUnits2}); assert.deepEqual(innerAdUnits1, innerAdUnits2); }); it('Check different cookies', function () { - let adUnits1 = getAdUnits(); - let adUnits2 = getAdUnits(); + let adUnits1 = [createAdUnit()]; + let adUnits2 = [createAdUnit()]; let innerAdUnits1; let innerAdUnits2; let pubcid1; @@ -106,7 +111,7 @@ describe('User ID', function() { }); it('Check new cookie', function () { - let adUnits = getAdUnits(); + let adUnits = [createAdUnit()]; let innerAdUnits; init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); @@ -254,66 +259,17 @@ describe('User ID', function() { }); }); - describe('Invoking requestBid', function () { - let storageResetCount = 0; - let createAuctionStub; + describe('Request bids hook appends userId to bid objs in adapters', function() { let adUnits; - let adUnitCodes; - let sampleSpec = { - code: 'sampleBidder', - isBidRequestValid: () => {}, - buildRequest: (reqs) => {}, - interpretResponse: () => {}, - getUserSyncs: () => {} - }; - - beforeEach(function () { - // simulate existing browser cookie values - utils.setCookie('pubcid', `testpubcid${storageResetCount}`, (new Date(Date.now() + 5000).toUTCString())); - utils.setCookie('unifiedid', JSON.stringify({ - 'TDID': `testunifiedid${storageResetCount}` - }), (new Date(Date.now() + 5000).toUTCString())); - - // simulate existing browser local storage values - localStorage.setItem('unifiedid_alt', JSON.stringify({ - 'TDID': `testunifiedid_alt${storageResetCount}` - })); - localStorage.setItem('unifiedid_alt_exp', ''); - - adUnits = [{ - code: 'adUnit-code', - mediaTypes: { - banner: {}, - native: {}, - }, - sizes: [[300, 200], [300, 600]], - bids: [ - {bidder: 'sampleBidder', params: {placementId: 'banner-only-bidder'}} - ] - }]; - adUnitCodes = ['adUnit-code']; - let auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: function() {}, cbTimeout: 1999}); - createAuctionStub = sinon.stub(auctionModule, 'newAuction'); - createAuctionStub.returns(auction); - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); - - registerBidder(sampleSpec); + beforeEach(function() { + adUnits = [createAdUnit()]; }); - afterEach(function () { - storageResetCount++; - - utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); - utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); - localStorage.removeItem('unifiedid_alt'); - localStorage.removeItem('unifiedid_alt_exp'); - auctionModule.newAuction.restore(); - $$PREBID_GLOBAL$$.requestBids.removeAll(); - config.resetConfig(); - }); + it('test hook from pubcommonid cookie', function(done) { + utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 100000).toUTCString())); - it('test hook from pubcommonid cookie', function() { + init(config, [pubCommonIdSubmodule]); config.setConfig({ usersync: { syncDelay: 0, @@ -321,17 +277,20 @@ describe('User ID', function() { } }); - $$PREBID_GLOBAL$$.requestBids({adUnits}); - - adUnits.forEach((unit) => { - unit.bids.forEach((bid) => { - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal(`testpubcid${storageResetCount}`); + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + }); }); - }); + utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); }); - it('test hook from pubcommonid config value object', function() { + it('test hook from pubcommonid config value object', function(done) { + init(config, [pubCommonIdSubmodule]); config.setConfig({ usersync: { syncDelay: 0, @@ -341,34 +300,47 @@ describe('User ID', function() { }]} }); - $$PREBID_GLOBAL$$.requestBids({adUnits}); - - adUnits.forEach((unit) => { - unit.bids.forEach((bid) => { - expect(bid).to.have.deep.nested.property('userId.pubcidvalue'); - expect(bid.userId.pubcidvalue).to.equal('testpubcidvalue'); + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.pubcidvalue'); + expect(bid.userId.pubcidvalue).to.equal('testpubcidvalue'); + }); }); - }); + done(); + }, {adUnits}); }); - it('test hook from pubcommonid html5', function() { + it('test hook from pubcommonid html5', function(done) { + // simulate existing browser local storage values + localStorage.setItem('unifiedid_alt', JSON.stringify({'TDID': 'testunifiedid_alt'})); + localStorage.setItem('unifiedid_alt_exp', ''); + + init(config, [unifiedIdSubmodule]); config.setConfig({ usersync: { syncDelay: 0, userIds: [createStorageConfig('unifiedId', 'unifiedid_alt', 'html5')]} }); - $$PREBID_GLOBAL$$.requestBids({adUnits}); - - adUnits.forEach((unit) => { - unit.bids.forEach((bid) => { - expect(bid).to.have.deep.nested.property('userId.tdid'); - expect(bid.userId.tdid).to.equal(`testunifiedid_alt${storageResetCount}`); + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.tdid'); + expect(bid.userId.tdid).to.equal('testunifiedid_alt'); + }); }); - }); + localStorage.removeItem('unifiedid_alt'); + localStorage.removeItem('unifiedid_alt_exp'); + done(); + }, {adUnits}); }); - it('test hook when both pubCommonId and unifiedId have data to pass', function() { + it('test hook when both pubCommonId and unifiedId have data to pass', function(done) { + utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); + utils.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); + + init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); config.setConfig({ usersync: { syncDelay: 0, @@ -378,19 +350,21 @@ describe('User ID', function() { ]} }); - $$PREBID_GLOBAL$$.requestBids({adUnits}); - - adUnits.forEach((unit) => { - unit.bids.forEach((bid) => { - // verify that the PubCommonId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.pubcid'); - expect(bid.userId.pubcid).to.equal(`testpubcid${storageResetCount}`); - - // also check that UnifiedId id data was copied to bid - expect(bid).to.have.deep.nested.property('userId.tdid'); - expect(bid.userId.tdid).to.equal(`testunifiedid${storageResetCount}`); + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + // verify that the PubCommonId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + // also check that UnifiedId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.tdid'); + expect(bid.userId.tdid).to.equal('testunifiedid'); + }); }); - }); + utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); }); - }); + }) }); From 6ca459d620cf4937dd419718742be0dba0ea78d7 Mon Sep 17 00:00:00 2001 From: Michael Wilson Date: Mon, 29 Apr 2019 10:05:19 -0600 Subject: [PATCH 028/146] fixed gumgums example params in readme (#3779) --- modules/gumgumBidAdapter.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/gumgumBidAdapter.md b/modules/gumgumBidAdapter.md index 57616a90ac2..3abc8a246c9 100644 --- a/modules/gumgumBidAdapter.md +++ b/modules/gumgumBidAdapter.md @@ -21,7 +21,7 @@ var adUnits = [ bidder: 'gumgum', params: { inSlot: '15901', // GumGum Slot ID given to the client, - bidFloor: 0.03 // CPM bid floor + bidfloor: 0.03 // CPM bid floor } } ] @@ -33,7 +33,7 @@ var adUnits = [ bidder: 'gumgum', params: { inScreen: 'dc9d6be1', // GumGum Zone ID given to the client - bidFloor: 0.03 // CPM bid floor + bidfloor: 0.03 // CPM bid floor } } ] From c6069f1f55efa05755f05132d7e3cd6b3610e975 Mon Sep 17 00:00:00 2001 From: ReklamStoreIT <48473631+ReklamStoreIT@users.noreply.github.com> Date: Mon, 29 Apr 2019 20:05:49 +0300 Subject: [PATCH 029/146] ReklamStore Adapter Update (#3784) * ReklamStore Bid Adapter ReklamStore Bid Adapter * Added unit test for user sync Added unit test for user sync * Update bid adapter Update bid adapter * Reklamstore Adapter Update Reklamstore Adapter Update --- modules/reklamstoreBidAdapter.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/reklamstoreBidAdapter.js b/modules/reklamstoreBidAdapter.js index be484822860..49f08ab0473 100644 --- a/modules/reklamstoreBidAdapter.js +++ b/modules/reklamstoreBidAdapter.js @@ -36,7 +36,6 @@ export const spec = { regionId: bid.params.regionId, dt: getDeviceType(), os: getOS(), - dbg: 1, ref: extractDomain(url), _: (new Date().getTime()), mobile_web: 1 @@ -65,7 +64,7 @@ export const spec = { height: bidResponse.h, creativeId: bidResponse.adId || 1, currency: CURRENCY, - netRevenue: false, + netRevenue: true, ttl: TIME_TO_LIVE, ad: bidResponse.ad }); From f350bbab99a29d4e335e4ab45b945c5393904448 Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Mon, 29 Apr 2019 10:06:42 -0700 Subject: [PATCH 030/146] Update PubMatic banner and video examples to use adSlot without (#3786) appended size --- modules/pubmaticBidAdapter.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/pubmaticBidAdapter.md b/modules/pubmaticBidAdapter.md index 0f92e1756a5..16a3b203e20 100644 --- a/modules/pubmaticBidAdapter.md +++ b/modules/pubmaticBidAdapter.md @@ -25,7 +25,7 @@ var adUnits = [ bidder: 'pubmatic', params: { publisherId: '156209', // required - adSlot: 'pubmatic_test2@300x250', // required + adSlot: 'pubmatic_test2', // required pmzoneid: 'zone1, zone11', // optional lat: '40.712775', // optional lon: '-74.005973', // optional @@ -33,7 +33,7 @@ var adUnits = [ kadpageurl: 'www.test.com', // optional gender: 'M', // optional kadfloor: '0.50', // optional - currency: 'AUD' // optional (Value configured only in the 1st adunit will be passed on. < br/> Values if present in subsequent adunits, will be ignored.) + currency: 'AUD', // optional (Value configured only in the 1st adunit will be passed on. < br/> Values if present in subsequent adunits, will be ignored.) dctr: 'key1=123|key2=345', // optional (Value configured only in the 1st adunit will be passed on. < br/> Values if present in subsequent adunits, will be ignored.) bcat: ['IAB1-5', 'IAB1-7'] // Optional: Blocked IAB Categories. (Values from all slots will be combined and only unique values will be passed. An array of strings only. Each category should be a string of a length of more than 3 characters.) } @@ -55,8 +55,8 @@ var adVideoAdUnits = [ bids: [{ bidder: 'pubmatic', params: { - publisherId: '351', // required - adSlot: '1363568@300x250', // required + publisherId: '156209', // required + adSlot: 'pubmatic_video1', // required video: { mimes: ['video/mp4','video/x-flv'], // required skippable: true, // optional From e31b9d8887af8ea73cce3e6a8e6dcb359497ca28 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Mon, 29 Apr 2019 10:11:43 -0700 Subject: [PATCH 031/146] Conversant Adapter - support User ID module (#3533) * added universal id support to bid adapter * added unit test for universal id support * added universal id support to bid adapter * added unit test for universal id support * renamed universalId to userId * removed old code from merge mistake * test for unit test --- modules/conversantBidAdapter.js | 4 +++- test/spec/modules/conversantBidAdapter_spec.js | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/conversantBidAdapter.js b/modules/conversantBidAdapter.js index cb0661c4417..90865493d8d 100644 --- a/modules/conversantBidAdapter.js +++ b/modules/conversantBidAdapter.js @@ -99,7 +99,9 @@ export const spec = { imp.banner = banner; } - if (bid.crumbs && bid.crumbs.pubcid) { + if (bid.userId && bid.userId.pubcid) { + pubcid = bid.userId.pubcid; + } else if (bid.crumbs && bid.crumbs.pubcid) { pubcid = bid.crumbs.pubcid; } diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index 34991252fa8..bfe3c6e8fa1 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -375,6 +375,19 @@ describe('Conversant adapter tests', function() { expect(payload).to.have.deep.nested.property('user.ext.fpc', 12345); }); + it('Verify User ID publisher commond id support', function() { + // clone bidRequests + let requests = utils.deepClone(bidRequests) + + // add pubcid to every entry + requests.forEach((unit) => { + Object.assign(unit, {userId: {pubcid: 67890}}); + }); + // construct http post payload + const payload = spec.buildRequests(requests).data; + expect(payload).to.have.deep.nested.property('user.ext.fpc', 67890); + }); + it('Verify GDPR bid request', function() { // add gdpr info const bidRequest = { From d6eeb3157f2adf58ad41c1dc357f823706612232 Mon Sep 17 00:00:00 2001 From: Samuel Horwitz Date: Tue, 30 Apr 2019 05:55:37 -0400 Subject: [PATCH 032/146] kargo sizes and full bid request object (#3771) --- modules/kargoBidAdapter.js | 10 ++++- test/spec/modules/kargoBidAdapter_spec.js | 52 +++++++++++++++++++++-- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index ecd26f49509..2c602186547 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -17,7 +17,11 @@ export const spec = { const currencyObj = config.getConfig('currency'); const currency = (currencyObj && currencyObj.adServerCurrency) || 'USD'; const bidIds = {}; - utils._each(validBidRequests, bid => bidIds[bid.bidId] = bid.params.placementId); + const bidSizes = {}; + utils._each(validBidRequests, bid => { + bidIds[bid.bidId] = bid.params.placementId; + bidSizes[bid.bidId] = bid.sizes; + }); const transformedParams = Object.assign({}, { timeout: bidderRequest.timeout, currency: currency, @@ -27,7 +31,9 @@ export const spec = { floor: 0, ceil: 20 }, - bidIDs: bidIds + bidIDs: bidIds, + bidSizes: bidSizes, + prebidRawBidRequests: validBidRequests }, spec._getAllMetadata()); const encodedParams = encodeURIComponent(JSON.stringify(transformedParams)); return Object.assign({}, bidderRequest, { diff --git a/test/spec/modules/kargoBidAdapter_spec.js b/test/spec/modules/kargoBidAdapter_spec.js index 28cb386242f..5d53a4e9c95 100644 --- a/test/spec/modules/kargoBidAdapter_spec.js +++ b/test/spec/modules/kargoBidAdapter_spec.js @@ -57,19 +57,22 @@ describe('kargo adapter tests', function () { params: { placementId: 'foo' }, - bidId: 1 + bidId: 1, + sizes: [[320, 50], [300, 250], [300, 600]] }, { params: { placementId: 'bar' }, - bidId: 2 + bidId: 2, + sizes: [[320, 50], [300, 250], [300, 600]] }, { params: { placementId: 'bar' }, - bidId: 3 + bidId: 3, + sizes: [[320, 50], [300, 250], [300, 600]] } ]; }); @@ -185,6 +188,14 @@ describe('kargo adapter tests', function () { setCookie('krg_crb', getInvalidKrgCrbType3OldStyle()); } + function getInvalidKrgCrbType4OldStyle() { + return '%7B%22v%22%3A%22bnVsbA%3D%3D%22%7D'; + } + + function initializeInvalidKrgCrbType4Cookie() { + setCookie('krg_crb', getInvalidKrgCrbType4OldStyle()); + } + function getEmptyKrgCrb() { return 'eyJleHBpcmVUaW1lIjoxNDk3NDQ5MzgyNjY4LCJsYXN0U3luY2VkQXQiOjE0OTczNjI5NzkwMTJ9'; } @@ -216,6 +227,11 @@ describe('kargo adapter tests', function () { 2: 'bar', 3: 'bar' }, + bidSizes: { + 1: [[320, 50], [300, 250], [300, 600]], + 2: [[320, 50], [300, 250], [300, 600]], + 3: [[320, 50], [300, 250], [300, 600]] + }, userIDs: { kargoID: '5f108831-302d-11e7-bf6b-4595acd3bf6c', clientID: '2410d8f2-c111-4811-88a5-7b5e190e475f', @@ -241,6 +257,29 @@ describe('kargo adapter tests', function () { ] }, pageURL: window.location.href, + prebidRawBidRequests: [ + { + bidId: 1, + params: { + placementId: 'foo' + }, + sizes: [[320, 50], [300, 250], [300, 600]] + }, + { + bidId: 2, + params: { + placementId: 'bar' + }, + sizes: [[320, 50], [300, 250], [300, 600]] + }, + { + bidId: 3, + params: { + placementId: 'bar' + }, + sizes: [[320, 50], [300, 250], [300, 600]] + } + ], rawCRB: expectedRawCRBCookie, rawCRBLocalStorage: expectedRawCRB }; @@ -339,6 +378,13 @@ describe('kargo adapter tests', function () { testBuildRequests(getExpectedKrakenParams(true, undefined, null, getInvalidKrgCrbType3OldStyle())); }); + it('handles broken Kargo CRBs where inner JSON is falsey', function() { + initializeKruxUser(); + initializeKruxSegments(); + initializeInvalidKrgCrbType4Cookie(); + testBuildRequests(getExpectedKrakenParams(true, undefined, null, getInvalidKrgCrbType4OldStyle())); + }); + it('handles a non-existant currency object on the config', function() { simulateNoCurrencyObject(); initializeKruxUser(); From 4155553988101b9ad17ef6668599fc71be14ae3c Mon Sep 17 00:00:00 2001 From: Eric Nolte Date: Tue, 30 Apr 2019 05:59:59 -0400 Subject: [PATCH 033/146] fix ref error on yieldmo adapter (#3776) * fix ref error on yieldmo adapter * Delete yarn.lock --- modules/yieldmoBidAdapter.js | 2 +- test/spec/modules/yieldmoBidAdapter_spec.js | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index d904791d29a..b2d13e88c80 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -47,7 +47,7 @@ export const spec = { if (userId) { const pubcid = userId.pubcid; serverRequest.pubcid = pubcid; - } else { + } else if (request.crumbs) { serverRequest.pubcid = request.crumbs.pubcid; } }); diff --git a/test/spec/modules/yieldmoBidAdapter_spec.js b/test/spec/modules/yieldmoBidAdapter_spec.js index 80a9265a5c2..12dd87e1517 100644 --- a/test/spec/modules/yieldmoBidAdapter_spec.js +++ b/test/spec/modules/yieldmoBidAdapter_spec.js @@ -1,6 +1,6 @@ import { expect } from 'chai'; import { spec } from 'modules/yieldmoBidAdapter'; -import {newBidder} from 'src/adapters/bidderFactory'; +import { newBidder } from 'src/adapters/bidderFactory'; import * as utils from 'src/utils'; describe('YieldmoAdapter', function () { @@ -50,6 +50,13 @@ describe('YieldmoAdapter', function () { expect(request.url).to.be.equal(ENDPOINT); }); + it('should not blow up if crumbs is undefined', function () { + let bidArray = [ + { ...bid, crumbs: undefined } + ] + expect(function () { spec.buildRequests(bidArray) }).not.to.throw() + }) + it('should place bid information into the p parameter of data', function () { let placementInfo = spec.buildRequests(bidArray).data.p; expect(placementInfo).to.equal('[{"placement_id":"adunit-code","callback_id":"30b31c1838de1e","sizes":[[300,250],[300,600]]}]'); From 40a4ac6f516f8ecf667e529e5721b864778c452d Mon Sep 17 00:00:00 2001 From: bjorn-lw <32431346+bjorn-lw@users.noreply.github.com> Date: Tue, 30 Apr 2019 15:41:34 +0200 Subject: [PATCH 034/146] Detect ad blocker recovered requests + send dynamic parameters to adapter + minor fixes (#3749) * Livewrapped bid and analytics adapter * Fixed some tests for browser compatibility * Fixed some tests for browser compatibility * Changed analytics adapter code name * Fix double quote in debug message * modified how gdpr is being passed * Added support for Publisher Common ID Module * Corrections for ttr in analytics * ANalytics updates * Auction start time stamp changed * Detect recovered ad blocked requests Make it possible to pass dynamic parameters to adapter --- modules/livewrappedAnalyticsAdapter.js | 31 +++++++--- modules/livewrappedBidAdapter.js | 11 +++- .../livewrappedAnalyticsAdapter_spec.js | 55 +++++++++++++++++- .../modules/livewrappedBidAdapter_spec.js | 58 +++++++++++++++++++ 4 files changed, 143 insertions(+), 12 deletions(-) diff --git a/modules/livewrappedAnalyticsAdapter.js b/modules/livewrappedAnalyticsAdapter.js index 272ccadfbcf..ec0ddb6fd54 100644 --- a/modules/livewrappedAnalyticsAdapter.js +++ b/modules/livewrappedAnalyticsAdapter.js @@ -21,6 +21,7 @@ const cache = { let livewrappedAnalyticsAdapter = Object.assign(adapter({EMPTYURL, ANALYTICSTYPE}), { track({eventType, args}) { + const time = utils.timestamp(); utils.logInfo('LIVEWRAPPED_EVENT:', [eventType, args]); switch (eventType) { @@ -30,16 +31,18 @@ let livewrappedAnalyticsAdapter = Object.assign(adapter({EMPTYURL, ANALYTICSTYPE break; case CONSTANTS.EVENTS.BID_REQUESTED: utils.logInfo('LIVEWRAPPED_BID_REQUESTED:', args); + cache.auctions[args.auctionId].timeStamp = args.start; args.bids.forEach(function(bidRequest) { - cache.auctions[args.auctionId].timeStamp = args.start; cache.auctions[args.auctionId].bids[bidRequest.bidId] = { bidder: bidRequest.bidder, adUnit: bidRequest.adUnitCode, isBid: false, won: false, timeout: false, - sendStatus: 0 + sendStatus: 0, + readyToSend: 0, + start: args.start } utils.logInfo(bidRequest); @@ -49,25 +52,30 @@ let livewrappedAnalyticsAdapter = Object.assign(adapter({EMPTYURL, ANALYTICSTYPE case CONSTANTS.EVENTS.BID_RESPONSE: utils.logInfo('LIVEWRAPPED_BID_RESPONSE:', args); - let bidResponse = cache.auctions[args.auctionId].bids[args.adId]; + let bidResponse = cache.auctions[args.auctionId].bids[args.requestId]; bidResponse.isBid = args.getStatusCode() === CONSTANTS.STATUS.GOOD; bidResponse.width = args.width; bidResponse.height = args.height; bidResponse.cpm = args.cpm; bidResponse.ttr = args.timeToRespond; + bidResponse.readyToSend = 1; + if (!bidResponse.ttr) { + bidResponse.ttr = time - bidResponse.start; + } break; case CONSTANTS.EVENTS.BIDDER_DONE: utils.logInfo('LIVEWRAPPED_BIDDER_DONE:', args); args.bids.forEach(doneBid => { - let bid = cache.auctions[doneBid.auctionId].bids[doneBid.bidId]; + let bid = cache.auctions[doneBid.auctionId].bids[doneBid.bidId || doneBid.requestId]; if (!bid.ttr) { - bid.ttr = Date.now() - args.auctionStart; + bid.ttr = time - bid.start; } + bid.readyToSend = 1; }); break; case CONSTANTS.EVENTS.BID_WON: utils.logInfo('LIVEWRAPPED_BID_WON:', args); - let wonBid = cache.auctions[args.auctionId].bids[args.adId]; + let wonBid = cache.auctions[args.auctionId].bids[args.requestId]; wonBid.won = true; if (wonBid.sendStatus != 0) { livewrappedAnalyticsAdapter.sendEvents(); @@ -105,7 +113,8 @@ livewrappedAnalyticsAdapter.sendEvents = function() { requests: getSentRequests(), responses: getResponses(), wins: getWins(), - timeouts: getTimeouts() + timeouts: getTimeouts(), + rcv: getAdblockerRecovered() }; if (events.requests.length == 0 && @@ -118,6 +127,12 @@ livewrappedAnalyticsAdapter.sendEvents = function() { ajax(URL, undefined, JSON.stringify(events), {method: 'POST'}); } +function getAdblockerRecovered() { + try { + return utils.getWindowTop().I12C && utils.getWindowTop().I12C.Morph === 1; + } catch (e) {} +} + function getSentRequests() { var sentRequests = []; @@ -147,7 +162,7 @@ function getResponses() { Object.keys(cache.auctions[auctionId].bids).forEach(bidId => { let auction = cache.auctions[auctionId]; let bid = auction.bids[bidId]; - if (!(bid.sendStatus & RESPONSESENT) && !bid.timeout) { + if (bid.readyToSend && !(bid.sendStatus & RESPONSESENT) && !bid.timeout) { bid.sendStatus |= RESPONSESENT; responses.push({ diff --git a/modules/livewrappedBidAdapter.js b/modules/livewrappedBidAdapter.js index 69a89421a32..1ad18cb15eb 100644 --- a/modules/livewrappedBidAdapter.js +++ b/modules/livewrappedBidAdapter.js @@ -24,6 +24,7 @@ export const spec = { * seats: List of bidders and seats Optional. {"bidder name": ["seat 1", "seat 2"], ...} * deviceId: Device id if available Optional. * ifa: Advertising ID Optional. + * options Dynamic data Optional. Optional data to send into adapter. * * @param {BidRequest} bid The bid params to validate. * @return boolean True if this is a valid bid, and false otherwise. @@ -69,6 +70,7 @@ export const spec = { gdprApplies: bidderRequest.gdprConsent ? bidderRequest.gdprConsent.gdprApplies : undefined, gdprConsent: bidderRequest.gdprConsent ? bidderRequest.gdprConsent.consentString : undefined, cookieSupport: !utils.isSafariBrowser() && utils.cookiesAreEnabled(), + rcv: getAdblockerRecovered(), adRequests: [...adRequests] }; const payloadString = JSON.stringify(payload); @@ -178,7 +180,8 @@ function bidToAdRequest(bid) { callerAdUnitId: bid.params.adUnitName || bid.adUnitCode || bid.placementCode, bidId: bid.bidId, transactionId: bid.transactionId, - formats: bid.sizes.map(sizeToFormat) + formats: bid.sizes.map(sizeToFormat), + options: bid.params.options }; } @@ -189,4 +192,10 @@ function sizeToFormat(size) { } } +function getAdblockerRecovered() { + try { + return utils.getWindowTop().I12C && utils.getWindowTop().I12C.Morph === 1; + } catch (e) {} +} + registerBidder(spec); diff --git a/test/spec/modules/livewrappedAnalyticsAdapter_spec.js b/test/spec/modules/livewrappedAnalyticsAdapter_spec.js index 60fc9bdc1ec..92c1c4d3ab3 100644 --- a/test/spec/modules/livewrappedAnalyticsAdapter_spec.js +++ b/test/spec/modules/livewrappedAnalyticsAdapter_spec.js @@ -3,6 +3,7 @@ import CONSTANTS from 'src/constants.json'; import { config } from 'src/config'; let events = require('src/events'); +let utils = require('src/utils'); let adapterManager = require('src/adapterManager').default; const { @@ -27,6 +28,7 @@ const BID1 = { cpm: 1.1, timeToRespond: 200, bidId: '2ecff0db240757', + requestId: '2ecff0db240757', adId: '2ecff0db240757', auctionId: '25c6d7f5-699a-4bfc-87c9-996f915341fa', getStatusCode() { @@ -40,9 +42,20 @@ const BID2 = Object.assign({}, BID1, { cpm: 2.2, timeToRespond: 300, bidId: '3ecff0db240757', + requestId: '3ecff0db240757', adId: '3ecff0db240757', }); +const BID3 = { + bidId: '4ecff0db240757', + requestId: '4ecff0db240757', + adId: '4ecff0db240757', + auctionId: '25c6d7f5-699a-4bfc-87c9-996f915341fa', + getStatusCode() { + return CONSTANTS.STATUS.NO_BID; + } +}; + const MOCK = { AUCTION_INIT: { 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', @@ -61,6 +74,11 @@ const MOCK = { 'bidder': 'livewrapped', 'adUnitCode': 'box_d_1', 'bidId': '3ecff0db240757', + }, + { + 'bidder': 'livewrapped', + 'adUnitCode': 'box_d_2', + 'bidId': '4ecff0db240757', } ], 'start': 1519149562216 @@ -73,17 +91,20 @@ const MOCK = { }, BID_WON: [ Object.assign({}, BID1, { - 'status': 'rendered' + 'status': 'rendered', + 'requestId': '2ecff0db240757' }), Object.assign({}, BID2, { - 'status': 'rendered' + 'status': 'rendered', + 'requestId': '3ecff0db240757' }) ], BIDDER_DONE: { 'bidderCode': 'livewrapped', 'bids': [ BID1, - BID2 + BID2, + BID3 ] }, BID_TIMEOUT: [ @@ -106,6 +127,11 @@ const ANALYTICS_MESSAGE = { adUnit: 'box_d_1', bidder: 'livewrapped', timeStamp: 1519149562216 + }, + { + adUnit: 'box_d_2', + bidder: 'livewrapped', + timeStamp: 1519149562216 } ], responses: [ @@ -128,6 +154,13 @@ const ANALYTICS_MESSAGE = { cpm: 2.2, ttr: 300, IsBid: true + }, + { + timeStamp: 1519149562216, + adUnit: 'box_d_2', + bidder: 'livewrapped', + ttr: 200, + IsBid: false } ], timeouts: [], @@ -177,6 +210,7 @@ describe('Livewrapped analytics adapter', function () { xhr.onCreate = request => requests.push(request); sandbox.stub(events, 'getEvents').returns([]); + sandbox.stub(utils, 'timestamp').returns(1519149562416); clock = sandbox.useFakeTimers(1519767013781); }); @@ -206,6 +240,7 @@ describe('Livewrapped analytics adapter', function () { }); it('should build a batched message from prebid events', function () { + sandbox.stub(utils, 'getWindowTop').returns({}); performStandardAuction(); clock.tick(BID_WON_TIMEOUT + 1000); @@ -261,5 +296,19 @@ describe('Livewrapped analytics adapter', function () { expect(message.timeouts[0].bidder).to.equal('livewrapped'); expect(message.timeouts[0].adUnit).to.equal('panorama_d_1'); }); + + it('should detect adblocker recovered request', function () { + sandbox.stub(utils, 'getWindowTop').returns({ I12C: { Morph: 1 } }); + performStandardAuction(); + + clock.tick(BID_WON_TIMEOUT + 1000); + + expect(requests.length).to.equal(1); + let request = requests[0]; + + let message = JSON.parse(request.requestBody); + + expect(message.rcv).to.equal(true); + }); }); }); diff --git a/test/spec/modules/livewrappedBidAdapter_spec.js b/test/spec/modules/livewrappedBidAdapter_spec.js index b12ff56c075..855eb2ee3f9 100644 --- a/test/spec/modules/livewrappedBidAdapter_spec.js +++ b/test/spec/modules/livewrappedBidAdapter_spec.js @@ -303,6 +303,64 @@ describe('Livewrapped adapter tests', function () { expect(data).to.deep.equal(expectedQuery); }); + it('should make a well-formed single request object with optional parameters', function() { + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + let testbidRequest = clone(bidderRequest); + delete testbidRequest.bids[0].params.userId; + delete testbidRequest.bids[0].params.seats; + delete testbidRequest.bids[0].params.adUnitId; + testbidRequest.bids[0].params.options = {keyvalues: [{key: 'key', value: 'value'}]}; + let result = spec.buildRequests(testbidRequest.bids, testbidRequest); + let data = JSON.parse(result.data); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + url: 'http://www.domain.com', + version: '1.1', + cookieSupport: true, + adRequests: [{ + callerAdUnitId: 'panorama_d_1', + bidId: '2ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 980, height: 240}, {width: 980, height: 120}], + options: {keyvalues: [{key: 'key', value: 'value'}]} + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + + it('should make a well-formed single request object with ad blocker revovered parameter', function() { + sandbox.stub(utils, 'getWindowTop').returns({ I12C: { Morph: 1 } }); + sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); + sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); + let testbidRequest = clone(bidderRequest); + delete testbidRequest.bids[0].params.userId; + delete testbidRequest.bids[0].params.seats; + delete testbidRequest.bids[0].params.adUnitId; + let result = spec.buildRequests(testbidRequest.bids, testbidRequest); + let data = JSON.parse(result.data); + + let expectedQuery = { + auctionId: 'F7557995-65F5-4682-8782-7D5D34D82A8C', + publisherId: '26947112-2289-405D-88C1-A7340C57E63E', + url: 'http://www.domain.com', + version: '1.1', + cookieSupport: true, + rcv: true, + adRequests: [{ + callerAdUnitId: 'panorama_d_1', + bidId: '2ffb201a808da7', + transactionId: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D', + formats: [{width: 980, height: 240}, {width: 980, height: 120}] + }] + }; + + expect(data).to.deep.equal(expectedQuery); + }); + it('should pass gdpr true parameters', function() { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); sandbox.stub(utils, 'cookiesAreEnabled').callsFake(() => true); From 8f5ea4c2efb047353dd0ddbbcc304fa88ddf92fd Mon Sep 17 00:00:00 2001 From: mefjush Date: Tue, 30 Apr 2019 16:02:33 +0200 Subject: [PATCH 035/146] Add support for bidderRequest.refererInfo in Adhese Adapter (#3725) * Add support for bidderRequest.refererInfo in Adhese Adapter. * Apply code review suggestions --- modules/adheseBidAdapter.js | 10 ++++++++-- test/spec/modules/adheseBidAdapter_spec.js | 9 +++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/modules/adheseBidAdapter.js b/modules/adheseBidAdapter.js index daea17a2a5b..6ca8c8a6aa6 100644 --- a/modules/adheseBidAdapter.js +++ b/modules/adheseBidAdapter.js @@ -18,13 +18,15 @@ export const spec = { if (validBidRequests.length === 0) { return null; } + const { gdprConsent, refererInfo } = bidderRequest; const account = getAccount(validBidRequests); const targets = validBidRequests.map(bid => bid.params.data).reduce(mergeTargets, {}); - const gdprParams = (bidderRequest.gdprConsent && bidderRequest.gdprConsent.consentString) ? [ 'xt' + bidderRequest.gdprConsent.consentString ] : []; + const gdprParams = (gdprConsent && gdprConsent.consentString) ? [`xt${gdprConsent.consentString}`] : []; + const refererParams = (refererInfo && refererInfo.referer) ? [`xf${base64urlEncode(refererInfo.referer)}`] : []; const targetsParams = Object.keys(targets).map(targetCode => targetCode + targets[targetCode].join(';')); const slotsParams = validBidRequests.map(bid => 'sl' + bidToSlotName(bid)); - const params = [...slotsParams, ...targetsParams, ...gdprParams].map(s => '/' + s).join(''); + const params = [...slotsParams, ...targetsParams, ...gdprParams, ...refererParams].map(s => `/${s}`).join(''); const cacheBuster = '?t=' + new Date().getTime(); const uri = 'https://ads-' + account + '.adhese.com/json' + params + cacheBuster; @@ -166,4 +168,8 @@ function getAdDetails(ad) { return { creativeId: creativeId, dealId: dealId }; } +function base64urlEncode(s) { + return btoa(s).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); +} + registerBidder(spec); diff --git a/test/spec/modules/adheseBidAdapter_spec.js b/test/spec/modules/adheseBidAdapter_spec.js index 68beedd4b35..348fb772319 100644 --- a/test/spec/modules/adheseBidAdapter_spec.js +++ b/test/spec/modules/adheseBidAdapter_spec.js @@ -64,6 +64,9 @@ describe('AdheseAdapter', function () { gdprConsent: { gdprApplies: true, consentString: 'CONSENT_STRING' + }, + refererInfo: { + referer: 'http://prebid.org/dev-docs/subjects?_d=1' } }; @@ -91,6 +94,12 @@ describe('AdheseAdapter', function () { expect(req.url).to.contain('/xtCONSENT_STRING'); }); + it('should include referer param in base64url format', function () { + let req = spec.buildRequests([ minimalBid() ], bidderRequest); + + expect(req.url).to.contain('/xfaHR0cDovL3ByZWJpZC5vcmcvZGV2LWRvY3Mvc3ViamVjdHM_X2Q9MQ'); + }); + it('should include bids', function () { let bid = minimalBid(); let req = spec.buildRequests([ bid ], bidderRequest); From e741cf8f5ad6f8c27109f109c7b796599820ed4d Mon Sep 17 00:00:00 2001 From: Prebid Manager <49466873+Prebid-Manager@users.noreply.github.com> Date: Wed, 1 May 2019 01:39:33 +0700 Subject: [PATCH 036/146] PrebidManager Analytics: init module (#3735) * PrebidManager Analytics: init module * PrebidManager Analytics: tests fix * PrebidManager Analytics: fix test * PrebidManager Analytics: renamed file names * PrebidManager Analytics: work on PR comments - default url, version, tests * PrebidManager Analytics: bring back ver * PrebidManager Analytics: setInterval for flush in enable analytics, fix empty config options, prebid function pageViewId * PrebidManager Analytics: disable analytics --- modules/prebidmanagerAnalyticsAdapter.js | 191 ++++++++++++++++++ modules/prebidmanagerAnalyticsAdapter.md | 9 + .../prebidmanagerAnalyticsAdapter_spec.js | 117 +++++++++++ 3 files changed, 317 insertions(+) create mode 100644 modules/prebidmanagerAnalyticsAdapter.js create mode 100644 modules/prebidmanagerAnalyticsAdapter.md create mode 100644 test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js diff --git a/modules/prebidmanagerAnalyticsAdapter.js b/modules/prebidmanagerAnalyticsAdapter.js new file mode 100644 index 00000000000..eb9ad344253 --- /dev/null +++ b/modules/prebidmanagerAnalyticsAdapter.js @@ -0,0 +1,191 @@ +import {ajaxBuilder} from '../src/ajax'; +import adapter from '../src/AnalyticsAdapter'; +import adapterManager from '../src/adapterManager'; + +/** + * prebidmanagerAnalyticsAdapter.js - analytics adapter for prebidmanager + */ +const DEFAULT_EVENT_URL = 'https://endpoint.prebidmanager.com/endpoint' +const analyticsType = 'endpoint'; +const analyticsName = 'Prebid Manager Analytics: '; + +var utils = require('src/utils'); +var CONSTANTS = require('src/constants.json'); +let ajax = ajaxBuilder(0); + +var _VERSION = 1; +var initOptions = null; +var _pageViewId = utils.generateUUID(); +var _startAuction = 0; +var _bidRequestTimeout = 0; +let flushInterval; +var pmAnalyticsEnabled = false; + +var w = window; +var d = document; +var e = d.documentElement; +var g = d.getElementsByTagName('body')[0]; +var x = w.innerWidth || e.clientWidth || g.clientWidth; +var y = w.innerHeight || e.clientHeight || g.clientHeight; + +var _pageView = { + eventType: 'pageView', + userAgent: window.navigator.userAgent, + timestamp: Date.now(), + timezoneOffset: new Date().getTimezoneOffset(), + language: window.navigator.language, + vendor: window.navigator.vendor, + screenWidth: x, + screenHeight: y +}; + +var _eventQueue = [ + _pageView +]; + +let prebidmanagerAnalytics = Object.assign(adapter({url: DEFAULT_EVENT_URL, analyticsType}), { + track({eventType, args}) { + handleEvent(eventType, args); + } +}); + +prebidmanagerAnalytics.originEnableAnalytics = prebidmanagerAnalytics.enableAnalytics; +prebidmanagerAnalytics.enableAnalytics = function (config) { + initOptions = config.options || {}; + initOptions.url = initOptions.url || DEFAULT_EVENT_URL; + pmAnalyticsEnabled = true; + prebidmanagerAnalytics.originEnableAnalytics(config); + flushInterval = setInterval(flush, 1000); +}; + +prebidmanagerAnalytics.originDisableAnalytics = prebidmanagerAnalytics.disableAnalytics; +prebidmanagerAnalytics.disableAnalytics = function() { + if (!pmAnalyticsEnabled) { + return; + } + flush(); + clearInterval(flushInterval); + prebidmanagerAnalytics.originDisableAnalytics(); +}; + +function flush() { + if (!pmAnalyticsEnabled) { + return; + } + + if (_eventQueue.length > 1) { + var data = { + pageViewId: _pageViewId, + ver: _VERSION, + bundleId: initOptions.bundleId, + events: _eventQueue + }; + + ajax( + initOptions.url, + () => utils.logInfo(`${analyticsName} sent events batch`), + _VERSION + ':' + JSON.stringify(data), + { + contentType: 'text/plain', + method: 'POST', + withCredentials: true + } + ); + _eventQueue = [ + _pageView + ]; + } +} + +function handleEvent(eventType, eventArgs) { + eventArgs = eventArgs ? JSON.parse(JSON.stringify(eventArgs)) : {}; + var pmEvent = {}; + + switch (eventType) { + case CONSTANTS.EVENTS.AUCTION_INIT: { + pmEvent = eventArgs; + _startAuction = pmEvent.timestamp; + _bidRequestTimeout = pmEvent.timeout; + break; + } + case CONSTANTS.EVENTS.AUCTION_END: { + pmEvent = eventArgs; + pmEvent.start = _startAuction; + pmEvent.end = Date.now(); + break; + } + case CONSTANTS.EVENTS.BID_ADJUSTMENT: { + pmEvent.bidders = eventArgs; + break; + } + case CONSTANTS.EVENTS.BID_TIMEOUT: { + pmEvent.bidders = eventArgs; + pmEvent.duration = _bidRequestTimeout; + break; + } + case CONSTANTS.EVENTS.BID_REQUESTED: { + pmEvent = eventArgs; + break; + } + case CONSTANTS.EVENTS.BID_RESPONSE: { + pmEvent = eventArgs; + delete pmEvent.ad; + break; + } + case CONSTANTS.EVENTS.BID_WON: { + pmEvent = eventArgs; + delete pmEvent.ad; + delete pmEvent.adUrl; + break; + } + case CONSTANTS.EVENTS.BIDDER_DONE: { + pmEvent = eventArgs; + break; + } + case CONSTANTS.EVENTS.SET_TARGETING: { + pmEvent.targetings = eventArgs; + break; + } + case CONSTANTS.EVENTS.REQUEST_BIDS: { + pmEvent = eventArgs; + break; + } + case CONSTANTS.EVENTS.ADD_AD_UNITS: { + pmEvent = eventArgs; + break; + } + case CONSTANTS.EVENTS.AD_RENDER_FAILED: { + pmEvent = eventArgs; + break; + } + default: + return; + } + + pmEvent.eventType = eventType; + pmEvent.timestamp = pmEvent.timestamp || Date.now(); + + sendEvent(pmEvent); +} + +function sendEvent(event) { + _eventQueue.push(event); + utils.logInfo(`${analyticsName}Event ${event.eventType}:`, event); + + if (event.eventType === CONSTANTS.EVENTS.AUCTION_END) { + flush(); + } +} + +adapterManager.registerAnalyticsAdapter({ + adapter: prebidmanagerAnalytics, + code: 'prebidmanager' +}); + +prebidmanagerAnalytics.getOptions = function () { + return initOptions; +}; + +prebidmanagerAnalytics.flush = flush; + +export default prebidmanagerAnalytics; diff --git a/modules/prebidmanagerAnalyticsAdapter.md b/modules/prebidmanagerAnalyticsAdapter.md new file mode 100644 index 00000000000..030e79b406f --- /dev/null +++ b/modules/prebidmanagerAnalyticsAdapter.md @@ -0,0 +1,9 @@ +# Overview + +Module Name: Prebid Manager Analytics Adapter +Module Type: Analytics Adapter +Maintainer: admin@prebidmanager.com + +# Description + +Analytics adapter for Prebid Manager. Contact admin@prebidmanager.com for information. diff --git a/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js b/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..cd414a70236 --- /dev/null +++ b/test/spec/modules/prebidmanagerAnalyticsAdapter_spec.js @@ -0,0 +1,117 @@ +import prebidmanagerAnalytics from 'modules/prebidmanagerAnalyticsAdapter'; +import {expect} from 'chai'; +let events = require('src/events'); +let constants = require('src/constants.json'); + +describe('Prebid Manager Analytics Adapter', function () { + let xhr; + let requests; + + let bidWonEvent = { + 'bidderCode': 'appnexus', + 'width': 300, + 'height': 250, + 'adId': '1ebb82ec35375e', + 'mediaType': 'banner', + 'cpm': 0.5, + 'requestId': '1582271863760569973', + 'creative_id': '96846035', + 'creativeId': '96846035', + 'ttl': 60, + 'currency': 'USD', + 'netRevenue': true, + 'auctionId': '9c7b70b9-b6ab-4439-9e71-b7b382797c18', + 'responseTimestamp': 1537521629657, + 'requestTimestamp': 1537521629331, + 'bidder': 'appnexus', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'timeToRespond': 326, + 'size': '300x250', + 'status': 'rendered', + 'eventType': 'bidWon', + 'ad': 'some ad', + 'adUrl': 'ad url' + }; + + before(function () { + xhr = sinon.useFakeXMLHttpRequest(); + xhr.onCreate = request => requests.push(request); + }); + + after(function () { + xhr.restore(); + }); + + describe('Prebid Manager Analytic tests', function () { + beforeEach(function () { + requests = []; + sinon.stub(events, 'getEvents').returns([]); + }); + + afterEach(function () { + prebidmanagerAnalytics.disableAnalytics(); + events.getEvents.restore(); + }); + + it('support custom endpoint', function () { + let custom_url = 'custom url'; + prebidmanagerAnalytics.enableAnalytics({ + provider: 'prebidmanager', + options: { + url: custom_url, + bundleId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + } + }); + + expect(prebidmanagerAnalytics.getOptions().url).to.equal(custom_url); + }); + + it('bid won event', function() { + xhr.onCreate = request => requests.push(request); + let bundleId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; + prebidmanagerAnalytics.enableAnalytics({ + provider: 'prebidmanager', + options: { + bundleId: bundleId + } + }); + + events.emit(constants.EVENTS.BID_WON, bidWonEvent); + prebidmanagerAnalytics.flush(); + + expect(requests.length).to.equal(1); + expect(requests[0].url).to.equal('https://endpoint.prebidmanager.com/endpoint'); + expect(requests[0].requestBody.substring(0, 2)).to.equal('1:'); + + const pmEvents = JSON.parse(requests[0].requestBody.substring(2)); + expect(pmEvents.pageViewId).to.exist; + expect(pmEvents.bundleId).to.equal(bundleId); + expect(pmEvents.ver).to.equal(1); + expect(pmEvents.events.length).to.equal(2); + expect(pmEvents.events[0].eventType).to.equal('pageView'); + expect(pmEvents.events[1].eventType).to.equal('bidWon'); + expect(pmEvents.events[1].ad).to.be.undefined; + expect(pmEvents.events[1].adUrl).to.be.undefined; + }); + + it('track event without errors', function () { + sinon.spy(prebidmanagerAnalytics, 'track'); + + prebidmanagerAnalytics.enableAnalytics({ + provider: 'prebidmanager', + options: { + bundleId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + } + }); + + events.emit(constants.EVENTS.AUCTION_INIT, {}); + events.emit(constants.EVENTS.BID_REQUESTED, {}); + events.emit(constants.EVENTS.BID_RESPONSE, {}); + events.emit(constants.EVENTS.BID_WON, {}); + events.emit(constants.EVENTS.AUCTION_END, {}); + events.emit(constants.EVENTS.BID_TIMEOUT, {}); + + sinon.assert.callCount(prebidmanagerAnalytics.track, 6); + }); + }); +}); From 352da36418323c9dba2d10663739f1857c0a5595 Mon Sep 17 00:00:00 2001 From: phtechno Date: Tue, 30 Apr 2019 23:21:33 +0200 Subject: [PATCH 037/146] smartadserverBidAdapter.js - make bid.params.domain optional (#3781) https://prg.smartadserver.com is the standard domain for smartadserver. This update make the "bid.params.domain"-parameter optional. --- modules/smartadserverBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index 00d617b3c92..401c72bffaf 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -16,7 +16,7 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function (bid) { - return !!(bid.params && bid.params.siteId && bid.params.pageId && bid.params.formatId && bid.params.domain); + return !!(bid.params && bid.params.siteId && bid.params.pageId && bid.params.formatId); }, /** * Make a server request from the list of BidRequests. @@ -63,7 +63,7 @@ export const spec = { var payloadString = JSON.stringify(payload); return { method: 'POST', - url: bid.params.domain + '/prebid/v1', + url: (bid.params.domain !== undefined ? bid.params.domain : 'https://prg.smartadserver.com') + '/prebid/v1', data: payloadString, }; }); From c48817ca4b54389cc53c2f26e84a02bfa03f0209 Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Wed, 1 May 2019 09:27:51 -0400 Subject: [PATCH 038/146] update e2e tests in README (#3778) * update e2e tests in README * add clarifying note * added note about Prebid.org members + browserstack --- README.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 62c835f8eb3..f802efecfcf 100644 --- a/README.md +++ b/README.md @@ -207,10 +207,20 @@ gulp test-coverage gulp view-coverage ``` -For end-to-end testing, edit the example file `./integrationExamples/gpt/pbjs_example_gpt.html`: +For Prebid.org members with access to BrowserStack, additional end-to-end testing can be done with: -1. Change `{id}` values appropriately to set up ad units and bidders -2. Set the path to Prebid.js in your example file as shown below (see `pbs.src`). +```bash +gulp e2e-test --host=test.localhost +``` + +To run these tests, the following items are required: +- setup an alias of localhost in your `hosts` file (eg `127.0.0.1 test.localhost`); note - you can use any alias. Use this alias in the command-line argument above. +- access to [BrowserStack](https://www.browserstack.com/) account. Assign the following variables in your bash_profile: +```bash +export BROWSERSTACK_USERNAME='YourUserNameHere' +export BROWSERSTACK_ACCESS_KEY='YourAccessKeyHere' +``` +You can get these BrowserStack values from your profile page. For development: From 1357e96a71e6cb7b6c850b5e5f555ea951828337 Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Wed, 1 May 2019 06:49:32 -0700 Subject: [PATCH 039/146] OpenX Adapter - support User ID module (#3529) * added universal id support to bid adapter * added unit test for universal id support in bid adapter * added universal id support to bid adapter * added unit test for universal id support in bid adapter * renamed universalID to userId * removed merge mistake * fix old code in test --- modules/openxBidAdapter.js | 4 +++- test/spec/modules/openxBidAdapter_spec.js | 24 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index 52a97a98952..9719fbb635a 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -233,7 +233,9 @@ function buildCommonQueryParamsFromBids(bids, bidderRequest) { } } - if (bids[0].crumbs && bids[0].crumbs.pubcid) { + if ((bids[0].userId && bids[0].userId.pubcid)) { + defaultParams.pubcid = bids[0].userId.pubcid; + } else if (bids[0].crumbs && bids[0].crumbs.pubcid) { defaultParams.pubcid = bids[0].crumbs.pubcid; } diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index 468d9cc33a7..ee8e452f707 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -940,6 +940,30 @@ describe('OpenxAdapter', function () { const request = spec.buildRequests(bidRequestsWithPubcid); expect(request[0].data.pubcid).to.equal('c4a4c843-2368-4b5e-b3b1-6ee4702b9ad6'); }); + + it('should send a pubcid query param when userId.pubcid is defined in the bid requests', function () { + const bidRequestsWithPubcid = [{ + bidder: 'openx', + params: { + unit: '11', + delDomain: 'test-del-domain' + }, + userId: { + pubcid: 'c1a4c843-2368-4b5e-b3b1-6ee4702b9ad6' + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + bidId: 'test-bid-id-1', + bidderRequestId: 'test-bid-request-1', + auctionId: 'test-auction-1' + }]; + const request = spec.buildRequests(bidRequestsWithPubcid); + expect(request[0].data.pubcid).to.equal('c1a4c843-2368-4b5e-b3b1-6ee4702b9ad6'); + }); }) }); From 293619775702f7e867322679ca963112c874e483 Mon Sep 17 00:00:00 2001 From: bretg Date: Wed, 1 May 2019 10:12:25 -0400 Subject: [PATCH 040/146] Delete sonobi_video.html (#3791) not sure how this empty file got placed in the root. removing --- sonobi_video.html | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 sonobi_video.html diff --git a/sonobi_video.html b/sonobi_video.html deleted file mode 100644 index e69de29bb2d..00000000000 From 8e2ee4aece18ccb7a470f8da0d8b147877a67f7b Mon Sep 17 00:00:00 2001 From: Rich Snapp Date: Wed, 1 May 2019 09:06:24 -0600 Subject: [PATCH 041/146] remove optimize.js from build process (#3789) --- gulpfile.js | 2 - package-lock.json | 1104 ++++++++++++++++++--------------------------- package.json | 1 - 3 files changed, 445 insertions(+), 662 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 0170df80c62..a89f570e496 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -19,7 +19,6 @@ var header = require('gulp-header'); var footer = require('gulp-footer'); var replace = require('gulp-replace'); var shell = require('gulp-shell'); -var optimizejs = require('gulp-optimize-js'); var eslint = require('gulp-eslint'); var gulpif = require('gulp-if'); var sourcemaps = require('gulp-sourcemaps'); @@ -145,7 +144,6 @@ function makeWebpackPkg() { .pipe(webpackStream(cloned, webpack)) .pipe(uglify()) .pipe(gulpif(file => file.basename === 'prebid-core.js', header(banner, { prebid: prebid }))) - .pipe(optimizejs()) .pipe(gulp.dest('build/dist')); } diff --git a/package-lock.json b/package-lock.json index 655363b9949..c3bd739008f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.10.0", + "version": "2.13.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -856,12 +856,6 @@ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, - "@types/node": { - "version": "8.10.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.45.tgz", - "integrity": "sha512-tGVTbA+i3qfXsLbq9rEq/hezaHY55QxQLeXQL2ejNgFAxxrgu8eMmYIOsRcl7hN1uTLVsKOOYacV/rcJM3sfgQ==", - "dev": true - }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", @@ -1079,6 +1073,58 @@ "default-require-extensions": "^1.0.0" } }, + "archiver": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-2.1.1.tgz", + "integrity": "sha1-/2YrSnggFJSj7lRNOjP+dJZQnrw=", + "dev": true, + "requires": { + "archiver-utils": "^1.3.0", + "async": "^2.0.0", + "buffer-crc32": "^0.2.1", + "glob": "^7.0.0", + "lodash": "^4.8.0", + "readable-stream": "^2.0.0", + "tar-stream": "^1.5.0", + "zip-stream": "^1.2.0" + }, + "dependencies": { + "async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + } + } + }, + "archiver-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-1.3.0.tgz", + "integrity": "sha1-5QtMCccL89aA4y/xt5lOn52JUXQ=", + "dev": true, + "requires": { + "glob": "^7.0.0", + "graceful-fs": "^4.1.0", + "lazystream": "^1.0.0", + "lodash": "^4.8.0", + "normalize-path": "^2.0.0", + "readable-stream": "^2.0.0" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, "archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", @@ -1296,12 +1342,6 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, - "ast-types": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.12.3.tgz", - "integrity": "sha512-wJUcAfrdW+IgDoMGNz5MmcvahKgB7BwIbLupdKVVHxHNYt+HVR2k35swdYNv9aZpF8nvlkjbnkp2rrNwxGckZA==", - "dev": true - }, "async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", @@ -2648,6 +2688,16 @@ "integrity": "sha512-xVNN69YGDghOqCCtA6FI7avYrr02mTJjOgB0/f1VPD3pJC8QEvjTKWc4epDx8AqxxA75NI0QpVM2gPJXUbE4Tg==", "dev": true }, + "bl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "dev": true, + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, "blob": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", @@ -2953,6 +3003,12 @@ "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", "dev": true }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, "buffer-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", @@ -3159,39 +3215,6 @@ "type-detect": "^4.0.5" } }, - "chai-nightwatch": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/chai-nightwatch/-/chai-nightwatch-0.2.1.tgz", - "integrity": "sha512-2lprSMi72sHq2ZGyPTYUDQNsd2O4z81SicascbI4bkU54Xzk5Ofunn2CbrExADGC7jBH2D8r66X/aSEl+/agXQ==", - "dev": true, - "requires": { - "assertion-error": "1.0.0", - "deep-eql": "0.1.3" - }, - "dependencies": { - "assertion-error": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz", - "integrity": "sha1-x/hUOP3UZrx8oWq5DIFRN5el0js=", - "dev": true - }, - "deep-eql": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", - "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", - "dev": true, - "requires": { - "type-detect": "0.1.1" - } - }, - "type-detect": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", - "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", - "dev": true - } - } - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -3485,6 +3508,29 @@ "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", "dev": true }, + "compress-commons": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-1.2.2.tgz", + "integrity": "sha1-UkqfEJA/OoEzibAiXSfEi7dRiQ8=", + "dev": true, + "requires": { + "buffer-crc32": "^0.2.1", + "crc32-stream": "^2.0.0", + "normalize-path": "^2.0.0", + "readable-stream": "^2.0.0" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3676,6 +3722,37 @@ "request": "^2.86.0" } }, + "crc": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "dev": true, + "requires": { + "buffer": "^5.1.0" + }, + "dependencies": { + "buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + } + } + }, + "crc32-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-2.0.0.tgz", + "integrity": "sha1-483TtN8xaN10494/u8t7KX/pCPQ=", + "dev": true, + "requires": { + "crc": "^3.4.4", + "readable-stream": "^2.0.0" + } + }, "create-ecdh": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", @@ -3768,6 +3845,21 @@ } } }, + "css-parse": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz", + "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=", + "dev": true, + "requires": { + "css": "^2.0.0" + } + }, + "css-value": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/css-value/-/css-value-0.0.1.tgz", + "integrity": "sha1-Xv1sLupeof1rasV+wEJ7GEUkJOo=", + "dev": true + }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -3801,15 +3893,6 @@ "assert-plus": "^1.0.0" } }, - "data-uri-to-buffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.1.tgz", - "integrity": "sha512-OkVVLrerfAKZlW2ZZ3Ve2y65jgiWqBKsTfUIAFbn8nVbPcCZg6l6gikKlEYv0kXcmzqGm6mFq/Jf2vriuEkv8A==", - "dev": true, - "requires": { - "@types/node": "^8.0.7" - } - }, "date-format": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz", @@ -3899,6 +3982,12 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "deepmerge": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.0.1.tgz", + "integrity": "sha512-VIPwiMJqJ13ZQfaCsIFnp5Me9tnjURiaIFxfz7EH0Ci0dTSQpZtSLrqOicXqEd/z2r+z+Klk9GzmnRsgpgbOsQ==", + "dev": true + }, "default-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", @@ -3998,25 +4087,6 @@ "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", "dev": true }, - "degenerator": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", - "integrity": "sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU=", - "dev": true, - "requires": { - "ast-types": "0.x.x", - "escodegen": "1.x.x", - "esprima": "3.x.x" - }, - "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - } - } - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -4069,6 +4139,12 @@ "repeating": "^2.0.0" } }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true + }, "detect-newline": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", @@ -4458,12 +4534,6 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", "dev": true }, - "ejs": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", - "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==", - "dev": true - }, "electron-to-chromium": { "version": "1.3.124", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.124.tgz", @@ -5232,12 +5302,6 @@ "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", "dev": true }, - "estree-walker": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.3.1.tgz", - "integrity": "sha1-5rGlHPcpJSTnI3wxLl/mZgwc4ao=", - "dev": true - }, "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", @@ -5306,18 +5370,52 @@ } }, "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "dev": true, "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", "is-stream": "^1.1.0", "npm-run-path": "^2.0.0", "p-finally": "^1.0.0", "signal-exit": "^3.0.0", "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } } }, "expand-braces": { @@ -5591,6 +5689,15 @@ "websocket-driver": ">=0.5.1" } }, + "fibers": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fibers/-/fibers-3.1.1.tgz", + "integrity": "sha512-dl3Ukt08rHVQfY8xGD0ODwyjwrRALtaghuqGH2jByYX1wpY+nAnRQjJ6Dbqq0DnVgNVQ9yibObzbF4IlPyiwPw==", + "dev": true, + "requires": { + "detect-libc": "^1.0.3" + } + }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -5610,12 +5717,6 @@ "object-assign": "^4.0.1" } }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true - }, "filename-regex": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", @@ -5890,6 +5991,12 @@ "null-check": "^1.0.0" } }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, "fs-extra": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.6.4.tgz", @@ -6474,42 +6581,6 @@ } } }, - "ftp": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", - "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", - "dev": true, - "requires": { - "readable-stream": "1.1.x", - "xregexp": "2.0.0" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, "fun-hooks": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/fun-hooks/-/fun-hooks-0.8.1.tgz", @@ -6527,6 +6598,15 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "requires": { + "globule": "^1.0.0" + } + }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", @@ -6557,33 +6637,6 @@ "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, - "get-uri": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.3.tgz", - "integrity": "sha512-x5j6Ks7FOgLD/GlvjKwgu7wdmMR55iuRHhn8hj/+gA+eSbxQvZ+AEomq+3MgVEZj1vpi738QahGbCCSIDtXtkw==", - "dev": true, - "requires": { - "data-uri-to-buffer": "2", - "debug": "4", - "extend": "~3.0.2", - "file-uri-to-path": "1", - "ftp": "~0.3.10", - "readable-stream": "3" - }, - "dependencies": { - "readable-stream": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", - "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -6767,6 +6820,17 @@ "integrity": "sha512-B69mWcqCmT3jNYmSxRxxOXWfzu3Go8NQXPfl2o0qPd1EEFhwW0dFUg9ztTu915zPQzqwIhWAlw6hmfIcCK4kkQ==", "dev": true }, + "globule": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", + "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", + "dev": true, + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + } + }, "glogg": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", @@ -6815,6 +6879,12 @@ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", "dev": true }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -7385,18 +7455,6 @@ "minimatch": "^3.0.3" } }, - "gulp-optimize-js": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/gulp-optimize-js/-/gulp-optimize-js-1.1.0.tgz", - "integrity": "sha1-X9FcaLNvbh5zh3hPhXhDX3VpYkU=", - "dev": true, - "requires": { - "gulp-util": "^3.0.7", - "lodash": "^4.16.2", - "optimize-js": "^1.0.0", - "through2": "^2.0.1" - } - }, "gulp-replace": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.0.0.tgz", @@ -7949,33 +8007,6 @@ "requires-port": "^1.0.0" } }, - "http-proxy-agent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", - "dev": true, - "requires": { - "agent-base": "4", - "debug": "3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -8014,6 +8045,12 @@ } } }, + "humanize-duration": { + "version": "3.15.3", + "resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.15.3.tgz", + "integrity": "sha512-BMz6w8p3NVa6QP9wDtqUkXfwgBqDaZ5z/np0EYdoWrLqL849Onp6JWMXMhbHtuvO9jUThLN5H1ThRQ8dUWnYkA==", + "dev": true + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -8164,12 +8201,6 @@ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", "dev": true }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - }, "is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", @@ -9333,80 +9364,12 @@ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "dev": true }, - "lodash._arraycopy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz", - "integrity": "sha1-due3wfH7klRzdIeKVi7Qaj5Q9uE=", - "dev": true - }, - "lodash._arrayeach": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz", - "integrity": "sha1-urFWsqkNPxu9XGU0AzSeXlkz754=", - "dev": true - }, - "lodash._baseassign": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", - "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", - "dev": true, - "requires": { - "lodash._basecopy": "^3.0.0", - "lodash.keys": "^3.0.0" - }, - "dependencies": { - "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", - "dev": true, - "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" - } - } - } - }, - "lodash._baseclone": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lodash._baseclone/-/lodash._baseclone-3.3.0.tgz", - "integrity": "sha1-MDUZv2OT/n5C802LYw73eU41Qrc=", - "dev": true, - "requires": { - "lodash._arraycopy": "^3.0.0", - "lodash._arrayeach": "^3.0.0", - "lodash._baseassign": "^3.0.0", - "lodash._basefor": "^3.0.0", - "lodash.isarray": "^3.0.0", - "lodash.keys": "^3.0.0" - }, - "dependencies": { - "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", - "dev": true, - "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" - } - } - } - }, "lodash._basecopy": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", "dev": true }, - "lodash._basefor": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash._basefor/-/lodash._basefor-3.0.3.tgz", - "integrity": "sha1-dVC06SGO8J+tJDQ7YSAhx5tMIMI=", - "dev": true - }, "lodash._basetostring": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", @@ -9419,12 +9382,6 @@ "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=", "dev": true }, - "lodash._bindcallback": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz", - "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=", - "dev": true - }, "lodash._escapehtmlchar": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz", @@ -9513,23 +9470,6 @@ "lodash._objecttypes": "~2.4.1" } }, - "lodash.assign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", - "dev": true - }, - "lodash.clone": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-3.0.3.tgz", - "integrity": "sha1-hGiMc9MrWpDKJWFpY/GJJSqZcEM=", - "dev": true, - "requires": { - "lodash._baseclone": "^3.0.0", - "lodash._bindcallback": "^3.0.0", - "lodash._isiterateecall": "^3.0.0" - } - }, "lodash.defaults": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", @@ -9540,12 +9480,6 @@ "lodash.keys": "~2.4.1" } }, - "lodash.defaultsdeep": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.0.tgz", - "integrity": "sha1-vsECT4WxvZbL6kBbI8FK1kQ6b4E=", - "dev": true - }, "lodash.escape": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-2.4.1.tgz", @@ -9595,12 +9529,6 @@ "lodash.isobject": "~2.4.1" } }, - "lodash.merge": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", - "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==", - "dev": true - }, "lodash.restparam": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", @@ -9764,15 +9692,6 @@ "es5-ext": "~0.10.2" } }, - "magic-string": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.16.0.tgz", - "integrity": "sha1-lw67DacZMwEoX7GqZQ85vdgetFo=", - "dev": true, - "requires": { - "vlq": "^0.2.1" - } - }, "make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -10266,12 +10185,6 @@ } } }, - "mkpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mkpath/-/mkpath-1.0.0.tgz", - "integrity": "sha1-67Opd+evHGg65v2hK1Raa6bFhT0=", - "dev": true - }, "mocha": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", @@ -10512,36 +10425,17 @@ "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", "dev": true }, - "netmask": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz", - "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=", - "dev": true - }, "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, - "nightwatch": { - "version": "1.0.19", - "resolved": "https://registry.npmjs.org/nightwatch/-/nightwatch-1.0.19.tgz", - "integrity": "sha512-Dl+EN4wFp927nn7KRkCIJ7b0Th9PVjiwflzqsoqJOwLPcLuzSBz4FYBvHXQtUkaL4/nELVgXurw/KXqj2gcFSg==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "chai-nightwatch": "0.2.1", - "ejs": "^2.5.9", - "lodash.clone": "3.0.3", - "lodash.defaultsdeep": "^4.6.0", - "lodash.merge": "^4.6.1", - "minimatch": "3.0.4", - "mkpath": "1.0.0", - "mocha": "^5.1.1", - "optimist": "^0.6.1", - "proxy-agent": "^3.0.0" - } + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true }, "nise": { "version": "1.4.10", @@ -10670,6 +10564,12 @@ "once": "^1.3.2" } }, + "npm-install-package": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/npm-install-package/-/npm-install-package-2.1.0.tgz", + "integrity": "sha1-1+/jz816sAYUuJbqUxGdyaslkSU=", + "dev": true + }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -10899,168 +10799,6 @@ } } }, - "optimize-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/optimize-js/-/optimize-js-1.0.3.tgz", - "integrity": "sha1-QyavhlfEpf8y2vcmYxdU9yq3/bw=", - "dev": true, - "requires": { - "acorn": "^3.3.0", - "concat-stream": "^1.5.1", - "estree-walker": "^0.3.0", - "magic-string": "^0.16.0", - "yargs": "^4.8.1" - }, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true - }, - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - } - }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, - "requires": { - "lcid": "^1.0.0" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } - }, - "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", - "dev": true - }, - "yargs": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", - "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", - "dev": true, - "requires": { - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "lodash.assign": "^4.0.3", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.1", - "which-module": "^1.0.0", - "window-size": "^0.2.0", - "y18n": "^3.2.1", - "yargs-parser": "^2.4.1" - } - }, - "yargs-parser": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", - "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=", - "dev": true, - "requires": { - "camelcase": "^3.0.0", - "lodash.assign": "^4.0.6" - } - } - } - }, "optionator": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", @@ -11105,6 +10843,23 @@ "execa": "^0.7.0", "lcid": "^1.0.0", "mem": "^1.1.0" + }, + "dependencies": { + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + } } }, "os-tmpdir": { @@ -11164,73 +10919,6 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "pac-proxy-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-3.0.0.tgz", - "integrity": "sha512-AOUX9jES/EkQX2zRz0AW7lSx9jD//hQS8wFXBvcnd/J2Py9KaMJMqV/LPqJssj1tgGufotb2mmopGPR15ODv1Q==", - "dev": true, - "requires": { - "agent-base": "^4.2.0", - "debug": "^3.1.0", - "get-uri": "^2.0.0", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", - "pac-resolver": "^3.0.0", - "raw-body": "^2.2.0", - "socks-proxy-agent": "^4.0.1" - }, - "dependencies": { - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "raw-body": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", - "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", - "dev": true, - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.3", - "iconv-lite": "0.4.23", - "unpipe": "1.0.0" - } - } - } - }, - "pac-resolver": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-3.0.0.tgz", - "integrity": "sha512-tcc38bsjuE3XZ5+4vP96OfhOugrX+JcnpUbhfuc4LuXBLQhoTthOstZeoQJBDnQUDYzYmdImKsbz0xSl1/9qeA==", - "dev": true, - "requires": { - "co": "^4.6.0", - "degenerator": "^1.0.4", - "ip": "^1.1.5", - "netmask": "^1.0.6", - "thunkify": "^2.1.2" - } - }, "pako": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", @@ -11660,39 +11348,6 @@ "integrity": "sha512-Fx65lf9/YDn3hUX08XUc0J8rSux36rEsyiv21ZGUC1mOyeM3lTRpZLcrm8aAolzS4itwVfm7TAPyxC2E5zd6xg==", "dev": true }, - "proxy-agent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.1.0.tgz", - "integrity": "sha512-IkbZL4ClW3wwBL/ABFD2zJ8iP84CY0uKMvBPk/OceQe/cEjrxzN1pMHsLwhbzUoRhG9QbSxYC+Z7LBkTiBNvrA==", - "dev": true, - "requires": { - "agent-base": "^4.2.0", - "debug": "^3.1.0", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", - "lru-cache": "^4.1.2", - "pac-proxy-agent": "^3.0.0", - "proxy-from-env": "^1.0.0", - "socks-proxy-agent": "^4.0.1" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "proxy-from-env": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", - "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", - "dev": true - }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -12288,6 +11943,27 @@ "uuid": "^3.3.2" } }, + "request-promise": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.4.tgz", + "integrity": "sha512-8wgMrvE546PzbR5WbYxUQogUnUDfM0S7QIFZMID+J73vdFARkFy+HElj4T+MWYhpXwlLp0EQ8Zoj8xUA0he4Vg==", + "dev": true, + "requires": { + "bluebird": "^3.5.0", + "request-promise-core": "1.1.2", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, + "request-promise-core": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", + "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -12387,6 +12063,12 @@ "integrity": "sha512-92ktAgvZhBzYTIK0Mja9uen5q5J3NRVMoDkJL2VMwq6SXjVCgqvQeVP2XAaUY6HT+XpQYeLSjb3UoitBryKmdA==", "dev": true }, + "rgb2hex": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/rgb2hex/-/rgb2hex-0.1.9.tgz", + "integrity": "sha512-32iuQzhOjyT+cv9aAFRBJ19JgHwzQwbjUhH3Fj2sWW2EEGAW8fpFrDFP5ndoKDxJaLO06x1hE3kyuIFrUQtybQ==", + "dev": true + }, "right-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", @@ -12722,12 +12404,6 @@ } } }, - "smart-buffer": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz", - "integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==", - "dev": true - }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -12960,26 +12636,6 @@ } } }, - "socks": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.2.tgz", - "integrity": "sha512-pCpjxQgOByDHLlNqlnh/mNSAxIUkyBBuwwhTcV+enZGbDaClPvHdvm6uvOwZfFJkam7cGhBNbb4JxiP8UZkRvQ==", - "dev": true, - "requires": { - "ip": "^1.1.5", - "smart-buffer": "4.0.2" - } - }, - "socks-proxy-agent": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz", - "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==", - "dev": true, - "requires": { - "agent-base": "~4.2.1", - "socks": "~2.3.2" - } - }, "sort-keys": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", @@ -13156,6 +12812,12 @@ "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", "dev": true }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true + }, "stream-array": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/stream-array/-/stream-array-1.1.2.tgz", @@ -13438,6 +13100,21 @@ "integrity": "sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A==", "dev": true }, + "tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "dev": true, + "requires": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + } + }, "temp-fs": { "version": "0.9.9", "resolved": "https://registry.npmjs.org/temp-fs/-/temp-fs-0.9.9.tgz", @@ -13508,12 +13185,6 @@ "xtend": "~4.0.0" } }, - "thunkify": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz", - "integrity": "sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0=", - "dev": true - }, "time-stamp": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", @@ -13601,6 +13272,12 @@ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", "dev": true }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "dev": true + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -14415,12 +14092,6 @@ "source-map": "^0.5.1" } }, - "vlq": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", - "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==", - "dev": true - }, "vm-browserify": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", @@ -14456,6 +14127,115 @@ "neo-async": "^2.5.0" } }, + "wdio-browserstack-service": { + "version": "0.1.18", + "resolved": "https://registry.npmjs.org/wdio-browserstack-service/-/wdio-browserstack-service-0.1.18.tgz", + "integrity": "sha512-6tISYMKzwr2oxx0yi2Q4GoFC2Mbq81iHhqxayacC4XgFR7QbmQkxwV8JPeq590AXhuhPqqmyuEGkMqc9fo/UoQ==", + "dev": true, + "requires": { + "browserstack-local": "^1.3.7", + "request": "^2.81.0", + "request-promise": "^4.2.1" + } + }, + "wdio-concise-reporter": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/wdio-concise-reporter/-/wdio-concise-reporter-0.1.2.tgz", + "integrity": "sha1-+kmA768kszWfHqQz73thYxVDphQ=", + "dev": true + }, + "wdio-dot-reporter": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/wdio-dot-reporter/-/wdio-dot-reporter-0.0.10.tgz", + "integrity": "sha512-A0TCk2JdZEn3M1DSG9YYbNRcGdx/YRw19lTiRpgwzH4qqWkO/oRDZRmi3Snn4L2j54KKTfPalBhlOtc8fojVgg==", + "dev": true + }, + "wdio-mocha-framework": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/wdio-mocha-framework/-/wdio-mocha-framework-0.6.4.tgz", + "integrity": "sha512-GZsXwoW60/fkkfqZJR/ZAdiALaM+hW+BbnTT9x214qPR4Pe5XeyYxhJNEdyf0dNI9625cMdkyZYaWoFHN5zDcA==", + "dev": true, + "requires": { + "babel-runtime": "^6.23.0", + "mocha": "^5.2.0", + "wdio-sync": "0.7.3" + } + }, + "wdio-spec-reporter": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/wdio-spec-reporter/-/wdio-spec-reporter-0.1.5.tgz", + "integrity": "sha512-MqvgTow8hFwhFT47q67JwyJyeynKodGRQCxF7ijKPGfsaG1NLssbXYc0JhiL7SiAyxnQxII0UxzTCd3I6sEdkg==", + "dev": true, + "requires": { + "babel-runtime": "~6.26.0", + "chalk": "^2.3.0", + "humanize-duration": "~3.15.0" + } + }, + "wdio-sync": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/wdio-sync/-/wdio-sync-0.7.3.tgz", + "integrity": "sha512-ukASSHOQmOxaz5HTILR0jykqlHBtAPsBpMtwhpiG0aW9uc7SO7PF+E5LhVvTG4ypAh+UGmY3rTjohOsqDr39jw==", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "fibers": "^3.0.0", + "object.assign": "^4.0.3" + } + }, + "webdriverio": { + "version": "4.14.4", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-4.14.4.tgz", + "integrity": "sha512-Knp2vzuzP5c5ybgLu+zTwy/l1Gh0bRP4zAr8NWcrStbuomm9Krn9oRF0rZucT6AyORpXinETzmeowFwIoo7mNA==", + "dev": true, + "requires": { + "archiver": "~2.1.0", + "babel-runtime": "^6.26.0", + "css-parse": "^2.0.0", + "css-value": "~0.0.1", + "deepmerge": "~2.0.1", + "ejs": "~2.5.6", + "gaze": "~1.1.2", + "glob": "~7.1.1", + "grapheme-splitter": "^1.0.2", + "inquirer": "~3.3.0", + "json-stringify-safe": "~5.0.1", + "mkdirp": "~0.5.1", + "npm-install-package": "~2.1.0", + "optimist": "~0.6.1", + "q": "~1.5.0", + "request": "^2.83.0", + "rgb2hex": "^0.1.9", + "safe-buffer": "~5.1.1", + "supports-color": "~5.0.0", + "url": "~0.11.0", + "wdio-dot-reporter": "~0.0.8", + "wgxpath": "~1.0.0" + }, + "dependencies": { + "ejs": { + "version": "2.5.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.9.tgz", + "integrity": "sha512-GJCAeDBKfREgkBtgrYSf9hQy9kTb3helv0zGdzqhM7iAkW8FA/ZF97VQDbwFiwIT8MQLLOe5VlPZOEvZAqtUAQ==", + "dev": true + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "supports-color": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.0.1.tgz", + "integrity": "sha512-7FQGOlSQ+AQxBNXJpVDj8efTA/FtyB5wcNE1omXXJ0cq6jm1jjDwuROlYDbnzHqdNPqliWFhcioCWSyav+xBnA==", + "dev": true, + "requires": { + "has-flag": "^2.0.0" + } + } + } + }, "webpack": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.12.0.tgz", @@ -15329,6 +15109,12 @@ "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", "dev": true }, + "wgxpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wgxpath/-/wgxpath-1.0.0.tgz", + "integrity": "sha1-7vikudVYzEla06mit1FZfs2a9pA=", + "dev": true + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -15344,12 +15130,6 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "window-size": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", - "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=", - "dev": true - }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -15404,12 +15184,6 @@ "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", "dev": true }, - "xregexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", - "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=", - "dev": true - }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", @@ -15448,6 +15222,18 @@ "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", "dev": true + }, + "zip-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-1.2.0.tgz", + "integrity": "sha1-qLxF9MG0lpnGuQGYuqyqzbzUugQ=", + "dev": true, + "requires": { + "archiver-utils": "^1.3.0", + "compress-commons": "^1.2.0", + "lodash": "^4.8.0", + "readable-stream": "^2.0.0" + } } } } diff --git a/package.json b/package.json index 1419f99c65f..13a35296e0b 100755 --- a/package.json +++ b/package.json @@ -49,7 +49,6 @@ "gulp-header": "^1.7.1", "gulp-if": "^2.0.2", "gulp-js-escape": "^1.0.1", - "gulp-optimize-js": "^1.1.0", "gulp-replace": "^1.0.0", "gulp-shell": "^0.5.2", "gulp-sourcemaps": "^2.6.0", From 23023365c9467167ab3e594ab486adb6f8b142cc Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Wed, 1 May 2019 11:29:16 -0700 Subject: [PATCH 042/146] Pubmatic: Making Adslot param optional (#3788) * changes for multiformat support * added new constant NATIVE_ASSETS in PubMatic adapter * removed NATIVE_ASSET_KEY reference in PubMatic adapter * removed reference of const NATIVE_ASSET_ID from PubMatic adapter * removed reference of const NATIVE_ASSET_DATA_TYPE in PubMatic adapter * using _commonNativeRequestObject in PubMatic adapter to avoid repeating code block * removed new-lines in const declaration * generating NATIVE_ASSET_REVERSE_ID from NATIVE_ASSETS * renamed NATIVE_ASSET_REVERSE_ID to NATIVE_ASSET_ID_TO_KEY_MAP * little modification in _checkParamDataType in PubMatic adapter * a minor improvement * using let instead of var * added NATIVE_ASSET_KEY_TO_ASSET_MAP and combining switch cases * lint update * removed some stale comments * using LOG_WARN_PREFIX * using const UNDEFINED * added a logWarn in catch * using arrow functions * code review changes * making adSlot parameter optional * added a test case for without adSlot config * changes to checkMediaType function * suppress warning of missing mediaTypes.banner for native and video impression * ignore fluid size, if present, in banner impression * fix for ignoring fluid size in banner impression * added relevant comments and test cases for fluid case in banner request * added sample config for multiformat adunit * commented redundant check * marking adSlot param as optional in examples * fixed some lint errors * removed commnented code * removed a trailing white space * removed a todo comment as suggested in review * deleting commented test cases --- modules/pubmaticBidAdapter.js | 16 ++---- modules/pubmaticBidAdapter.md | 8 +-- test/spec/modules/pubmaticBidAdapter_spec.js | 51 ++++++++++++++------ 3 files changed, 43 insertions(+), 32 deletions(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 6891285fcc2..c791acd9485 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -511,7 +511,7 @@ function _createImpressionObject(bid, conf) { impObj = { id: bid.bidId, - tagid: bid.params.adUnit, + tagid: bid.params.adUnit || undefined, bidfloor: _parseSlotParam('kadfloor', bid.params.kadfloor), secure: window.location.protocol === 'https:' ? 1 : 0, ext: { @@ -746,10 +746,6 @@ export const spec = { utils.logWarn(LOG_WARN_PREFIX + 'Error: publisherId is mandatory and cannot be numeric. Call to OpenBid will not be sent for ad unit: ' + JSON.stringify(bid)); return false; } - if (!utils.isStr(bid.params.adSlot)) { - utils.logWarn(LOG_WARN_PREFIX + 'Error: adSlotId is mandatory and cannot be numeric. Call to OpenBid will not be sent for ad unit: ' + JSON.stringify(bid)); - return false; - } // video ad validation if (bid.params.hasOwnProperty('video')) { if (!bid.params.video.hasOwnProperty('mimes') || !utils.isArray(bid.params.video.mimes) || bid.params.video.mimes.length === 0) { @@ -783,17 +779,11 @@ export const spec = { var blockedIabCategories = []; validBidRequests.forEach(originalBid => { bid = utils.deepClone(originalBid); + bid.params.adSlot = bid.params.adSlot || ''; _parseAdSlot(bid); if (bid.params.hasOwnProperty('video')) { - if (!(bid.params.adSlot && bid.params.adUnit && bid.params.adUnitIndex)) { - utils.logWarn(LOG_WARN_PREFIX + 'Skipping the non-standard adslot: ', bid.params.adSlot, JSON.stringify(bid)); - return; - } + // Nothing to do } else { - if (!(bid.params.adSlot && bid.params.adUnit && bid.params.adUnitIndex)) { - utils.logWarn(LOG_WARN_PREFIX + 'Skipping the non-standard adslot: ', bid.params.adSlot, JSON.stringify(bid)); - return; - } // If we have a native mediaType configured alongside banner, its ok if the banner size is not set in width and height // The corresponding banner imp object will not be generated, but we still want the native object to be sent, hence the following check if (!(bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(NATIVE)) && bid.params.width === 0 && bid.params.height === 0) { diff --git a/modules/pubmaticBidAdapter.md b/modules/pubmaticBidAdapter.md index 16a3b203e20..1948acba40f 100644 --- a/modules/pubmaticBidAdapter.md +++ b/modules/pubmaticBidAdapter.md @@ -25,7 +25,7 @@ var adUnits = [ bidder: 'pubmatic', params: { publisherId: '156209', // required - adSlot: 'pubmatic_test2', // required + adSlot: 'pubmatic_test2', // optional pmzoneid: 'zone1, zone11', // optional lat: '40.712775', // optional lon: '-74.005973', // optional @@ -56,7 +56,7 @@ var adVideoAdUnits = [ bidder: 'pubmatic', params: { publisherId: '156209', // required - adSlot: 'pubmatic_video1', // required + adSlot: 'pubmatic_video1', // optional video: { mimes: ['video/mp4','video/x-flv'], // required skippable: true, // optional @@ -104,7 +104,7 @@ var adUnits = [ bidder: 'pubmatic', params: { publisherId: '156295', // required - adSlot: 'pubmatic_test2@1x1', // required + adSlot: 'pubmatic_test2@1x1', // optional } }] }]; @@ -146,7 +146,7 @@ var adUnits = [ bidder: 'pubmatic', params: { publisherId: '156209', // required - adSlot: 'pubmatic_test2@300x250', // required + adSlot: 'pubmatic_test2@300x250', // optional pmzoneid: 'zone1, zone11', // optional lat: '40.712775', // optional lon: '-74.005973', // optional diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index bf008574027..b25fd22f822 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -668,27 +668,15 @@ describe('PubMatic adapter', function () { expect(isValid).to.equal(false); }); - it('invalid bid case: adSlot not passed', function () { - let validBid = { - bidder: 'pubmatic', - params: { - publisherId: '301' - } - }, - isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(false); - }); - - it('invalid bid case: adSlot is not string', function () { + it('valid bid case: adSlot is not passed', function () { let validBid = { bidder: 'pubmatic', params: { - publisherId: '301', - adSlot: 15671365 + publisherId: '301' } }, isValid = spec.isBidRequestValid(validBid); - expect(isValid).to.equal(false); + expect(isValid).to.equal(true); }); }); @@ -742,6 +730,39 @@ describe('PubMatic adapter', function () { expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); }); + it('Request params check: without adSlot', function () { + delete bidRequests[0].params.adSlot; + let request = spec.buildRequests(bidRequests); + let data = JSON.parse(request.data); + expect(data.at).to.equal(1); // auction type + expect(data.cur[0]).to.equal('USD'); // currency + expect(data.site.domain).to.be.a('string'); // domain should be set + expect(data.site.page).to.equal(bidRequests[0].params.kadpageurl); // forced pageURL + expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id + expect(data.site.ext).to.exist.and.to.be.an('object'); // dctr parameter + expect(data.site.ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); + expect(data.user.yob).to.equal(parseInt(bidRequests[0].params.yob)); // YOB + expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender + expect(data.device.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.device.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.user.geo.lat).to.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.user.geo.lon).to.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version + expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].transactionId); // Prebid TransactionId + expect(data.ext.wrapper.wiid).to.equal(bidRequests[0].params.wiid); // OpenWrap: Wrapper Impression ID + expect(data.ext.wrapper.profile).to.equal(parseInt(bidRequests[0].params.profId)); // OpenWrap: Wrapper Profile ID + expect(data.ext.wrapper.version).to.equal(parseInt(bidRequests[0].params.verId)); // OpenWrap: Wrapper Profile Version ID + + expect(data.imp[0].id).to.equal(bidRequests[0].bidId); // Prebid bid id is passed as id + expect(data.imp[0].bidfloor).to.equal(parseFloat(bidRequests[0].params.kadfloor)); // kadfloor + expect(data.imp[0].tagid).to.deep.equal(undefined); // tagid + expect(data.imp[0].banner.w).to.equal(728); // width + expect(data.imp[0].banner.h).to.equal(90); // height + expect(data.imp[0].banner.format).to.deep.equal([{w: 160, h: 600}]); + expect(data.imp[0].ext.pmZoneId).to.equal(bidRequests[0].params.pmzoneid.split(',').slice(0, 50).map(id => id.trim()).join()); // pmzoneid + expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); + }); + it('Request params multi size format object check', function () { let bidRequests = [ { From ef45260a202f31b5d1ad9331f5d30e4128ff393a Mon Sep 17 00:00:00 2001 From: Bret Gorsline Date: Wed, 1 May 2019 15:06:04 -0400 Subject: [PATCH 043/146] Prebid 2.13.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 13a35296e0b..0768304e10b 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.13.0-pre", + "version": "2.13.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 1c245444c5bec189b05798920035d98fe1195d7e Mon Sep 17 00:00:00 2001 From: Bret Gorsline Date: Wed, 1 May 2019 15:43:10 -0400 Subject: [PATCH 044/146] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0768304e10b..72ddc2e6e53 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.13.0", + "version": "2.14.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 5ba930d20f2aeb4318372af9afda0b676fc820e0 Mon Sep 17 00:00:00 2001 From: sumit116 Date: Thu, 2 May 2019 17:23:00 +0530 Subject: [PATCH 045/146] add adUnitCodes as param for setTargetingForAst() (#3792) --- src/prebid.js | 5 +++-- src/targeting.js | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/prebid.js b/src/prebid.js index d475a715bbf..1b6a40aff97 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -260,16 +260,17 @@ $$PREBID_GLOBAL$$.setTargetingForGPTAsync = function (adUnit, customSlotMatching /** * Set query string targeting on all AST (AppNexus Seller Tag) ad units. Note that this function has to be called after all ad units on page are defined. For working example code, see [Using Prebid.js with AppNexus Publisher Ad Server](http://prebid.org/dev-docs/examples/use-prebid-with-appnexus-ad-server.html). + * @param {(string|string[])} adUnitCode adUnitCode or array of adUnitCodes * @alias module:pbjs.setTargetingForAst */ -$$PREBID_GLOBAL$$.setTargetingForAst = function() { +$$PREBID_GLOBAL$$.setTargetingForAst = function(adUnitCodes) { utils.logInfo('Invoking $$PREBID_GLOBAL$$.setTargetingForAn', arguments); if (!targeting.isApntagDefined()) { utils.logError('window.apntag is not defined on the page'); return; } - targeting.setTargetingForAst(); + targeting.setTargetingForAst(adUnitCodes); // emit event events.emit(SET_TARGETING, targeting.getAllTargeting()); diff --git a/src/targeting.js b/src/targeting.js index 90897b8d956..6caa4029c9c 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -248,10 +248,11 @@ export function newTargeting(auctionManager) { }; /** + * @param {(string|string[])} adUnitCode adUnitCode or array of adUnitCodes * Sets targeting for AST */ - targeting.setTargetingForAst = function() { - let astTargeting = targeting.getAllTargeting(); + targeting.setTargetingForAst = function(adUnitCodes) { + let astTargeting = targeting.getAllTargeting(adUnitCodes); try { targeting.resetPresetTargetingAST(); From f04f91282e5b0e209bee1921d25dd62a7302106a Mon Sep 17 00:00:00 2001 From: Alexander Fominov Date: Fri, 3 May 2019 01:16:02 +0300 Subject: [PATCH 046/146] Add HPMD Network bid adapter (#3764) --- modules/hpmdnetworkBidAdapter.js | 96 ++++++++++++ modules/hpmdnetworkBidAdapter.md | 32 ++++ .../modules/hpmdnetworkBidAdapter_spec.js | 148 ++++++++++++++++++ 3 files changed, 276 insertions(+) create mode 100644 modules/hpmdnetworkBidAdapter.js create mode 100644 modules/hpmdnetworkBidAdapter.md create mode 100644 test/spec/modules/hpmdnetworkBidAdapter_spec.js diff --git a/modules/hpmdnetworkBidAdapter.js b/modules/hpmdnetworkBidAdapter.js new file mode 100644 index 00000000000..ad17caba7bc --- /dev/null +++ b/modules/hpmdnetworkBidAdapter.js @@ -0,0 +1,96 @@ +import { registerBidder } from 'src/adapters/bidderFactory'; +import { BANNER } from 'src/mediaTypes'; + +const BIDDER_CODE = 'hpmdnetwork'; +const BIDDER_CODE_ALIAS = 'hpmd'; +const HPMDNETWORK_HOST = '//banner.hpmdnetwork.ru/bidder/request'; +const DEFAULT_TTL = 300; +const DEFAULT_CURRENCY = 'RUB'; + +export const spec = { + code: BIDDER_CODE, + aliases: [ BIDDER_CODE_ALIAS ], + supportedMediaTypes: [ BANNER ], + isBidRequestValid: isBidRequestValid, + buildRequests: buildRequests, + interpretResponse: interpretResponse, +}; + +function isBidRequestValid(bid) { + const { placementId } = bid.params; + return !!placementId; +} + +function buildRequests(validBidRequests, bidderRequest) { + const payload = {}; + payload.places = []; + + validBidRequests.forEach((bidRequest) => { + const place = { + id: bidRequest.bidId, + placementId: bidRequest.params.placementId + '', + }; + payload.places.push(place); + }); + + payload.url = bidderRequest.refererInfo.referer; + payload.settings = { currency: DEFAULT_CURRENCY }; + + return { + method: 'POST', + url: HPMDNETWORK_HOST, + data: payload, + }; +} + +function interpretResponse(serverResponse) { + const { body } = serverResponse; + const bidResponses = []; + + if (body.bids) { + body.bids.forEach((bid) => { + const size = getCreativeSize(bid); + const bidResponse = { + requestId: bid.id, + cpm: bid.cpm, + ad: wrapDisplayUrl(bid.displayUrl), + width: size.width, + height: size.height, + creativeId: bid.creativeId || generateRandomInt(), + currency: bid.currency || DEFAULT_CURRENCY, + netRevenue: true, + ttl: bid.ttl || DEFAULT_TTL, + }; + + bidResponses.push(bidResponse); + }); + } + + return bidResponses; +} + +function wrapDisplayUrl(displayUrl) { + return ``; +} + +function getCreativeSize(creativeSize) { + const size = { + width: 1, + height: 1, + }; + + if (!!creativeSize.width && creativeSize.width !== -1) { + size.width = creativeSize.width; + } + if (!!creativeSize.height && creativeSize.height !== -1) { + size.height = creativeSize.height; + } + + return size; +} + +function generateRandomInt() { + return Math.random().toString(16).substring(2); +} + +registerBidder(spec); diff --git a/modules/hpmdnetworkBidAdapter.md b/modules/hpmdnetworkBidAdapter.md new file mode 100644 index 00000000000..b7ac51a9311 --- /dev/null +++ b/modules/hpmdnetworkBidAdapter.md @@ -0,0 +1,32 @@ +# Overview + +Module Name: HPMD Network Bidder Adapter + +Module Type: Bidder Adapter + +Maintainer: a.fominov@hpmdnetwork.ru + +# Description + +You can use this adapter to get a bid from HPMD Network. + +About us : https://www.hpmdnetwork.ru/ + + +# Test Parameters +```javascript + var adUnits = [ + { + code: 'test-div', + bids: [ + { + bidder: "hpmdnetwork", + params: { + placementId: "123" + } + } + ] + } + ]; +``` + diff --git a/test/spec/modules/hpmdnetworkBidAdapter_spec.js b/test/spec/modules/hpmdnetworkBidAdapter_spec.js new file mode 100644 index 00000000000..37ec44f07c4 --- /dev/null +++ b/test/spec/modules/hpmdnetworkBidAdapter_spec.js @@ -0,0 +1,148 @@ +import { expect } from 'chai'; +import { spec } from 'modules/hpmdnetworkBidAdapter'; + +describe('HPMDNetwork Adapter', function() { + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + const validBid = { + bidder: 'hpmdnetwork', + params: { + placementId: '1' + } + }; + + expect(spec.isBidRequestValid(validBid)).to.equal(true); + }); + + it('should return false for when required params are not passed', function () { + const invalidBid = { + bidder: 'hpmdnetwork', + params: {} + }; + + expect(spec.isBidRequestValid(invalidBid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const bidRequests = [ + { + bidId: 'bid1', + bidder: 'hpmdnetwork', + params: { + placementId: '1' + } + }, + { + bidId: 'bid2', + bidder: 'hpmdnetwork', + params: { + placementId: '2', + } + } + ]; + const bidderRequest = { + refererInfo: { + referer: 'https://example.com?foo=bar' + } + }; + + const bidRequest = spec.buildRequests(bidRequests, bidderRequest); + + it('should build single POST request for multiple bids', function() { + expect(bidRequest.method).to.equal('POST'); + expect(bidRequest.url).to.equal('//banner.hpmdnetwork.ru/bidder/request'); + expect(bidRequest.data).to.be.an('object'); + expect(bidRequest.data.places).to.be.an('array'); + expect(bidRequest.data.places).to.have.lengthOf(2); + }); + + it('should pass bid parameters', function() { + const place1 = bidRequest.data.places[0]; + const place2 = bidRequest.data.places[1]; + + expect(place1.placementId).to.equal('1'); + expect(place2.placementId).to.equal('2'); + expect(place1.id).to.equal('bid1'); + expect(place2.id).to.equal('bid2'); + }); + + it('should pass site parameters', function() { + const url = bidRequest.data.url; + + expect(url).to.be.an('String'); + expect(url).to.equal('https://example.com?foo=bar'); + }); + + it('should pass settings', function() { + const settings = bidRequest.data.settings; + + expect(settings).to.be.an('object'); + expect(settings.currency).to.equal('RUB'); + }); + }); + + describe('interpretResponse', function () { + const serverResponse = { + body: { + 'bids': + [ + { + 'cpm': 20, + 'currency': 'RUB', + 'displayUrl': '//banner.hpmdnetwork.ru/bidder/display?dbid=0&vbid=168', + 'id': '1', + 'creativeId': '11111', + }, + { + 'cpm': 30, + 'currency': 'RUB', + 'displayUrl': '//banner.hpmdnetwork.ru/bidder/display?dbid=0&vbid=170', + 'id': '2', + 'creativeId': '22222', + 'width': 300, + 'height': 250, + }, + ] + } + }; + + const bids = spec.interpretResponse(serverResponse); + + it('should return empty array for response with no bids', function() { + const emptyBids = spec.interpretResponse({ body: {} }); + + expect(emptyBids).to.have.lengthOf(0); + }); + + it('should parse all bids from response', function() { + expect(bids).to.have.lengthOf(2); + }); + + it('should parse bid without sizes', function() { + expect(bids[0].requestId).to.equal('1'); + expect(bids[0].cpm).to.equal(20); + expect(bids[0].width).to.equal(1); + expect(bids[0].height).to.equal(1); + expect(bids[0].ttl).to.equal(300); + expect(bids[0].currency).to.equal('RUB'); + expect(bids[0]).to.have.property('creativeId'); + expect(bids[0].creativeId).to.equal('11111'); + expect(bids[0].netRevenue).to.equal(true); + expect(bids[0].ad).to.include(''); + }); + + it('should parse bid with sizes', function() { + expect(bids[1].requestId).to.equal('2'); + expect(bids[1].cpm).to.equal(30); + expect(bids[1].width).to.equal(300); + expect(bids[1].height).to.equal(250); + expect(bids[1].ttl).to.equal(300); + expect(bids[1].currency).to.equal('RUB'); + expect(bids[1]).to.have.property('creativeId'); + expect(bids[1].creativeId).to.equal('22222'); + expect(bids[1].netRevenue).to.equal(true); + expect(bids[1].ad).to.include(''); + }); + }); +}); From 38e50d2a72f551f92fa4741385e3aff8d47ba7bb Mon Sep 17 00:00:00 2001 From: Alex Pashkov Date: Fri, 3 May 2019 18:19:28 +0300 Subject: [PATCH 047/146] VI Bid Adapter Changes (#3748) * Add new viBidAdapter * Change request schema * Min area is 1 * Send bidId * Refactor getDocumentHeight * Add getDocumentHeight test * Remove getWindowHeight * Add getOffset tests * Add getWindowParents tests * Add getTopmostReachableWindow test * Add curWindow * Add more tests * Add getIframeType tests * Rename hostile->nonfriendly * Add more params to bid request * Add getFrameElements tests * Add area tests * Add more tests * Add more tests * Add getViUserId * Add fallback to localStorage * Change bid URL * Add media-types * Remove user id * withCredentials: true * Add https * Remove getViUserId from spec * Add mergeSizes * Add useSizes param --- modules/viBidAdapter.js | 423 +++++++++-- modules/viBidAdapter.md | 1 + test/spec/modules/viBidAdapter_spec.js | 997 ++++++++++++++++++++++--- 3 files changed, 1248 insertions(+), 173 deletions(-) diff --git a/modules/viBidAdapter.js b/modules/viBidAdapter.js index 5166277ff81..93ead1c2380 100644 --- a/modules/viBidAdapter.js +++ b/modules/viBidAdapter.js @@ -1,69 +1,388 @@ -import * as utils from '../src/utils'; import { registerBidder } from '../src/adapters/bidderFactory'; -import { BANNER } from '../src/mediaTypes'; +import * as mediaTypes from '../src/mediaTypes'; -const BIDDER_CODE = 'vi'; -const SUPPORTED_MEDIA_TYPES = [BANNER]; +export function get(path, obj, notFound) { + path = typeof path === 'string' ? path.split('.') : path; -function isBidRequestValid(bid) { - return !!(bid.params.pubId); + while (path.length) { + const [key] = path; + if (!(obj instanceof Object) || !(key in obj)) return notFound; + obj = obj[key]; + path = path.slice(1); + } + + return obj; } -function buildRequests(bidReqs) { - let imps = []; - utils._each(bidReqs, function (bid) { - imps.push({ - id: bid.bidId, - sizes: utils.parseSizesInput(bid.sizes).map(size => size.split('x')), - bidFloor: parseFloat(bid.params.bidFloor) > 0 ? bid.params.bidFloor : 0 - }); - }); +export function merge(a, b, fn = a => a) { + const res = {}; + + for (const key in a) { + if (key in b) { + res[key] = fn(a[key], b[key]); + } else { + res[key] = a[key]; + } + } + + for (const key in b) { + if (!(key in a)) res[key] = b[key]; + } + + return res; +} + +export function ratioToPercentageCeil(x) { + return Math.ceil(x * 100); +} - const bidRequest = { - id: bidReqs[0].requestId, - imps: imps, - publisherId: utils.getBidIdParameter('pubId', bidReqs[0].params), - siteId: utils.getBidIdParameter('siteId', bidReqs[0].params), - cat: utils.getBidIdParameter('cat', bidReqs[0].params), - language: utils.getBidIdParameter('lang', bidReqs[0].params), - domain: utils.getTopWindowLocation().hostname, - page: utils.getTopWindowUrl(), - referrer: utils.getTopWindowReferrer() +export function getDocumentHeight(curDocument = document) { + return Math.max( + get('body.clientHeight', curDocument, 0), + get('body.scrollHeight', curDocument, 0), + get('body.offsetHeight', curDocument, 0), + get('documentElement.clientHeight', curDocument, 0), + get('documentElement.scrollHeight', curDocument, 0), + get('documentElement.offsetHeight', curDocument, 0) + ); +} + +export function getOffset(element) { + const rect = element.getBoundingClientRect(); + const elementWindow = getElementWindow(element); + if (!elementWindow) throw new Error('cannot get element window'); + const scrollLeft = + elementWindow.pageXOffset || get('documentElement.scrollLeft', document, 0); + const scrollTop = + elementWindow.pageYOffset || get('documentElement.scrollTop', document, 0); + return { + top: rect.top + scrollTop, + right: rect.right + scrollLeft, + bottom: rect.bottom + scrollTop, + left: rect.left + scrollLeft }; +} + +var IframeType; + +(function(IframeType) { + IframeType['safeframe'] = 'safeframe'; + IframeType['friendly'] = 'friendly'; + IframeType['nonfriendly'] = 'nonfriendly'; +})(IframeType || (IframeType = {})); + +export function getWindowParents(curWindow = window) { + const parents = []; + + while (curWindow && curWindow.parent && curWindow !== curWindow.parent) { + parents.push(curWindow.parent); + curWindow = curWindow.parent; + } + + return parents; +} + +export function getTopmostReachableWindow(curWindow = window) { + const parents = getWindowParents(curWindow); + return parents.length ? parents[parents.length - 1] : curWindow; +} + +export function topDocumentIsReachable(curWindow = window) { + if (!isInsideIframe(curWindow)) return true; + const windowParents = getWindowParents(curWindow); + + try { + const topWindow = windowParents[windowParents.length - 1]; + + return topWindow === curWindow.top && !!curWindow.top.document; + } catch (e) { + return false; + } +} + +export function isInsideIframe(curWindow = window) { + return curWindow !== curWindow.top; +} + +export function isInsideSafeframe(curWindow = window) { + return !topDocumentIsReachable(curWindow) && !!curWindow.$sf; +} + +export function isInsideFriendlyIframe(curWindow = window) { + return isInsideIframe(curWindow) && topDocumentIsReachable(curWindow); +} + +export function getIframeType(curWindow = window) { + if (!isInsideIframe(curWindow)) return; + if (isInsideSafeframe(curWindow)) return IframeType.safeframe; + if (isInsideFriendlyIframe(curWindow)) return IframeType.friendly; + return IframeType.nonfriendly; +} + +function getElementWindow(element) { + return element.ownerDocument + ? element.ownerDocument.defaultView + : element.defaultView; +} + +const NO_CUTS = { + top: 0, + right: 0, + bottom: 0, + left: 0 +}; + +export function getRectCuts(rect, vh, vw, vCuts = NO_CUTS) { + let { top, left } = rect; + const { bottom, right } = rect; + top = top + vCuts.top; + left = left + vCuts.left; + vh = vh + vCuts.bottom; + vw = vw + vCuts.right; return { - method: 'POST', - url: `//pb.vi-serve.com/prebid/bid`, - data: JSON.stringify(bidRequest), - options: {contentType: 'application/json', withCredentials: false} + bottom: Math.min(0, vh - bottom), + left: Math.min(0, left), + right: Math.min(0, vw - right), + top: Math.min(0, top) }; } -function interpretResponse(bids) { - let responses = []; - utils._each(bids.body, function(bid) { - responses.push({ - requestId: bid.id, - cpm: parseFloat(bid.price), - width: parseInt(bid.width, 10), - height: parseInt(bid.height, 10), - creativeId: bid.creativeId, - dealId: bid.dealId || null, - currency: 'USD', - netRevenue: true, - mediaType: BANNER, - ad: decodeURIComponent(`${bid.ad}`), - ttl: 60000 +export function getFrameElements(curWindow = window) { + const frameElements = []; + + while (curWindow && curWindow.frameElement) { + frameElements.unshift(curWindow.frameElement); + curWindow = + curWindow.frameElement.ownerDocument && + curWindow.frameElement.ownerDocument.defaultView; + } + + return frameElements; +} + +export function getElementCuts(element, vCuts) { + const window = getElementWindow(element); + return getRectCuts( + element.getBoundingClientRect(), + window ? window.innerHeight : 0, + window ? window.innerWidth : 0, + vCuts + ); +} + +export function area(width, height, areaCuts = NO_CUTS) { + const { top, right, bottom, left } = areaCuts; + return Math.max(0, (width + left + right) * (height + top + bottom)); +} + +export function getInViewRatio(element) { + const elements = [...getFrameElements(getElementWindow(element)), element]; + const vCuts = elements.reduce( + (vCuts, element) => getElementCuts(element, vCuts), + NO_CUTS + ); + return ( + area(element.offsetWidth || 1, element.offsetHeight || 1, vCuts) / + area(element.offsetWidth || 1, element.offsetHeight || 1) + ); +} + +export function getInViewRatioInsideTopFrame(element) { + const elements = [...getFrameElements().slice(1), element]; + const vCuts = elements.reduce( + (vCuts, element) => getElementCuts(element, vCuts), + NO_CUTS + ); + return ( + area(element.offsetWidth, element.offsetHeight, vCuts) / + area(element.offsetWidth, element.offsetHeight) + ); +} + +export function getMayBecomeVisible(element) { + return !isInsideIframe() || !!getInViewRatioInsideTopFrame(element); +} + +export function getInViewPercentage(element) { + return ratioToPercentageCeil(getInViewRatio(element)); +} + +export function getOffsetTopDocument(element) { + return [...getFrameElements(getElementWindow(element)), element].reduce( + (acc, elem) => merge(acc, getOffset(elem), (a, b) => a + b), + { + top: 0, + right: 0, + bottom: 0, + left: 0 + } + ); +} + +export function getOffsetTopDocumentPercentage(element) { + const elementWindow = getElementWindow(element); + if (!elementWindow) throw new Error('cannot get element window'); + if (!topDocumentIsReachable(elementWindow)) { + throw new Error("top window isn't reachable"); + } + const topWindow = getTopmostReachableWindow(elementWindow); + const documentHeight = getDocumentHeight(topWindow.document); + return ratioToPercentageCeil( + getOffsetTopDocument(element).top / documentHeight + ); +} + +export function getOffsetToView(element) { + const elemWindow = getElementWindow(element); + if (!elemWindow) throw new Error('cannot get element window'); + const topWindow = getTopmostReachableWindow(elemWindow); + const { top, bottom } = getOffsetTopDocument(element); + const topWindowHeight = topWindow.innerHeight; + + if (bottom < topWindow.scrollY) return bottom - topWindow.scrollY; + + if (top > topWindow.scrollY + topWindowHeight) { + return top - topWindow.scrollY - topWindowHeight; + } + + return 0; +} + +export function getOffsetToViewPercentage(element) { + return ratioToPercentageCeil( + getOffsetToView(element) / + getDocumentHeight( + getTopmostReachableWindow(getElementWindow(element)).document + ) + ); +} + +export function getViewabilityDescription(element) { + let iframeType; + try { + if (!element) { + return { + error: 'no element' + }; + } + iframeType = getIframeType(getElementWindow(element)); + if (!iframeType || iframeType === IframeType.friendly) { + const inViewPercentage = getInViewPercentage(element); + return { + inView: inViewPercentage, + hidden: !inViewPercentage && !getMayBecomeVisible(element), + offsetTop: getOffsetTopDocumentPercentage(element), + offsetView: getOffsetToViewPercentage(element), + iframeType + }; + } + return { + iframeType + }; + } catch (error) { + return { + iframeType, + error: error.message + }; + } +} + +export function mergeArrays(hashFn, ...args) { + const seen = {}; + const merged = []; + args.forEach(sizes => { + sizes.forEach(size => { + const key = hashFn(size); + if (!(key in seen)) { + seen[key] = true; + merged.push(size); + } }); }); - return responses; + return merged; } -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: SUPPORTED_MEDIA_TYPES, - isBidRequestValid, - buildRequests, - interpretResponse -} +const spec = { + code: 'vi', + supportedMediaTypes: [mediaTypes.VIDEO, mediaTypes.BANNER], + + isBidRequestValid({ adUnitCode, params: { pubId, lang, cat } = {} }) { + return [pubId, lang, cat].every(x => typeof x === 'string'); + }, + + /** + * + * @param bidRequests + * @param bidderRequest + * @return { + * {method: string, + * data: { + imps: { + bidId: string, + adUnitCode: string, + sizes: [[number, number]], + pubId: string, + lang: string, + cat: string, + iframeType: string | undefined, + error: string | null, + inView: number, + offsetTop: number, + offsetView: number, + hidden: boolean, + bidFloor: number + }[], + refererInfo: { + referer: string + reachedTop: boolean, + numIframes: number, + stack: string[] + canonicalUrl: string + } + }, + * options: {withCredentials: boolean, contentType: string}, url: string}} + */ + buildRequests(bidRequests, bidderRequest) { + return { + method: 'POST', + url: 'https://pb.vi-serve.com/prebid/bid', + data: { + refererInfo: bidderRequest.refererInfo, + imps: bidRequests.map( + ({ bidId, adUnitCode, sizes, params, mediaTypes }) => { + const slot = document.getElementById(adUnitCode); + const bannerSizes = get('banner.sizes', mediaTypes); + const playerSize = get('video.playerSize', mediaTypes); + + const sizesToMerge = []; + if (!params.useSizes) { + if (sizes) sizesToMerge.push(sizes); + if (bannerSizes) sizesToMerge.push(bannerSizes); + if (playerSize) sizesToMerge.push(playerSize); + } else if (params.useSizes === 'banner' && bannerSizes) { + sizesToMerge.push(bannerSizes); + } else if (params.useSizes === 'video' && playerSize) { + sizesToMerge.push(playerSize); + } + return { + bidId, + adUnitCode, + sizes: mergeArrays(x => x.join(','), ...sizesToMerge), + ...getViewabilityDescription(slot), + ...params + }; + } + ) + }, + options: { + contentType: 'application/json', + withCredentials: true + } + }; + }, + interpretResponse({ body }) { + return body; + } +}; registerBidder(spec); diff --git a/modules/viBidAdapter.md b/modules/viBidAdapter.md index 23288024fcc..2608ccc4adb 100644 --- a/modules/viBidAdapter.md +++ b/modules/viBidAdapter.md @@ -38,4 +38,5 @@ var adUnits = [{ | `lang` | required | Ad language, in ISO 639-1 language code format | 'en-US', 'es-ES', 'de' | | `cat` | required | Ad IAB category (top-level or subcategory), single one supported | 'IAB1', 'IAB9-1' | | `bidFloor` | optional | Lowest value of expected bid price | 0.001 | +| `useSizes` | optional | Specifies from which section of the config sizes are taken, possible values are 'banner', 'video'. If omitted, sizes from both sections are merged. | 'banner' | diff --git a/test/spec/modules/viBidAdapter_spec.js b/test/spec/modules/viBidAdapter_spec.js index 2468da0cfaf..b12d534ff02 100644 --- a/test/spec/modules/viBidAdapter_spec.js +++ b/test/spec/modules/viBidAdapter_spec.js @@ -1,139 +1,894 @@ -import { expect } from 'chai'; -import { spec } from 'modules/viBidAdapter'; -import { newBidder } from 'src/adapters/bidderFactory'; - -const ENDPOINT = `//pb.vi-serve.com/prebid/bid`; - -describe('viBidAdapter', function() { - newBidder(spec); - - describe('isBidRequestValid', function () { - let bid = { - 'bidder': 'vi', - 'params': { - 'pubId': 'sb_test', - 'lang': 'en-US', - 'cat': 'IAB1', - 'bidFloor': 0.05 - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [320, 480] - ], - 'bidId': '29b891ad542377', - 'bidderRequestId': '1dc9a08206a57b', - 'requestId': '24176695-e3f0-44db-815b-ed97cf5ad49b', - 'placementCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '474da635-9cf0-4188-a3d9-58961be8f905' - }; +import { + ratioToPercentageCeil, + merge, + getDocumentHeight, + getOffset, + getWindowParents, + getRectCuts, + getTopmostReachableWindow, + topDocumentIsReachable, + isInsideIframe, + isInsideSafeframe, + getIframeType, + getFrameElements, + getElementCuts, + getInViewRatio, + getMayBecomeVisible, + getInViewPercentage, + getInViewRatioInsideTopFrame, + getOffsetTopDocument, + getOffsetTopDocumentPercentage, + getOffsetToView, + getOffsetToViewPercentage, + area, + get, + getViewabilityDescription, + mergeArrays +} from 'modules/viBidAdapter'; - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); +describe('ratioToPercentageCeil', () => { + it('1 converts to percentage', () => + expect(ratioToPercentageCeil(0.01)).to.equal(1)); + it('2 converts to percentage', () => + expect(ratioToPercentageCeil(0.00000000001)).to.equal(1)); + it('3 converts to percentage', () => + expect(ratioToPercentageCeil(0.5)).to.equal(50)); + it('4 converts to percentage', () => + expect(ratioToPercentageCeil(1)).to.equal(100)); + it('5 converts to percentage', () => + expect(ratioToPercentageCeil(0.99)).to.equal(99)); + it('6 converts to percentage', () => + expect(ratioToPercentageCeil(0.990000000000001)).to.equal(100)); +}); - it('should return false when pubId not passed', function () { - bid.params.pubId = undefined; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); +describe('merge', () => { + it('merges two objects', () => { + expect( + merge({ a: 1, b: 2, d: 0 }, { a: 2, b: 2, c: 3 }, (a, b) => a + b) + ).to.deep.equal({ a: 3, b: 4, c: 3, d: 0 }); }); +}); - describe('buildRequests', function () { - let bidRequests = [{ - 'bidder': 'vi', - 'params': { - 'pubId': 'sb_test', - 'lang': 'en-US', - 'cat': 'IAB1', - 'bidFloor': 0.05 +describe('getDocumentHeight', () => { + [ + { + curDocument: { + body: { + clientHeight: 0, + offsetHeight: 0, + scrollHeight: 0 + }, + documentElement: { + clientHeight: 0, + offsetHeight: 0, + scrollHeight: 0 + } }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [320, 480] - ], - 'bidId': '29b891ad542377', - 'bidderRequestId': '1dc9a08206a57b', - 'requestId': '24176695-e3f0-44db-815b-ed97cf5ad49b', - 'placementCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '474da635-9cf0-4188-a3d9-58961be8f905' - }]; - - const request = spec.buildRequests(bidRequests); - - it('POST bid request to vi', function () { - expect(request.method).to.equal('POST'); - }); + expected: 0 + }, + { + curDocument: { + body: { + clientHeight: 0, + offsetHeight: 13, + scrollHeight: 24 + }, + documentElement: { + clientHeight: 0, + offsetHeight: 0, + scrollHeight: 0 + } + }, + expected: 24 + }, + { + curDocument: { + body: { + clientHeight: 0, + offsetHeight: 13, + scrollHeight: 24 + }, + documentElement: { + clientHeight: 100, + offsetHeight: 50, + scrollHeight: 30 + } + }, + expected: 100 + } + ].forEach(({ curDocument, expected }) => + expect(getDocumentHeight(curDocument)).to.be.equal(expected) + ); +}); - it('check endpoint URL', function () { - expect(request.url).to.equal(ENDPOINT) - }); +describe('getOffset', () => { + [ + { + element: { + ownerDocument: { + defaultView: { + pageXOffset: 0, + pageYOffset: 0 + } + }, + getBoundingClientRect: () => ({ + top: 0, + right: 0, + bottom: 0, + left: 0 + }) + }, + expected: { + top: 0, + right: 0, + bottom: 0, + left: 0 + } + } + ].forEach(({ description, element, expected }, i) => + it( + 'returns element offsets from the document edges (including scroll): ' + + i, + () => expect(getOffset(element)).to.be.deep.equal(expected) + ) + ); + it('Throws when there is no window', () => + expect( + getOffset.bind(null, { + ownerDocument: { + defaultView: null + }, + getBoundingClientRect: () => ({ + top: 0, + right: 0, + bottom: 0, + left: 0 + }) + }) + ).to.throw()); +}); + +describe('getWindowParents', () => { + const win = {}; + win.top = win; + win.parent = win; + const win1 = { top: win, parent: win }; + const win2 = { top: win, parent: win1 }; + const win3 = { top: win, parent: win2 }; + + it('get parents up to the top', () => + expect(getWindowParents(win3)).to.be.deep.equal([win2, win1, win])); +}); + +describe('getTopmostReachableWindow', () => { + const win = {}; + win.top = win; + win.parent = win; + const win1 = { top: win, parent: win }; + const win2 = { top: win, parent: win1 }; + const win3 = { top: win, parent: win2 }; + + it('get parents up to the top', () => + expect(getTopmostReachableWindow(win3)).to.be.equal(win)); +}); + +const topWindow = { document, frameElement: 0 }; +topWindow.top = topWindow; +topWindow.parent = topWindow; +const topFrameElement = { + ownerDocument: { + defaultView: topWindow + } +}; +const frameWindow1 = { + top: topWindow, + parent: topWindow, + frameElement: topFrameElement +}; +const frameElement1 = { + ownerDocument: { + defaultView: frameWindow1 + } +}; +const frameWindow2 = { + top: topWindow, + parent: frameWindow1, + frameElement: frameElement1 +}; +const frameElement2 = { + ownerDocument: { + defaultView: frameWindow2 + } +}; +const frameWindow3 = { + top: topWindow, + parent: frameWindow2, + frameElement: frameElement2 +}; + +describe('topDocumentIsReachable', () => { + it('returns true if it no inside iframe', () => + expect(topDocumentIsReachable(topWindow)).to.be.true); + it('returns true if it can access top document', () => + expect(topDocumentIsReachable(frameWindow3)).to.be.true); +}); + +describe('isInsideIframe', () => { + it('returns true if window !== window.top', () => + expect(isInsideIframe(topWindow)).to.be.false); + it('returns true if window !== window.top', () => + expect(isInsideIframe(frameWindow1)).to.be.true); +}); + +const safeframeWindow = { $sf: {} }; + +describe('isInsideSafeframe', () => { + it('returns true if top window is not reachable and window.$sf is defined', () => + expect(isInsideSafeframe(safeframeWindow)).to.be.true); +}); + +const hostileFrameWindow = {}; + +describe('getIframeType', () => { + it('returns undefined when is not inside iframe', () => + expect(getIframeType(topWindow)).to.be.undefined); + it("returns 'safeframe' when inside sf", () => + expect(getIframeType(safeframeWindow)).to.be.equal('safeframe')); + it("returns 'friendly' when inside friendly iframe and can reach top window", () => + expect(getIframeType(frameWindow3)).to.be.equal('friendly')); + it("returns 'nonfriendly' when cannot get top window", () => + expect(getIframeType(hostileFrameWindow)).to.be.equal('nonfriendly')); +}); + +describe('getFrameElements', () => { + it('it returns a list iframe elements up to the top, topmost goes first', () => { + expect(getFrameElements(frameWindow3)).to.be.deep.equal([ + topFrameElement, + frameElement1, + frameElement2 + ]); + }); +}); + +describe('area', () => { + it('calculates area', () => expect(area(10, 10)).to.be.equal(100)); + it('calculates area', () => + expect( + area(10, 10, { top: -2, left: -2, bottom: 0, right: 0 }) + ).to.be.equal(64)); +}); + +describe('getElementCuts', () => { + it('returns element cuts', () => + expect( + getElementCuts({ + getBoundingClientRect() { + return { + top: 0, + right: 200, + bottom: 200, + left: 0 + }; + }, + ownerDocument: { + defaultView: { + innerHeight: 1000, + innerWidth: 1000 + } + } + }) + ).to.be.deep.equal({ + top: 0, + right: 0, + bottom: 0, + left: 0 + })); +}); + +describe('getInViewRatio', () => { + it('returns inViewRatio', () => + expect( + getInViewRatio({ + ownerDocument: { + defaultView: { + innerHeight: 1000, + innerWidth: 1000 + } + }, + offsetWidth: 200, + offsetHeight: 200, + getBoundingClientRect() { + return { + top: 0, + right: 200, + bottom: 200, + left: 0 + }; + } + }) + ).to.be.deep.equal(1)); +}); + +describe('getMayBecomeVisible', () => { + it('returns true if not inside iframe of visible inside the iframe', () => + expect( + getMayBecomeVisible({ + ownerDocument: { + defaultView: { + innerHeight: 1000, + innerWidth: 1000 + } + }, + offsetWidth: 200, + offsetHeight: 200, + getBoundingClientRect() { + return { + top: 0, + right: 200, + bottom: 200, + left: 0 + }; + } + }) + ).to.be.true); +}); + +describe('getInViewPercentage', () => { + it('returns inViewRatioPercentage', () => + expect( + getInViewPercentage({ + ownerDocument: { + defaultView: { + innerHeight: 1000, + innerWidth: 1000 + } + }, + offsetWidth: 200, + offsetHeight: 200, + getBoundingClientRect() { + return { + top: 0, + right: 200, + bottom: 200, + left: 0 + }; + } + }) + ).to.be.deep.equal(100)); +}); + +describe('getInViewRatioInsideTopFrame', () => { + it('returns inViewRatio', () => + expect( + getInViewRatioInsideTopFrame({ + ownerDocument: { + defaultView: { + innerHeight: 1000, + innerWidth: 1000 + } + }, + offsetWidth: 200, + offsetHeight: 200, + getBoundingClientRect() { + return { + top: 0, + right: 200, + bottom: 200, + left: 0 + }; + } + }) + ).to.be.deep.equal(1)); +}); + +describe('getOffsetTopDocument', () => { + it('returns offset relative to the top document', () => + expect( + getOffsetTopDocument({ + ownerDocument: { + defaultView: { + pageXOffset: 0, + pageYOffset: 0 + } + }, + getBoundingClientRect: () => ({ + top: 0, + right: 0, + bottom: 0, + left: 0 + }) + }) + ).to.be.deep.equal({ + top: 0, + right: 0, + bottom: 0, + left: 0 + })); +}); + +describe('getOffsetTopDocumentPercentage', () => { + it('returns offset from the top as a percentage of the page length', () => { + const topWindow = { + pageXOffset: 0, + pageYOffset: 100, + document: { + body: { + clientHeight: 1000 + } + } + }; + topWindow.top = topWindow; + topWindow.parent = topWindow; + expect( + getOffsetTopDocumentPercentage({ + ownerDocument: { + defaultView: topWindow + }, + getBoundingClientRect: () => ({ + top: 100, + right: 0, + bottom: 0, + left: 0 + }) + }) + ).to.be.equal(20); + }); + it('throws when cannot get window', () => + expect(() => + getOffsetTopDocumentPercentage({ + ownerDocument: {} + }) + ).to.throw()); + it("throw when top document isn't reachable", () => { + const topWindow = { ...topWindow, document: null }; + expect(() => + getOffsetTopDocumentPercentage({ + ownerDocument: { + defaultView: { + top: topWindow + } + } + }) + ).to.throw(); }); +}); + +describe('getOffsetToView', () => { + expect( + getOffsetToView({ + ownerDocument: { + defaultView: { + scrollY: 0, + pageXOffset: 0, + pageYOffset: 0 + } + }, + getBoundingClientRect: () => ({ + top: 0, + right: 0, + bottom: 0, + left: 0 + }) + }) + ).to.be.equal(0); +}); - describe('buildRequests can handle size in 1-dim array', function () { - let bidRequests = [{ - 'bidder': 'vi', - 'params': { - 'pubId': 'sb_test', - 'lang': 'en-US', - 'cat': 'IAB1', - 'bidFloor': 0.05 +describe('getOffsetToView', () => { + expect( + getOffsetToViewPercentage({ + ownerDocument: { + defaultView: { + scrollY: 0, + pageXOffset: 0, + pageYOffset: 0, + document: { + body: { + clientHeight: 1000 + } + } + } }, - 'adUnitCode': 'adunit-code', - 'sizes': [320, 480], - 'bidId': '29b891ad542377', - 'bidderRequestId': '1dc9a08206a57b', - 'requestId': '24176695-e3f0-44db-815b-ed97cf5ad49b', - 'placementCode': 'div-gpt-ad-1460505748561-0', - 'transactionId': '474da635-9cf0-4188-a3d9-58961be8f905' - }]; - - const request = spec.buildRequests(bidRequests); - - it('POST bid request to vi', function () { - expect(request.method).to.equal('POST'); + getBoundingClientRect: () => ({ + top: 0, + right: 0, + bottom: 0, + left: 0 + }) + }) + ).to.be.equal(0); +}); + +describe('getCuts without vCuts', () => { + const cases = { + 'completely in view 1': { + top: 0, + bottom: 200, + right: 200, + left: 0, + vw: 300, + vh: 300, + expected: { + top: 0, + right: 0, + bottom: 0, + left: 0 + } + }, + 'completely in view 2': { + top: 100, + bottom: 200, + right: 200, + left: 0, + vw: 300, + vh: 300, + expected: { + top: 0, + right: 0, + bottom: 0, + left: 0 + } + }, + 'half cut from the top': { + top: -200, + bottom: 200, + right: 200, + left: 0, + vw: 300, + vh: 300, + expected: { + top: -200, + right: 0, + bottom: 0, + left: 0 + } + }, + 'half cut from the bottom': { + top: 0, + bottom: 600, + right: 200, + left: 0, + vw: 300, + vh: 300, + expected: { + top: 0, + right: 0, + bottom: -300, + left: 0 + } + }, + 'quarter cut from top and bottom': { + top: -25, + bottom: 75, + right: 200, + left: 0, + vw: 300, + vh: 50, + expected: { + top: -25, + right: 0, + bottom: -25, + left: 0 + } + }, + 'out of view top': { + top: -200, + bottom: -5, + right: 200, + left: 0, + vw: 300, + vh: 200, + expected: { + top: -200, + right: 0, + bottom: 0, + left: 0 + } + }, + 'out of view bottom': { + top: 250, + bottom: 500, + right: 200, + left: 0, + vw: 300, + vh: 200, + expected: { + top: 0, + right: 0, + bottom: -300, + left: 0 + } + }, + 'half cut from left': { + top: 0, + bottom: 200, + left: -200, + right: 200, + vw: 300, + vh: 300, + expected: { + top: 0, + right: 0, + bottom: 0, + left: -200 + } + }, + 'half cut from left and top': { + top: -100, + bottom: 100, + left: -200, + right: 200, + vw: 300, + vh: 300, + expected: { + top: -100, + right: 0, + bottom: 0, + left: -200 + } + }, + 'quarter cut from all sides': { + top: -100, + left: -100, + bottom: 300, + right: 300, + vw: 200, + vh: 200, + expected: { + top: -100, + right: -100, + bottom: -100, + left: -100 + } + } + }; + for (let descr in cases) { + it(descr, () => { + const { expected, vh, vw, ...rect } = cases[descr]; + expect(getRectCuts(rect, vh, vw)).to.deep.equal(expected); }); + } +}); - it('check endpoint URL', function () { - expect(request.url).to.equal(ENDPOINT) +describe('getCuts with vCuts', () => { + const cases = { + 'completely in view 1, half-cut viewport from top': { + top: 0, + right: 200, + bottom: 200, + left: 0, + vw: 200, + vh: 200, + vCuts: { + top: -100, + right: 0, + bottom: 0, + left: 0 + }, + expected: { + top: -100, + right: 0, + bottom: 0, + left: 0 + } + }, + 'completely in view 2, half-cut viewport from bottom': { + top: 100, + bottom: 200, + right: 200, + left: 0, + vw: 300, + vh: 300, + vCuts: { + top: 0, + right: 0, + bottom: -150, + left: 0 + }, + expected: { + top: 0, + right: 0, + bottom: -50, + left: 0 + } + }, + 'half cut from the top, 1/3 viewport cut from the bottom': { + top: -200, + bottom: 200, + right: 200, + left: 0, + vw: 300, + vh: 300, + vCuts: { + top: 0, + right: 0, + bottom: -100, + left: 0 + }, + expected: { + top: -200, + right: 0, + bottom: 0, + left: 0 + } + }, + 'half cut from the bottom': { + top: 0, + bottom: 600, + right: 200, + left: 0, + vw: 300, + vh: 300, + expected: { + top: 0, + right: 0, + bottom: -300, + left: 0 + } + }, + 'quarter cut from top and bottom': { + top: -25, + bottom: 75, + right: 200, + left: 0, + vw: 300, + vh: 50, + expected: { + top: -25, + right: 0, + bottom: -25, + left: 0 + } + }, + 'out of view top': { + top: -200, + bottom: -5, + right: 200, + left: 0, + vw: 300, + vh: 200, + expected: { + top: -200, + right: 0, + bottom: 0, + left: 0 + } + }, + 'out of view bottom': { + top: 250, + bottom: 500, + right: 200, + left: 0, + vw: 300, + vh: 200, + expected: { + top: 0, + right: 0, + bottom: -300, + left: 0 + } + }, + 'half cut from left': { + top: 0, + bottom: 200, + left: -200, + right: 200, + vw: 300, + vh: 300, + expected: { + top: 0, + right: 0, + bottom: 0, + left: -200 + } + }, + 'half cut from left and top': { + top: -100, + bottom: 100, + left: -200, + right: 200, + vw: 300, + vh: 300, + expected: { + top: -100, + right: 0, + bottom: 0, + left: -200 + } + }, + 'quarter cut from all sides': { + top: -100, + left: -100, + bottom: 300, + right: 300, + vw: 200, + vh: 200, + expected: { + top: -100, + right: -100, + bottom: -100, + left: -100 + } + } + }; + for (let descr in cases) { + it(descr, () => { + const { expected, vh, vw, vCuts, ...rect } = cases[descr]; + expect(getRectCuts(rect, vh, vw, vCuts)).to.deep.equal(expected); }); - }); + } +}); - describe('interpretResponse', function () { - let response = { - body: [{ - 'id': '29b891ad542377', - 'price': 0.1, - 'width': 320, - 'height': 480, - 'ad': '', - 'creativeId': 'dZsPGv' - }] - }; +describe('get', () => { + it('returns a property in a nested object 1', () => + expect(get(['a'], { a: 1 })).to.equal(1)); + it('returns a property in a nested object 2', () => + expect(get(['a', 'b'], { a: { b: 1 } })).to.equal(1)); + it('returns a property in a nested object 3', () => + expect(get(['a', 'b'], { a: { b: 1 } })).to.equal(1)); + it('returns undefined if property does not exist', () => + expect(get(['a', 'b'], { b: 1 })).to.equal(undefined)); + it('returns undefined if property does not exist', () => + expect(get(['a', 'b'], undefined)).to.equal(undefined)); + it('returns undefined if property does not exist', () => + expect(get(['a', 'b'], 1213)).to.equal(undefined)); + const DEFAULT = -5; + it('returns defaultValue if property does not exist', () => + expect(get(['a', 'b'], { b: 1 }, DEFAULT)).to.equal(DEFAULT)); + it('returns defaultValue if property does not exist', () => + expect(get(['a', 'b'], undefined, DEFAULT)).to.equal(DEFAULT)); + it('returns defaultValue if property does not exist', () => + expect(get(['a', 'b'], 1213, DEFAULT)).to.equal(DEFAULT)); + it('can work with arrays 1', () => expect(get([0, 1], [[1, 2]])).to.equal(2)); + it('can work with arrays 2', () => + expect(get([0, 'a'], [{ a: 42 }])).to.equal(42)); +}); - it('should get the correct bid response', function () { - let expectedResponse = [{ - 'requestId': '29b891ad542377', - 'cpm': 0.1, - 'width': 320, - 'height': 480, - 'creativeId': 'dZsPGv', - 'dealId': null, - 'currency': 'USD', - 'netRevenue': true, - 'mediaType': 'banner', - 'ad': decodeURIComponent(``), - 'ttl': 60000 - }]; - - let result = spec.interpretResponse(response); - expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); +describe('getViewabilityDescription', () => { + it('returns error when there is no element', () => { + expect(getViewabilityDescription(null)).to.deep.equal({ + error: 'no element' }); - - it('handles empty bid response', function () { - let response = { - body: [] - }; - let result = spec.interpretResponse(response); - expect(result.length).to.equal(0); + }); + it('returns only iframe type for nonfrienly iframe', () => { + expect( + getViewabilityDescription({ + ownerDocument: { + defaultView: {} + } + }) + ).to.deep.equal({ + iframeType: 'nonfriendly' + }); + }); + it('returns only iframe type for safeframe iframe', () => { + expect( + getViewabilityDescription({ + ownerDocument: { + defaultView: { + $sf: true + } + } + }) + ).to.deep.equal({ + iframeType: 'safeframe' }); }); }); + +describe('mergeSizes', () => { + it('merges provides arrays of tuples, leaving only unique', () => { + expect( + mergeArrays(x => x.join(','), [[1, 2], [2, 4]], [[1, 2]]) + ).to.deep.equal([[1, 2], [2, 4]]); + }); + it('merges provides arrays of tuples, leaving only unique', () => { + expect( + mergeArrays( + x => x.join(','), + [[1, 2], [2, 4]], + [[1, 2]], + [[400, 500], [500, 600]] + ) + ).to.deep.equal([[1, 2], [2, 4], [400, 500], [500, 600]]); + }); +}); From 10ec9a599f46502b14ef41bf09173c36c959d7d5 Mon Sep 17 00:00:00 2001 From: Stefano <50023896+bkse-stefanodechicchis@users.noreply.github.com> Date: Tue, 7 May 2019 10:34:12 -0400 Subject: [PATCH 048/146] Add bucksense Adapter (#3785) * Add bucksense Adapter * fixed first revision for new Bucksense adapter approval https://circleci.com/gh/prebid/Prebid.js/2365? * fixed second revision for new Bucksense adapter approval from jsnellbaker https://github.com/prebid/Prebid.js/pull/3785 * fixed third revision for new Bucksense adapter approval. https://github.com/prebid/Prebid.js/pull/3785 * fixed 4th revision from jsnellbaker - removed console.log and using utils.logInfo(); - removed bksdebug params; pending issue: - gulp test-coverage still under 80% --- modules/bucksenseBidAdapter.js | 124 +++++++++++++++ modules/bucksenseBidAdapter.md | 38 +++++ test/spec/modules/bucksenseBidAdapter_spec.js | 147 ++++++++++++++++++ 3 files changed, 309 insertions(+) create mode 100644 modules/bucksenseBidAdapter.js create mode 100644 modules/bucksenseBidAdapter.md create mode 100644 test/spec/modules/bucksenseBidAdapter_spec.js diff --git a/modules/bucksenseBidAdapter.js b/modules/bucksenseBidAdapter.js new file mode 100644 index 00000000000..12a9e287f38 --- /dev/null +++ b/modules/bucksenseBidAdapter.js @@ -0,0 +1,124 @@ +import { registerBidder } from '../src/adapters/bidderFactory'; +import { BANNER } from '../src/mediaTypes'; +import * as utils from '../src/utils'; + +const WHO = 'BKSHBID-005'; +const BIDDER_CODE = 'bucksense'; +const URL = 'https://prebid.bksn.se:445/prebidjs/'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + + /** + * Determines whether or not the given bid request is valid. + * + * @param {object} bid The bid to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function (bid) { + utils.logInfo(WHO + ' isBidRequestValid() - INPUT bid:', bid); + if (bid.bidder !== BIDDER_CODE || typeof bid.params === 'undefined') { + return false; + } + if (typeof bid.params.placementId === 'undefined') { + return false; + } + return true; + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests A non-empty list of valid bid requests that should be sent to the Server. + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests, bidderRequest) { + utils.logInfo(WHO + ' buildRequests() - INPUT validBidRequests:', validBidRequests, 'INPUT bidderRequest:', bidderRequest); + let requests = []; + const len = validBidRequests.length; + for (let i = 0; i < len; i++) { + var bid = validBidRequests[i]; + var params = {}; + for (var key in bid.params) { + if (bid.params.hasOwnProperty(key)) { + params[key] = encodeURI(bid.params[key]); + } + } + delete bid.params; + var sizes = bid.sizes; + delete bid.sizes; + var sendData = { + 'pub_id': location.host, + 'pl_id': '' + params.placementId, + 'secure': (location.protocol === 'https:') ? 1 : 0, + 'href': encodeURI(location.href), + 'bid_id': bid.bidId, + 'params': params, + 'sizes': sizes, + '_bid': bidderRequest + }; + requests.push({ + method: 'POST', + url: URL, + data: sendData + }); + } + utils.logInfo(WHO + ' buildRequests() - requests:', requests); + return requests; + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse, request) { + utils.logInfo(WHO + ' interpretResponse() - INPUT serverResponse:', serverResponse, 'INPUT request:', request); + + const bidResponses = []; + if (serverResponse.body) { + var oResponse = serverResponse.body; + + var sRequestID = oResponse.requestId || ''; + var nCPM = oResponse.cpm || 0; + var nWidth = oResponse.width || 0; + var nHeight = oResponse.height || 0; + var nTTL = oResponse.ttl || 0; + var sCreativeID = oResponse.creativeId || 0; + var sCurrency = oResponse.currency || 'USD'; + var bNetRevenue = oResponse.netRevenue || true; + var sAd = oResponse.ad || ''; + + if (request && sRequestID.length == 0) { + utils.logInfo(WHO + ' interpretResponse() - use RequestID from Placments'); + sRequestID = request.data.bid_id || ''; + } + + if (request && request.data.params.hasOwnProperty('testcpm')) { + utils.logInfo(WHO + ' interpretResponse() - use Test CPM '); + nCPM = request.data.params.testcpm; + } + + let bidResponse = { + requestId: sRequestID, + cpm: nCPM, + width: nWidth, + height: nHeight, + ttl: nTTL, + creativeId: sCreativeID, + currency: sCurrency, + netRevenue: bNetRevenue, + ad: sAd + }; + bidResponses.push(bidResponse); + } else { + utils.logInfo(WHO + ' interpretResponse() - serverResponse not valid'); + } + utils.logInfo(WHO + ' interpretResponse() - return', bidResponses); + return bidResponses; + }, + +}; +registerBidder(spec); diff --git a/modules/bucksenseBidAdapter.md b/modules/bucksenseBidAdapter.md new file mode 100644 index 00000000000..a240a2c31d8 --- /dev/null +++ b/modules/bucksenseBidAdapter.md @@ -0,0 +1,38 @@ +# Overview + +``` +Module Name: Bucksense Bidder Adapter +Module Type: Bidder Adapter +Maintainer: stefano.dechicchis@bucksense.com +``` + +# Description + +Use `bucksense` as bidder. + +`placementId` is required, use the Placement ID received by Bucksense. + + +Module that connects to Example's demand sources + +## AdUnits configuration example +``` + var adUnits = [ + { + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [ + { + bidder: "bucksense", + params: { + placementId : 1000 + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/test/spec/modules/bucksenseBidAdapter_spec.js b/test/spec/modules/bucksenseBidAdapter_spec.js new file mode 100644 index 00000000000..17b5c3ceff5 --- /dev/null +++ b/test/spec/modules/bucksenseBidAdapter_spec.js @@ -0,0 +1,147 @@ +import {expect} from 'chai'; +import {spec} from 'modules/bucksenseBidAdapter'; + +describe('Bucksense Adapter', function() { + const BIDDER_CODE = 'bucksense'; + const BID_ID = '12345'; + const PLACE_ID = '1000'; + + function getValidBidObject() { + return { + bidder: BIDDER_CODE, + params: { + placementId: PLACE_ID, + } + } + }; + + describe('isBidRequestValid', function() { + var bid; + + beforeEach(function() { + bid = getValidBidObject(); + }); + + it('returns true when valid bid request is sent', function() { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + + it('returns true when valid test bid request is sent', function() { + bid.params['test'] = 1; + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + + it('returns false when bidder not set to "bucksense"', function() { + bid.bidder = 'dummy'; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('returns false when params not set', function() { + delete bid.params; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + + describe('buildRequests', function() { + var bid, bidRequestObj; + + beforeEach(function() { + bid = getValidBidObject(); + bidRequestObj = { + 'bidderCode': 'bucksense', + 'auctionId': '73540558-86cb-4eef-895f-bf99c5353bd7', + 'bidderRequestId': '1feebcb5938c7e', + 'bids': [ + { + 'bidder': 'bucksense', + 'params': { + 'placementId': 1000 + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ [ 300, 250 ], [ 300, 600 ] ] + } + }, + 'adUnitCode': 'test-div', + 'transactionId': '52b3ed07-2a09-4f58-a426-75acc7602c96', + 'sizes': [ [ 300, 250 ], [ 300, 600 ] ], + 'bidId': '22aecdacdcd773', + 'bidderRequestId': '1feebcb5938c7e', + 'auctionId': '73540558-86cb-4eef-895f-bf99c5353bd7', + 'src': 'client', + 'bidRequestsCount': 1 + } + ], + 'auctionStart': 1557176022728, + 'timeout': 1000, + 'refererInfo': { + 'referer': 'http://stefanod.hera.pe/prebid/?pbjs_debug=true', + 'reachedTop': true, + 'numIframes': 0, + 'stack': [ + 'http://stefanod.hera.pe/prebid/?pbjs_debug=true' + ] + }, + 'start': 1557176022731 + }; + }); + + it('should build a very basic request', function() { + var request = spec.buildRequests([bid], bidRequestObj); + expect(request[0].method).to.equal('POST'); + }); + + it('bidRequest data', function () { + var request = spec.buildRequests([bid], bidRequestObj); + expect(request[0].data).to.exist; + }); + }); + + describe('interpretResponse', function() { + var serverResponse; + var serverRequest; + + beforeEach(function() { + serverRequest = { + 'method': 'POST', + 'url': 'https://prebid.bksn.se:445/prebidjs/', + 'data': { + 'pub_id': 'prebid.org', + 'pl_id': '1000', + 'secure': 0, + 'href': 'http://prebid.org/developers.html', + 'bid_id': '27aaf8e96d9fd5', + 'params': { + 'placementId': '1000' + }, + 'sizes': [ [ 300, 250 ], [ 300, 600 ] ] + } + }; + + serverResponse = { + body: { + 'requestId': '', + 'cpm': 0.3, + 'width': 300, + 'height': 250, + 'ttl': 360, + 'creativeId': 'creative002', + 'currency': 'USD', + 'netRevenue': false, + 'ad': '
    ' + } + }; + }); + + it('should return an array of bid responses', function() { + var responses = spec.interpretResponse(serverResponse, serverRequest); + expect(responses).to.be.an('array').with.length(1); + }); + + it('should return an array of bid responses', function() { + serverResponse = {}; + var responses = spec.interpretResponse(serverResponse, serverRequest); + expect(responses).to.be.an('array').with.length(0); + }); + }); +}); From 7406f1a1bd64e709e07a04ecfbd6cd76173fb9c7 Mon Sep 17 00:00:00 2001 From: VideoReach <49446045+VideoReach@users.noreply.github.com> Date: Tue, 7 May 2019 20:29:41 +0200 Subject: [PATCH 049/146] Add Video Reach adapter (#3766) * Add Video Reach adapter * Add Video Reach adapter * Add Video Reach adapter --- modules/videoreachBidAdapter.js | 97 ++++++++++++ modules/videoreachBidAdapter.md | 27 ++++ .../spec/modules/videoreachBidAdapter_spec.js | 141 ++++++++++++++++++ 3 files changed, 265 insertions(+) create mode 100644 modules/videoreachBidAdapter.js create mode 100644 modules/videoreachBidAdapter.md create mode 100644 test/spec/modules/videoreachBidAdapter_spec.js diff --git a/modules/videoreachBidAdapter.js b/modules/videoreachBidAdapter.js new file mode 100644 index 00000000000..03290c9b79c --- /dev/null +++ b/modules/videoreachBidAdapter.js @@ -0,0 +1,97 @@ +import {registerBidder} from '../src/adapters/bidderFactory'; +const utils = require('../src/utils'); +const BIDDER_CODE = 'videoreach'; +const ENDPOINT_URL = '//a.videoreach.de/hb/'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: ['banner'], + + isBidRequestValid: function(bid) { + return !!(bid.params.TagId); + }, + + buildRequests: function(validBidRequests, bidderRequest) { + var data = { + referrer: utils.getTopWindowUrl(), + data: validBidRequests.map(function(bid) { + return { + TagId: utils.getValue(bid.params, 'TagId'), + bidId: utils.getBidIdParameter('bidId', bid), + bidderRequestId: utils.getBidIdParameter('bidderRequestId', bid), + auctionId: utils.getBidIdParameter('auctionId', bid), + transactionId: utils.getBidIdParameter('transactionId', bid) + } + }) + }; + + if (bidderRequest && bidderRequest.gdprConsent) { + data.gdpr = { + consent_string: bidderRequest.gdprConsent.consentString, + consent_required: bidderRequest.gdprConsent.gdprApplies + }; + } + + return { + method: 'POST', + url: ENDPOINT_URL, + data: JSON.stringify(data) + }; + }, + + interpretResponse: function(serverResponse) { + const bidResponses = []; + serverResponse = serverResponse.body; + + if (serverResponse.responses) { + serverResponse.responses.forEach(function (bid) { + const bidResponse = { + cpm: bid.cpm, + width: bid.width, + height: bid.height, + currency: bid.currency, + netRevenue: true, + ttl: bid.ttl, + ad: bid.ad, + requestId: bid.bidId, + creativeId: bid.creativeId + }; + bidResponses.push(bidResponse); + }); + } + return bidResponses; + }, + + getUserSyncs: function(syncOptions, responses, gdprConsent) { + const syncs = []; + + if (syncOptions.pixelEnabled && responses.length) { + const SyncPixels = responses[0].body.responses[0].sync; + + let params = ''; + if (gdprConsent && typeof gdprConsent.consentString === 'string') { + if (typeof gdprConsent.gdprApplies === 'boolean') { + params += 'gdpr=' + gdprConsent.gdprApplies + '&gdpr_consent=' + gdprConsent.consentString; + } else { + params += 'gdpr_consent=' + gdprConsent.consentString; + } + } + + var gdpr; + if (SyncPixels) { + SyncPixels.forEach(sync => { + gdpr = (params) ? ((sync.split('?')[1] ? '&' : '?') + params) : ''; + + syncs.push({ + type: 'image', + url: sync + gdpr + }); + }); + } + } + + return syncs; + } +}; + +registerBidder(spec); diff --git a/modules/videoreachBidAdapter.md b/modules/videoreachBidAdapter.md new file mode 100644 index 00000000000..cdd1ecc04c5 --- /dev/null +++ b/modules/videoreachBidAdapter.md @@ -0,0 +1,27 @@ +# Overview + +**Module Name**: Video Reach Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: hello@videoreach.de + +# Description + +Video Reach Bidder Adapter for Prebid.js. + +Use `videoreach` as bidder. + +`TagId` ist required. + +## AdUnits configuration example +``` + var adUnits = [{ + code: 'your-slot', //use exactly the same code as your slot div id. + sizes: [[1, 1]], + bids: [{ + bidder: 'videoreach', + params: { + TagId: 'XXXXX' + } + }] + }]; +``` diff --git a/test/spec/modules/videoreachBidAdapter_spec.js b/test/spec/modules/videoreachBidAdapter_spec.js new file mode 100644 index 00000000000..b74a0236551 --- /dev/null +++ b/test/spec/modules/videoreachBidAdapter_spec.js @@ -0,0 +1,141 @@ +import {expect} from 'chai'; +import {spec} from 'modules/videoreachBidAdapter'; +import {newBidder} from 'src/adapters/bidderFactory'; + +const ENDPOINT_URL = '//a.videoreach.de/hb/'; + +describe('videoreachBidAdapter', function () { + describe('isBidRequestValid', function () { + let bid = { + 'params': { + 'TagId': 'ABCDE' + }, + 'bidId': '242d506d4e4f15', + 'bidderRequestId': '1893a2136a84a2', + 'auctionId': '8fb7b1c7-317b-4edf-83f0-c4669a318522', + 'transactionId': '85a2e190-0684-4f95-ad32-6c90757ed622' + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'TagId': '' + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'videoreach', + 'params': { + 'TagId': 'ABCDE' + }, + 'adUnitCode': 'adzone', + 'auctionId': '8fb7b1c7-317b-4edf-83f0-c4669a318522', + 'sizes': [[1, 1]], + 'bidId': '242d506d4e4f15', + 'bidderRequestId': '1893a2136a84a2', + 'transactionId': '85a2e190-0684-4f95-ad32-6c90757ed622', + 'mediaTypes': { + 'banner': { + 'sizes': [1, 1] + }, + } + } + ]; + + it('send bid request to endpoint', function () { + const request = spec.buildRequests(bidRequests); + + expect(request.url).to.equal(ENDPOINT_URL); + expect(request.method).to.equal('POST'); + }); + + it('send bid request with GDPR to endpoint', function () { + let consentString = 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA'; + + let bidderRequest = { + 'gdprConsent': { + 'consentString': consentString, + 'gdprApplies': true + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.gdpr.consent_required).to.exist; + expect(payload.gdpr.consent_string).to.equal(consentString); + }); + }); + + describe('interpretResponse', function () { + let serverResponse = + { + 'body': { + 'responses': [{ + 'bidId': '242d506d4e4f15', + 'transactionId': '85a2e190-0684-4f95-ad32-6c90757ed622', + 'cpm': 10.0, + 'width': '1', + 'height': '1', + 'ad': '', + 'ttl': 360, + 'creativeId': '5cb5dc9375c0e', + 'netRevenue': true, + 'currency': 'EUR', + 'sync': ['https:\/\/SYNC_URL'] + }] + } + }; + + it('should handle response', function() { + let expectedResponse = [ + { + cpm: 10.0, + width: '1', + height: '1', + currency: 'EUR', + netRevenue: true, + ttl: 360, + ad: '', + requestId: '242d506d4e4f15', + creativeId: '5cb5dc9375c0e' + } + ]; + + let result = spec.interpretResponse(serverResponse); + expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + }); + + it('should handles empty response', function() { + let serverResponse = { + 'body': { + 'responses': [] + } + }; + + let result = spec.interpretResponse(serverResponse); + expect(result.length).to.equal(0); + }); + + describe('getUserSyncs', () => { + it('should push user sync images if enabled', () => { + const syncOptions = { pixelEnabled: true }; + const syncs = spec.getUserSyncs(syncOptions, [serverResponse]); + + expect(syncs[0]).to.deep.equal({ + type: 'image', + url: 'https://SYNC_URL' + }); + }) + }); + }); +}); From 7050fb17343650b0fe5a84c382a6b30bb4387d2b Mon Sep 17 00:00:00 2001 From: Sergio Date: Tue, 7 May 2019 21:39:24 +0200 Subject: [PATCH 050/146] onBidWon implementation (#3801) --- modules/adponeBidAdapter.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/adponeBidAdapter.js b/modules/adponeBidAdapter.js index cd30f663b16..2d27c9c4240 100644 --- a/modules/adponeBidAdapter.js +++ b/modules/adponeBidAdapter.js @@ -59,6 +59,13 @@ export const spec = { }); return answer; + }, + + onBidWon: bid => { + const bidString = JSON.stringify(bid); + const encodedBuf = window.btoa(bidString); + const img = new Image(1, 1); + img.src = `https://rtb.adpone.com/prebid/analytics?q=${encodedBuf}`; } }; From 8776afe35b878891bde3018d32f3a0387fa15ea9 Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Tue, 7 May 2019 15:52:23 -0400 Subject: [PATCH 051/146] Prebid 2.14.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 72ddc2e6e53..1a2042b2332 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.14.0-pre", + "version": "2.14.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From f5e6e3aace89534a66e3c3bf19eb4b40186ebdd5 Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Tue, 7 May 2019 16:11:34 -0400 Subject: [PATCH 052/146] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1a2042b2332..b772f2eccdb 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.14.0", + "version": "2.15.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From bd1636ada243f30c309bd8212e4446d39d86659b Mon Sep 17 00:00:00 2001 From: Mathias Methner Date: Wed, 8 May 2019 07:03:06 +0200 Subject: [PATCH 053/146] Orbidder uses onSetTargeting callback (#3804) * initial orbidder version in personal github repo * use adUnits from orbidder_example.html * replace obsolete functions * forgot to commit the test * check if bidderRequest object is available * try to fix weird safari/ie issue * ebayK: add more params * update orbidderBidAdapter.md * use spec. instead of this. for consistency reasons * add bidfloor parameter to params object * fix gdpr object handling * default to consentRequired: false when not explicitly given * wip - use onSetTargeting callback * add tests for onSetTargeting callback --- modules/orbidderBidAdapter.js | 22 ++++++++------ package-lock.json | 30 ++++++++++++++------ test/spec/modules/orbidderBidAdapter_spec.js | 19 +++++++++---- 3 files changed, 49 insertions(+), 22 deletions(-) diff --git a/modules/orbidderBidAdapter.js b/modules/orbidderBidAdapter.js index fc5eecbab08..1123cc5d50e 100644 --- a/modules/orbidderBidAdapter.js +++ b/modules/orbidderBidAdapter.js @@ -45,9 +45,7 @@ export const spec = { if (bidderRequest && bidderRequest.gdprConsent) { ret.data.gdprConsent = { consentString: bidderRequest.gdprConsent.consentString, - consentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') - ? bidderRequest.gdprConsent.gdprApplies - : true + consentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') && bidderRequest.gdprConsent.gdprApplies }; } return ret; @@ -72,15 +70,23 @@ export const spec = { return bidResponses; }, - onBidWon(winObj) { + onBidWon(bid) { + this.onHandler(bid, '/win'); + }, + + onSetTargeting (bid) { + this.onHandler(bid, '/targeting'); + }, + + onHandler (bid, route) { const getRefererInfo = detectReferer(window); - winObj.pageUrl = getRefererInfo().referer; - if (spec.bidParams[winObj.adId]) { - winObj.params = spec.bidParams[winObj.adId]; + bid.pageUrl = getRefererInfo().referer; + if (spec.bidParams[bid.adId]) { + bid.params = spec.bidParams[bid.adId]; } - spec.ajaxCall(`${spec.orbidderHost}/win`, JSON.stringify(winObj)); + spec.ajaxCall(`${spec.orbidderHost}${route}`, JSON.stringify(bid)); }, ajaxCall(endpoint, data) { diff --git a/package-lock.json b/package-lock.json index c3bd739008f..6f908eeba52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.13.0-pre", + "version": "2.14.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -6093,12 +6093,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6113,17 +6115,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -6240,7 +6245,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -6252,6 +6258,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6266,6 +6273,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6273,12 +6281,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -6297,6 +6307,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -6377,7 +6388,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -6389,6 +6401,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -6510,6 +6523,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", diff --git a/test/spec/modules/orbidderBidAdapter_spec.js b/test/spec/modules/orbidderBidAdapter_spec.js index bc88090095b..3818f502901 100644 --- a/test/spec/modules/orbidderBidAdapter_spec.js +++ b/test/spec/modules/orbidderBidAdapter_spec.js @@ -1,6 +1,7 @@ import {expect} from 'chai'; import {spec} from 'modules/orbidderBidAdapter'; import {newBidder} from 'src/adapters/bidderFactory'; +import openxAdapter from '../../../modules/openxAnalyticsAdapter'; describe('orbidderBidAdapter', () => { const adapter = newBidder(spec); @@ -107,7 +108,7 @@ describe('orbidderBidAdapter', () => { const request = buildRequest(defaultBidRequest, { gdprConsent: {} }); - expect(request.data.gdprConsent.consentRequired).to.be.equal(true); + expect(request.data.gdprConsent.consentRequired).to.be.equal(false); }); it('handles non-existent gdpr object', () => { @@ -146,9 +147,9 @@ describe('orbidderBidAdapter', () => { }); }); - describe('onBidWon', () => { + describe('onCallbackHandler', () => { let ajaxStub; - const winObj = { + const bidObj = { adId: 'testId', test: 1, pageUrl: 'www.someurl.de', @@ -163,12 +164,18 @@ describe('orbidderBidAdapter', () => { ajaxStub.restore(); }); - it('calls orbidder\'s win endpoint', () => { - spec.onBidWon(winObj); + it('calls orbidder\'s callback endpoint', () => { + spec.onBidWon(bidObj); expect(ajaxStub.calledOnce).to.equal(true); expect(ajaxStub.firstCall.args[0].indexOf('https://')).to.equal(0); expect(ajaxStub.firstCall.args[0]).to.equal(`${spec.orbidderHost}/win`); - expect(ajaxStub.firstCall.args[1]).to.equal(JSON.stringify(winObj)); + expect(ajaxStub.firstCall.args[1]).to.equal(JSON.stringify(bidObj)); + + spec.onSetTargeting(bidObj); + expect(ajaxStub.calledTwice).to.equal(true); + expect(ajaxStub.secondCall.args[0].indexOf('https://')).to.equal(0); + expect(ajaxStub.secondCall.args[0]).to.equal(`${spec.orbidderHost}/targeting`); + expect(ajaxStub.secondCall.args[1]).to.equal(JSON.stringify(bidObj)); }); }); From ad7b59d62b89da45b638e23d74d96df5f043d03c Mon Sep 17 00:00:00 2001 From: imonomy <49719148+imonomy@users.noreply.github.com> Date: Thu, 9 May 2019 20:30:11 +0300 Subject: [PATCH 054/146] Add Imonomy network BidAdapter (#3765) * Add Imonomy network BidAdapter * add changes due to the comments on Prebid --- modules/imonomyBidAdapter.js | 130 ++++++++++++++++ modules/imonomyBidAdapter.md | 29 ++++ test/spec/modules/imonomyBidAdapter_spec.js | 164 ++++++++++++++++++++ 3 files changed, 323 insertions(+) create mode 100644 modules/imonomyBidAdapter.js create mode 100644 modules/imonomyBidAdapter.md create mode 100644 test/spec/modules/imonomyBidAdapter_spec.js diff --git a/modules/imonomyBidAdapter.js b/modules/imonomyBidAdapter.js new file mode 100644 index 00000000000..fa3ad0cfea2 --- /dev/null +++ b/modules/imonomyBidAdapter.js @@ -0,0 +1,130 @@ +import * as utils from 'src/utils'; +import { registerBidder } from 'src/adapters/bidderFactory'; + +const BIDDER_CODE = 'imonomy'; +const ENDPOINT = '//b.imonomy.com/openrtb/hb/00000'; +const USYNCURL = '//b.imonomy.com/UserMatching/b/'; + +export const spec = { + code: BIDDER_CODE, + + /** + * Determines whether or not the given bid request is valid. Valid bid request must have placementId and hbid + * + * @param {BidRequest} bid The bid params to validate. + * @return {boolean} True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: bid => { + return !!(bid && bid.params && bid.params.placementId && bid.params.hbid); + }, + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests an array of bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: validBidRequests => { + const tags = validBidRequests.map(bid => { + // map each bid id to bid object to retrieve adUnit code in callback + let tag = { + uuid: bid.bidId, + sizes: bid.sizes, + trid: bid.transactionId, + hbid: bid.params.hbid, + placementid: bid.params.placementId + }; + + // add floor price if specified (not mandatory) + if (bid.params.floorPrice) { + tag.floorprice = bid.params.floorPrice; + } + + return tag; + }); + + // Imonomy server config + const time = new Date().getTime(); + const kbConf = { + ts_as: time, + hb_placements: [], + hb_placement_bidids: {}, + hb_floors: {}, + cb: _generateCb(time), + tz: new Date().getTimezoneOffset(), + }; + + validBidRequests.forEach(bid => { + kbConf.hdbdid = kbConf.hdbdid || bid.params.hbid; + kbConf.encode_bid = kbConf.encode_bid || bid.params.encode_bid; + kbConf.hb_placement_bidids[bid.params.placementId] = bid.bidId; + if (bid.params.floorPrice) { + kbConf.hb_floors[bid.params.placementId] = bid.params.floorPrice; + } + kbConf.hb_placements.push(bid.params.placementId); + }); + + let payload = {}; + if (!utils.isEmpty(tags)) { + payload = { bids: [...tags], kbConf }; + } + + let endpointToUse = ENDPOINT; + if (kbConf.hdbdid) { + endpointToUse = endpointToUse.replace('00000', kbConf.hdbdid); + } + + return { + method: 'POST', + url: endpointToUse, + data: JSON.stringify(payload) + }; + }, + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} response A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: (response) => { + const bidResponses = []; + if (response && response.body && response.body.bids) { + response.body.bids.forEach(bid => { + // The bid ID. Used to tie this bid back to the request. + if (bid.uuid) { + bid.requestId = bid.uuid; + } else { + utils.logError('No uuid for bid'); + } + // The creative payload of the returned bid. + if (bid.creative) { + bid.ad = bid.creative; + } else { + utils.logError('No creative for bid'); + } + bidResponses.push(bid); + }); + } + return bidResponses; + }, + /** + * Register User Sync. + */ + getUserSyncs: syncOptions => { + if (syncOptions.iframeEnabled) { + return [{ + type: 'iframe', + url: USYNCURL + }]; + } + } +}; + +/** +* Generated cache baster value to be sent to bid server +* @param {*} time current time to use for creating cb. +*/ +function _generateCb(time) { + return Math.floor((time % 65536) + (Math.floor(Math.random() * 65536) * 65536)); +} + +registerBidder(spec); diff --git a/modules/imonomyBidAdapter.md b/modules/imonomyBidAdapter.md new file mode 100644 index 00000000000..451eb0994d8 --- /dev/null +++ b/modules/imonomyBidAdapter.md @@ -0,0 +1,29 @@ +# Overview + +**Module Name**: Imonomy Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: support@imonomy.com + +# Description + +Connects to Imonomy demand source to fetch bids. + +# Test Parameters +``` + var adUnits = [{ + code: 'banner-ad-div', + sizes: [[300, 250]], + + // Replace this object to test a new Adapter! + bids: [{ + bidder: 'imonomy', + params: { + placementId: 'e69148e0ba6c4c07977dc2daae5e1577', + hbid: '14567718624', + floorPrice: 0.5 + } + }] + }]; +``` + + diff --git a/test/spec/modules/imonomyBidAdapter_spec.js b/test/spec/modules/imonomyBidAdapter_spec.js new file mode 100644 index 00000000000..206e227a3b1 --- /dev/null +++ b/test/spec/modules/imonomyBidAdapter_spec.js @@ -0,0 +1,164 @@ +import { expect } from 'chai'; +import { spec } from 'modules/imonomyBidAdapter'; + +describe('Imonomy Adapter Tests', function () { + const bidsRequest = [ + { + bidder: 'imonomy', + params: { + placementId: '170577', + hbid: '14567718624', + }, + placementCode: 'div-gpt-ad-1460505748561-0', + transactionId: '9f801c02-bbe8-4683-8ed4-bc816ea186bb', + sizes: [ + [300, 250] + ], + bidId: '2faedf1095f815', + bidderRequestId: '18065867f8ae39', + auctionId: '529e1518-b872-45cf-807c-2d41dfa5bcd3' + }, + { + bidder: 'imonomy', + params: { + placementId: '281277', + hbid: '14567718624', + floorPrice: 0.5 + }, + placementCode: 'div-gpt-ad-1460505748561-0', + transactionId: '9f801c02-bbe8-4683-8ed4-bc816ea186bb', + sizes: [ + [728, 90] + ], + bidId: '3c34e2367a3f59', + bidderRequestId: '18065867f8ae39', + auctionId: '529e1518-b872-45cf-807c-2d41dfa5bcd3' + }]; + + const bidsResponse = { + body: { + bids: [ + { + placementid: '170577', + uuid: '2faedf1095f815', + width: 300, + height: 250, + cpm: 0.51, + creative: '', + ttl: 360, + currency: 'USD', + netRevenue: true, + creativeId: 'd30b58c2ba' + } + ] + } + }; + + it('Verifies imonomyAdapter bidder code', function () { + expect(spec.code).to.equal('imonomy'); + }); + + it('Verifies imonomyAdapter bid request validation', function () { + expect(spec.isBidRequestValid(bidsRequest[0])).to.equal(true); + expect(spec.isBidRequestValid(bidsRequest[1])).to.equal(true); + expect(spec.isBidRequestValid({})).to.equal(false); + expect(spec.isBidRequestValid({ params: {} })).to.equal(false); + expect(spec.isBidRequestValid({ params: { hbid: 12345 } })).to.equal(false); + expect(spec.isBidRequestValid({ params: { placementid: 12345 } })).to.equal(false); + expect(spec.isBidRequestValid({ params: { hbid: 12345, placementId: 67890 } })).to.equal(true); + expect(spec.isBidRequestValid({ params: { hbid: 12345, placementId: 67890, floorPrice: 0.8 } })).to.equal(true); + }); + + it('Verify imonomyAdapter build request', function () { + var startTime = new Date().getTime(); + + const request = spec.buildRequests(bidsRequest); + expect(request.url).to.equal('//b.imonomy.com/openrtb/hb/14567718624'); + expect(request.method).to.equal('POST'); + const requestData = JSON.parse(request.data); + + // bids object + let bids = requestData.bids; + expect(bids).to.have.lengthOf(2); + + // first bid request: no floor price + expect(bids[0].uuid).to.equal('2faedf1095f815'); + expect(bids[0].floorprice).to.be.undefined; + expect(bids[0].placementid).to.equal('170577'); + expect(bids[0].hbid).to.equal('14567718624'); + expect(bids[0].trid).to.equal('9f801c02-bbe8-4683-8ed4-bc816ea186bb'); + expect(bids[0].sizes).to.have.lengthOf(1); + expect(bids[0].sizes[0][0]).to.equal(300); + expect(bids[0].sizes[0][1]).to.equal(250); + + // second bid request: with floor price + expect(bids[1].uuid).to.equal('3c34e2367a3f59'); + expect(bids[1].floorprice).to.equal(0.5); + expect(bids[1].placementid).to.equal('281277'); + expect(bids[1].hbid).to.equal('14567718624'); + expect(bids[1].trid).to.equal('9f801c02-bbe8-4683-8ed4-bc816ea186bb'); + expect(bids[1]).to.have.property('sizes') + .that.is.an('array') + .of.length(1) + .that.deep.equals([[728, 90]]); + + // kbConf object + let kbConf = requestData.kbConf; + expect(kbConf.hdbdid).to.equal(bids[0].hbid); + expect(kbConf.hdbdid).to.equal(bids[1].hbid); + expect(kbConf.encode_bid).to.be.undefined; + // kbConf timezone and cb + expect(kbConf.cb).not.to.be.undefined; + expect(kbConf.ts_as).to.be.above(startTime - 1); + expect(kbConf.tz).to.equal(new Date().getTimezoneOffset()); + // kbConf bid ids + expect(kbConf.hb_placement_bidids) + .to.have.property(bids[0].placementid) + .that.equal(bids[0].uuid); + expect(kbConf.hb_placement_bidids) + .to.have.property(bids[1].placementid) + .that.equal(bids[1].uuid); + // kbConf floor price + expect(kbConf.hb_floors).not.to.have.property(bids[0].placementid) + expect(kbConf.hb_floors).to.have.property(bids[1].placementid).that.equal(bids[1].floorprice); + // kbConf placement ids + expect(kbConf.hb_placements).to.have.lengthOf(2); + expect(kbConf.hb_placements[0]).to.equal(bids[0].placementid); + expect(kbConf.hb_placements[1]).to.equal(bids[1].placementid); + }); + + it('Verify imonomyAdapter build response', function () { + const request = spec.buildRequests(bidsRequest); + const bids = spec.interpretResponse(bidsResponse, request); + + // 'server' return single bid + expect(bids).to.have.lengthOf(1); + + // verify bid object + const bid = bids[0]; + const responseBids = bidsResponse.body.bids; + + expect(bid.cpm).to.equal(responseBids[0].cpm); + expect(bid.ad).to.equal(responseBids[0].creative); + expect(bid.requestId).equal(responseBids[0].uuid); + expect(bid.uuid).equal(responseBids[0].uuid); + expect(bid.width).to.equal(responseBids[0].width); + expect(bid.height).to.equal(responseBids[0].height); + expect(bid.ttl).to.equal(responseBids[0].ttl); + expect(bid.currency).to.equal('USD'); + expect(bid.netRevenue).to.equal(true); + expect(bid.creativeId).to.equal(responseBids[0].creativeId); + }); + + it('Verifies imonomyAdapter sync options', function () { + // user sync disabled + expect(spec.getUserSyncs({})).to.be.undefined; + expect(spec.getUserSyncs({ iframeEnabled: false })).to.be.undefined; + // user sync enabled + const options = spec.getUserSyncs({ iframeEnabled: true }); + expect(options).to.not.be.undefined; + expect(options).to.have.lengthOf(1); + expect(options[0].type).to.equal('iframe'); + expect(options[0].url).to.equal('//b.imonomy.com/UserMatching/b/'); + }); +}); From f78b8e96cfbc459963b8b03308896fc9af7e9312 Mon Sep 17 00:00:00 2001 From: sumit116 Date: Fri, 10 May 2019 20:15:36 +0530 Subject: [PATCH 055/146] Rad 2751/specify ad units set targeting for ast (#3805) * add adUnitCodes as param for setTargetingForAst() * unit tests for setTargetingForAst * refactor * Revert "refactor" This reverts commit 1a89d024cb561f44cc59fe85d3f0adea11e24381. * refactor to add more tests --- src/targeting.js | 2 +- test/spec/unit/core/targeting_spec.js | 40 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/targeting.js b/src/targeting.js index 6caa4029c9c..4ac993cf94a 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -255,7 +255,7 @@ export function newTargeting(auctionManager) { let astTargeting = targeting.getAllTargeting(adUnitCodes); try { - targeting.resetPresetTargetingAST(); + targeting.resetPresetTargetingAST(adUnitCodes); } catch (e) { utils.logError('unable to reset targeting for AST' + e) } diff --git a/test/spec/unit/core/targeting_spec.js b/test/spec/unit/core/targeting_spec.js index 13ebfd7dc4e..727c790c991 100644 --- a/test/spec/unit/core/targeting_spec.js +++ b/test/spec/unit/core/targeting_spec.js @@ -326,4 +326,44 @@ describe('targeting tests', function () { }); }); }); + + describe('setTargetingForAst', function () { + let sandbox, + apnTagStub; + beforeEach(function() { + sandbox = sinon.createSandbox(); + sandbox.stub(targetingInstance, 'resetPresetTargetingAST'); + apnTagStub = sandbox.stub(window.apntag, 'setKeywords'); + }); + afterEach(function () { + sandbox.restore(); + }); + + it('should set single addUnit code', function() { + let adUnitCode = 'testdiv-abc-ad-123456-0'; + sandbox.stub(targetingInstance, 'getAllTargeting').returns({ + 'testdiv1-abc-ad-123456-0': {hb_bidder: 'appnexus'} + }); + targetingInstance.setTargetingForAst(adUnitCode); + expect(targetingInstance.getAllTargeting.called).to.equal(true); + expect(targetingInstance.resetPresetTargetingAST.called).to.equal(true); + expect(apnTagStub.callCount).to.equal(1); + expect(apnTagStub.getCall(0).args[0]).to.deep.equal('testdiv1-abc-ad-123456-0'); + expect(apnTagStub.getCall(0).args[1]).to.deep.equal({HB_BIDDER: 'appnexus'}); + }); + + it('should set array of addUnit codes', function() { + let adUnitCodes = ['testdiv1-abc-ad-123456-0', 'testdiv2-abc-ad-123456-0'] + sandbox.stub(targetingInstance, 'getAllTargeting').returns({ + 'testdiv1-abc-ad-123456-0': {hb_bidder: 'appnexus'}, + 'testdiv2-abc-ad-123456-0': {hb_bidder: 'appnexus'} + }); + targetingInstance.setTargetingForAst(adUnitCodes); + expect(targetingInstance.getAllTargeting.called).to.equal(true); + expect(targetingInstance.resetPresetTargetingAST.called).to.equal(true); + expect(apnTagStub.callCount).to.equal(2); + expect(apnTagStub.getCall(1).args[0]).to.deep.equal('testdiv2-abc-ad-123456-0'); + expect(apnTagStub.getCall(1).args[1]).to.deep.equal({HB_BIDDER: 'appnexus'}); + }); + }); }); From ed21da1a4adc2a9bf6eda6327c9970862011608f Mon Sep 17 00:00:00 2001 From: werowe Date: Mon, 13 May 2019 14:47:26 +0200 Subject: [PATCH 056/146] fixed misspelled word (#3816) fixed spelled of installd to installed --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f802efecfcf..21e02ebee7f 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ prebid.requestBids({ *Note:* In the 1.24.0 release of Prebid.js we have transitioned to using gulp 4.0 from using gulp 3.9.1. To compily with gulp's recommended setup for 4.0, you'll need to have `gulp-cli` installed globally prior to running the general `npm install`. This shouldn't impact any other projects you may work on that use an earlier version of gulp in it's setup. -If you have a previous version of `gulp` installed globally, you'll need to remove it before installing `gulp-cli`. You can check if this is installed by running `gulp -v` and seeing the version that's listed in the `CLI` field of the output. If you have the `gulp` package installd globally, it's likely the same version that you'll see in the `Local` field. If you already have `gulp-cli` installed, it should be a lower major version (it's at version `2.0.1` at the time of the transition). +If you have a previous version of `gulp` installed globally, you'll need to remove it before installing `gulp-cli`. You can check if this is installed by running `gulp -v` and seeing the version that's listed in the `CLI` field of the output. If you have the `gulp` package installed globally, it's likely the same version that you'll see in the `Local` field. If you already have `gulp-cli` installed, it should be a lower major version (it's at version `2.0.1` at the time of the transition). To remove the old package, you can use the command: `npm rm gulp -g` From acd3077136fef7f672ffba9ee404a5fac48f599c Mon Sep 17 00:00:00 2001 From: Rich Audience Date: Mon, 13 May 2019 16:23:07 +0200 Subject: [PATCH 057/146] RichAudience: Files name updated (#3793) * Files name updated * uploading bidder --- .../{richAudienceBidAdapter.js => richaudienceBidAdapter.js} | 0 .../{richAudienceBidAdapter.md => richaudienceBidAdapter.md} | 0 ...enceBidAdapter_spec.js => richaudienceBidAdapter_spec.js} | 5 ++--- 3 files changed, 2 insertions(+), 3 deletions(-) rename modules/{richAudienceBidAdapter.js => richaudienceBidAdapter.js} (100%) rename modules/{richAudienceBidAdapter.md => richaudienceBidAdapter.md} (100%) rename test/spec/modules/{richAudienceBidAdapter_spec.js => richaudienceBidAdapter_spec.js} (98%) diff --git a/modules/richAudienceBidAdapter.js b/modules/richaudienceBidAdapter.js similarity index 100% rename from modules/richAudienceBidAdapter.js rename to modules/richaudienceBidAdapter.js diff --git a/modules/richAudienceBidAdapter.md b/modules/richaudienceBidAdapter.md similarity index 100% rename from modules/richAudienceBidAdapter.md rename to modules/richaudienceBidAdapter.md diff --git a/test/spec/modules/richAudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js similarity index 98% rename from test/spec/modules/richAudienceBidAdapter_spec.js rename to test/spec/modules/richaudienceBidAdapter_spec.js index c974ff70ce3..16d67ce7ceb 100644 --- a/test/spec/modules/richAudienceBidAdapter_spec.js +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -1,13 +1,12 @@ // import or require modules necessary for the test, e.g.: import {expect} from 'chai'; // may prefer 'assert' in place of 'expect' -// import spec from 'modules/richAudienceBidAdapter'; import { spec -} from 'modules/richAudienceBidAdapter'; +} from 'modules/richaudienceBidAdapter'; import {config} from 'src/config'; import * as utils from 'src/utils'; -describe('Rich Audience adapter tests', function () { +describe('Richaudience adapter tests', function () { var DEFAULT_PARAMS = [{ adUnitCode: 'test-div', bidId: '2c7c8e9c900244', From 6b8d0873a3370aa64ebdb8ed72a98be2821a24f4 Mon Sep 17 00:00:00 2001 From: Jozef Bartek <31618107+jbartek25@users.noreply.github.com> Date: Mon, 13 May 2019 17:46:16 +0200 Subject: [PATCH 058/146] Improve Digital adapter: Endpoint update (#3811) * Adding GDPR support * Always drop user syncs when available * Set dealID based on buying type * Native ads, single request option * Send ad unit sizes to Improve ad server * adapter version -> 5.1 * Adding usePrebidSizes config param * New ad server endpoint --- modules/improvedigitalBidAdapter.js | 4 ++-- .../modules/improvedigitalBidAdapter_spec.js | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js index fdfe5970572..0c9028133e0 100644 --- a/modules/improvedigitalBidAdapter.js +++ b/modules/improvedigitalBidAdapter.js @@ -271,10 +271,10 @@ export function ImproveDigitalAdServerJSClient(endPoint) { STANDARD: 0, SECURE: 1 }, - AD_SERVER_BASE_URL: 'ad.360yield.com', + AD_SERVER_BASE_URL: 'ice.360yield.com', END_POINT: endPoint || 'hb', AD_SERVER_URL_PARAM: 'jsonp=', - CLIENT_VERSION: 'JS-5.3.0', + CLIENT_VERSION: 'JS-6.0.0', MAX_URL_LENGTH: 2083, ERROR_CODES: { MISSING_PLACEMENT_PARAMS: 2, diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js index 61ba56ab822..6c78afca6b2 100644 --- a/test/spec/modules/improvedigitalBidAdapter_spec.js +++ b/test/spec/modules/improvedigitalBidAdapter_spec.js @@ -7,7 +7,7 @@ describe('Improve Digital Adapter Tests', function () { let idClient = new ImproveDigitalAdServerJSClient('hb'); const METHOD = 'GET'; - const URL = '//ad.360yield.com/hb'; + const URL = '//ice.360yield.com/hb'; const PARAM_PREFIX = 'jsonp='; const simpleBidRequest = { @@ -224,7 +224,7 @@ describe('Improve Digital Adapter Tests', function () { 'id': '33e9500b21129f', 'advid': '5279', 'price': 1.45888594164456, - 'nurl': 'http://ad.360yield.com/imp_pixel?ic=wVmhKI07hCVyGC1sNdFp.6buOSiGYOw8jPyZLlcMY2RCwD4ek3Fy6.xUI7U002skGBs3objMBoNU-Frpvmb9js3NKIG0YZJgWaNdcpXY9gOXE9hY4-wxybCjVSNzhOQB-zic73hzcnJnKeoGgcfvt8fMy18-yD0aVdYWt4zbqdoITOkKNCPBEgbPFu1rcje-o7a64yZ7H3dKvtnIixXQYc1Ep86xGSBGXY6xW2KfUOMT6vnkemxO72divMkMdhR8cAuqIubbx-ZID8-xf5c9k7p6DseeBW0I8ionrlTHx.rGosgxhiFaMqtr7HiA7PBzKvPdeEYN0hQ8RYo8JzYL82hA91A3V2m9Ij6y0DfIJnnrKN8YORffhxmJ6DzwEl1zjrVFbD01bqB3Vdww8w8PQJSkKQkd313tr-atU8LS26fnBmOngEkVHwAr2WCKxuUvxHmuVBTA-Lgz7wKwMoOJCA3hFxMavVb0ZFB7CK0BUTVU6z0De92Q.FJKNCHLMbjX3vcAQ90=', + 'nurl': 'http://ice.360yield.com/imp_pixel?ic=wVmhKI07hCVyGC1sNdFp.6buOSiGYOw8jPyZLlcMY2RCwD4ek3Fy6.xUI7U002skGBs3objMBoNU-Frpvmb9js3NKIG0YZJgWaNdcpXY9gOXE9hY4-wxybCjVSNzhOQB-zic73hzcnJnKeoGgcfvt8fMy18-yD0aVdYWt4zbqdoITOkKNCPBEgbPFu1rcje-o7a64yZ7H3dKvtnIixXQYc1Ep86xGSBGXY6xW2KfUOMT6vnkemxO72divMkMdhR8cAuqIubbx-ZID8-xf5c9k7p6DseeBW0I8ionrlTHx.rGosgxhiFaMqtr7HiA7PBzKvPdeEYN0hQ8RYo8JzYL82hA91A3V2m9Ij6y0DfIJnnrKN8YORffhxmJ6DzwEl1zjrVFbD01bqB3Vdww8w8PQJSkKQkd313tr-atU8LS26fnBmOngEkVHwAr2WCKxuUvxHmuVBTA-Lgz7wKwMoOJCA3hFxMavVb0ZFB7CK0BUTVU6z0De92Q.FJKNCHLMbjX3vcAQ90=', 'h': 290, 'pid': 1053688, 'sync': [ @@ -234,7 +234,7 @@ describe('Improve Digital Adapter Tests', function () { 'crid': '422031', 'w': 600, 'cid': '99006', - 'adm': 'document.writeln(\"\\\"\\\"\\/<\\/a>\");document.writeln(\"<\\/improvedigital_ad_output_information>\");' + 'adm': 'document.writeln(\"\\\"\\\"\\/<\\/a>\");document.writeln(\"<\\/improvedigital_ad_output_information>\");' } ], 'debug': '' @@ -261,7 +261,7 @@ describe('Improve Digital Adapter Tests', function () { 'crid': '422033', 'w': 700, 'cid': '99006', - 'adm': 'document.writeln(\"\\\"\\\"\\/<\\/a>\");document.writeln(\"<\\/improvedigital_ad_output_information>\");' + 'adm': 'document.writeln(\"\\\"\\\"\\/<\\/a>\");document.writeln(\"<\\/improvedigital_ad_output_information>\");' } ], 'debug': '' @@ -278,7 +278,7 @@ describe('Improve Digital Adapter Tests', function () { id: '33e9500b21129f', advid: '5279', price: 1.45888594164456, - nurl: 'http://ad.360yield.com/imp_pixel?ic=wVm', + nurl: 'http://ice.360yield.com/imp_pixel?ic=wVm', h: 290, pid: 1053688, sync: [ @@ -365,7 +365,7 @@ describe('Improve Digital Adapter Tests', function () { describe('interpretResponse', function () { let expectedBid = [ { - 'ad': '', + 'ad': '', 'adId': '33e9500b21129f', 'creativeId': '422031', 'cpm': 1.45888594164456, @@ -382,7 +382,7 @@ describe('Improve Digital Adapter Tests', function () { let expectedTwoBids = [ expectedBid[0], { - 'ad': '', + 'ad': '', 'adId': '1234', 'creativeId': '422033', 'cpm': 1.23, @@ -426,7 +426,7 @@ describe('Improve Digital Adapter Tests', function () { clickUrl: 'http://advertiser.com', clickTrackers: ['http://click.tracker.com/click?impid=123'], impressionTrackers: [ - 'http://ad.360yield.com/imp_pixel?ic=wVm', + 'http://ice.360yield.com/imp_pixel?ic=wVm', 'http://imptrack1.com', 'http://imptrack2.com' ], From 54ac989fec68e421c15e0be40bf8acd8e714f945 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 13 May 2019 15:53:37 -0700 Subject: [PATCH 059/146] Support Prebid.js User ID module in Sharethrough bid adapter (#3819) * Add HTML5 video support param to bid requests * Use const instead of var for consistency * Update supported sizes - Default size returned changed from 0x0 to 1x1 to support PrebidServer - Now will always respect the bid sizes supported when configured Co-authored-by: Josh Becker * Update maintainer contact email * Support Prebid.js User ID module - Add support for Unified ID solution of User ID module by checking for `bidRequest.userId.tdid` param in `buildRequests` method of Sharethrough's adapter - Update specs, maintain 80%+ code coverage * Update logic for changing userAgent string in tests --- modules/sharethroughBidAdapter.js | 16 ++-- .../modules/sharethroughBidAdapter_spec.js | 78 ++++++++++++++++--- 2 files changed, 76 insertions(+), 18 deletions(-) diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index 34682e4eb79..8c0c98bceae 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -11,10 +11,10 @@ export const sharethroughAdapterSpec = { isBidRequestValid: bid => !!bid.params.pkey && bid.bidder === BIDDER_CODE, buildRequests: (bidRequests, bidderRequest) => { - return bidRequests.map(bid => { + return bidRequests.map(bidRequest => { let query = { - placement_key: bid.params.pkey, - bidId: bid.bidId, + placement_key: bidRequest.params.pkey, + bidId: bidRequest.bidId, consent_required: false, instant_play_capable: canAutoPlayHTML5Video(), hbSource: 'prebid', @@ -30,12 +30,16 @@ export const sharethroughAdapterSpec = { query.consent_required = !!bidderRequest.gdprConsent.gdprApplies; } + if (bidRequest.userId && bidRequest.userId.tdid) { + query.ttduid = bidRequest.userId.tdid; + } + // Data that does not need to go to the server, // but we need as part of interpretResponse() const strData = { - stayInIframe: bid.params.iframe, - iframeSize: bid.params.iframeSize, - sizes: bid.sizes + stayInIframe: bidRequest.params.iframe, + iframeSize: bidRequest.params.iframeSize, + sizes: bidRequest.sizes } return { diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index f91c40f302e..825e42273cf 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -3,7 +3,7 @@ import { sharethroughAdapterSpec } from 'modules/sharethroughBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; const spec = newBidder(sharethroughAdapterSpec).getSpec(); -const bidderRequest = [ +const bidRequests = [ { bidder: 'sharethrough', bidId: 'bidId1', @@ -11,7 +11,8 @@ const bidderRequest = [ placementCode: 'foo', params: { pkey: 'aaaa1111' - } + }, + userId: { tdid: 'fake-tdid' } }, { bidder: 'sharethrough', @@ -128,6 +129,12 @@ const b64EncodeUnicode = (str) => { })); } +const setUserAgent = (str) => { + window.navigator['__defineGetter__']('userAgent', function () { + return str; + }); +} + describe('sharethrough adapter spec', function () { describe('.code', function () { it('should return a bidder code of sharethrough', function () { @@ -157,36 +164,78 @@ describe('sharethrough adapter spec', function () { }); it('should return true if req is correct', function () { - expect(spec.isBidRequestValid(bidderRequest[0])).to.eq(true); - expect(spec.isBidRequestValid(bidderRequest[1])).to.eq(true); + expect(spec.isBidRequestValid(bidRequests[0])).to.eq(true); + expect(spec.isBidRequestValid(bidRequests[1])).to.eq(true); }) }); describe('.buildRequests', function () { it('should return an array of requests', function () { - const bidRequests = spec.buildRequests(bidderRequest); + const builtBidRequests = spec.buildRequests(bidRequests); - expect(bidRequests[0].url).to.eq( + expect(builtBidRequests[0].url).to.eq( 'http://btlr.sharethrough.com/header-bid/v1'); - expect(bidRequests[1].url).to.eq( + expect(builtBidRequests[1].url).to.eq( 'http://btlr.sharethrough.com/header-bid/v1') - expect(bidRequests[0].method).to.eq('GET'); + expect(builtBidRequests[0].method).to.eq('GET'); + }); + + it('should set the instant_play_capable parameter correctly based on browser userAgent string', function () { + setUserAgent('Android Chrome/60'); + let builtBidRequests = spec.buildRequests(bidRequests); + expect(builtBidRequests[0].data.instant_play_capable).to.be.true; + + setUserAgent('iPhone Version/11'); + builtBidRequests = spec.buildRequests(bidRequests); + expect(builtBidRequests[0].data.instant_play_capable).to.be.true; + + setUserAgent('iPhone CriOS/60'); + builtBidRequests = spec.buildRequests(bidRequests); + expect(builtBidRequests[0].data.instant_play_capable).to.be.true; + + setUserAgent('Android Chrome/50'); + builtBidRequests = spec.buildRequests(bidRequests); + expect(builtBidRequests[0].data.instant_play_capable).to.be.false; + + setUserAgent('Android Chrome'); + builtBidRequests = spec.buildRequests(bidRequests); + expect(builtBidRequests[0].data.instant_play_capable).to.be.false; + + setUserAgent(undefined); + builtBidRequests = spec.buildRequests(bidRequests); + expect(builtBidRequests[0].data.instant_play_capable).to.be.false; }); it('should add consent parameters if gdprConsent is present', function () { const gdprConsent = { consentString: 'consent_string123', gdprApplies: true }; - const fakeBidRequest = { gdprConsent: gdprConsent }; - const bidRequest = spec.buildRequests(bidderRequest, fakeBidRequest)[0]; + const bidderRequest = { gdprConsent: gdprConsent }; + const bidRequest = spec.buildRequests(bidRequests, bidderRequest)[0]; expect(bidRequest.data.consent_required).to.eq(true); expect(bidRequest.data.consent_string).to.eq('consent_string123'); }); it('should handle gdprConsent is present but values are undefined case', function () { const gdprConsent = { consent_string: undefined, gdprApplies: undefined }; - const fakeBidRequest = { gdprConsent: gdprConsent }; - const bidRequest = spec.buildRequests(bidderRequest, fakeBidRequest)[0]; + const bidderRequest = { gdprConsent: gdprConsent }; + const bidRequest = spec.buildRequests(bidRequests, bidderRequest)[0]; expect(bidRequest.data).to.not.include.any.keys('consent_string') }); + + it('should add the ttduid parameter if a bid request contains a value for Unified ID from The Trade Desk', function () { + const bidRequest = spec.buildRequests(bidRequests)[0]; + expect(bidRequest.data.ttduid).to.eq('fake-tdid'); + }); + + it('should add Sharethrough specific parameters', function () { + const builtBidRequests = spec.buildRequests(bidRequests); + expect(builtBidRequests[0]).to.deep.include({ + strData: { + stayInIframe: undefined, + iframeSize: undefined, + sizes: [[600, 300]] + } + }); + }); }); describe('.interpretResponse', function () { @@ -319,6 +368,11 @@ describe('sharethrough adapter spec', function () { ); }); + it('returns an empty array if serverResponses is empty', function () { + const syncArray = spec.getUserSyncs({ pixelEnabled: true }, []); + expect(syncArray).to.be.an('array').that.is.empty; + }); + it('returns an empty array if the body is null', function () { const syncArray = spec.getUserSyncs({ pixelEnabled: true }, [{ body: null }]); expect(syncArray).to.be.an('array').that.is.empty; From 0877946ff4db97f9e8f9e6f397015f3def97ff78 Mon Sep 17 00:00:00 2001 From: Alex Khmelnitsky Date: Tue, 14 May 2019 02:02:32 +0300 Subject: [PATCH 060/146] Added iframe user sync support (#3822) --- modules/cedatoBidAdapter.js | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/modules/cedatoBidAdapter.js b/modules/cedatoBidAdapter.js index 8c049410b3a..78bb7b45c7b 100644 --- a/modules/cedatoBidAdapter.js +++ b/modules/cedatoBidAdapter.js @@ -109,26 +109,35 @@ export const spec = { const syncs = []; if (syncOptions.pixelEnabled) { resps.forEach(() => { - const uuid = getUserID(); - const syncUrl = SYNC_URL; - let params = ''; - if (gdprConsent && typeof gdprConsent.consentString === 'string') { - if (typeof gdprConsent.gdprApplies === 'boolean') { - params += `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; - } else { - params += `?gdpr_consent=${gdprConsent.consentString}`; - } - } - syncs.push({ - type: 'image', - url: syncUrl.replace('{UUID}', uuid) + params, - }); + syncs.push(getSync('image', gdprConsent)); + }); + } + if (syncOptions.iframeEnabled) { + resps.forEach(() => { + syncs.push(getSync('iframe', gdprConsent)); }); } return syncs; } } +const getSync = (type, gdprConsent) => { + const uuid = getUserID(); + const syncUrl = SYNC_URL; + let params = '&type=' + type; + if (gdprConsent && typeof gdprConsent.consentString === 'string') { + if (typeof gdprConsent.gdprApplies === 'boolean') { + params += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + params += `&gdpr_consent=${gdprConsent.consentString}`; + } + } + return { + type: type, + url: syncUrl.replace('{UUID}', uuid) + params, + }; +} + const getUserID = () => { const cookieName = COOKIE_NAME; const uuidLen = UUID_LEN; From be8d83211efffa134a5632069c661756f003def1 Mon Sep 17 00:00:00 2001 From: Salomon Rada Date: Tue, 14 May 2019 17:48:24 +0300 Subject: [PATCH 061/146] Gamoshi: Remove and update some bid response properties (#3806) * Add support for multi-format ad units. Add favoredMediaType property to params. * Add tests for gdpr consent. * Add adId to outbids * Modify media type resolving * Refactor multi-format ad units handler. * Remove adId from bid response * Add DEFAULT_TTL const for ttl property * Modify TTL const to 360 --- modules/gamoshiBidAdapter.js | 11 +++++------ test/spec/modules/gamoshiBidAdapter_spec.js | 8 ++++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/modules/gamoshiBidAdapter.js b/modules/gamoshiBidAdapter.js index c831f4db522..67475f8d8d9 100644 --- a/modules/gamoshiBidAdapter.js +++ b/modules/gamoshiBidAdapter.js @@ -11,6 +11,8 @@ const ENDPOINTS = { 'gambid': 'https://rtb.gamoshi.io', }; +const DEFAULT_TTL = 360; + export const helper = { getTopFrame: function () { try { @@ -114,7 +116,7 @@ export const spec = { if (mediaTypes && mediaTypes.video) { if (!hasFavoredMediaType || params.favoredMediaType === VIDEO) { - const playerSize = mediaTypes.video.playerSize; + const playerSize = mediaTypes.video.playerSize || sizes; const videoImp = Object.assign({}, imp, { video: { w: playerSize ? playerSize[0][0] : 300, @@ -150,18 +152,15 @@ export const spec = { bids.forEach(bid => { const outBid = { - adId: bidRequest.bidRequest.adUnitCode, requestId: bidRequest.bidRequest.bidId, cpm: bid.price, width: bid.w, height: bid.h, - ttl: 60 * 10, - creativeId: bid.crid, + ttl: DEFAULT_TTL, + creativeId: bid.crid || bid.adid, netRevenue: true, currency: bid.cur || response.cur, - adUnitCode: bidRequest.bidRequest.adUnitCode, mediaType: helper.getMediaType(bid) - }; if (utils.deepAccess(bidRequest.bidRequest, 'mediaTypes.' + outBid.mediaType)) { diff --git a/test/spec/modules/gamoshiBidAdapter_spec.js b/test/spec/modules/gamoshiBidAdapter_spec.js index b657dac34b6..a2c4eebc213 100644 --- a/test/spec/modules/gamoshiBidAdapter_spec.js +++ b/test/spec/modules/gamoshiBidAdapter_spec.js @@ -329,6 +329,8 @@ describe('GamoshiAdapter', function () { ] }; + const TTL = 360; + it('returns an empty array on missing response', function () { let response; @@ -345,13 +347,12 @@ describe('GamoshiAdapter', function () { const response = spec.interpretResponse({body: rtbResponse}, {bidRequest: bannerBidRequest}); expect(Array.isArray(response)).to.equal(true); expect(response.length).to.equal(1); - const ad0 = response[0]; expect(ad0.requestId).to.equal(bannerBidRequest.bidId); expect(ad0.cpm).to.equal(rtbResponse.seatbid[1].bid[0].price); expect(ad0.width).to.equal(rtbResponse.seatbid[1].bid[0].w); expect(ad0.height).to.equal(rtbResponse.seatbid[1].bid[0].h); - expect(ad0.ttl).to.equal(60 * 10); + expect(ad0.ttl).to.equal(TTL); expect(ad0.creativeId).to.equal(rtbResponse.seatbid[1].bid[0].crid); expect(ad0.netRevenue).to.equal(true); expect(ad0.currency).to.equal(rtbResponse.seatbid[1].bid[0].cur || rtbResponse.cur || 'USD'); @@ -364,13 +365,12 @@ describe('GamoshiAdapter', function () { const response = spec.interpretResponse({body: rtbResponse}, {bidRequest: videoBidRequest}); expect(Array.isArray(response)).to.equal(true); expect(response.length).to.equal(1); - const ad0 = response[0]; expect(ad0.requestId).to.equal(videoBidRequest.bidId); expect(ad0.cpm).to.equal(rtbResponse.seatbid[0].bid[0].price); expect(ad0.width).to.equal(rtbResponse.seatbid[0].bid[0].w); expect(ad0.height).to.equal(rtbResponse.seatbid[0].bid[0].h); - expect(ad0.ttl).to.equal(60 * 10); + expect(ad0.ttl).to.equal(TTL); expect(ad0.creativeId).to.equal(rtbResponse.seatbid[0].bid[0].crid); expect(ad0.netRevenue).to.equal(true); expect(ad0.currency).to.equal(rtbResponse.seatbid[0].bid[0].cur || rtbResponse.cur || 'USD'); From a4f247c2e6147ce321ce7cdd57855a7c8ac14911 Mon Sep 17 00:00:00 2001 From: Rich Snapp Date: Tue, 14 May 2019 08:49:11 -0600 Subject: [PATCH 062/146] update fun-hooks and use no-eval version for CSP (#3814) * update fun-hooks and use no-eval version for CSP * update fun-hooks to version with proper assign for ie --- package-lock.json | 92 ++++++++++++++++++++--------------------------- package.json | 2 +- src/hook.js | 2 +- 3 files changed, 41 insertions(+), 55 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6f908eeba52..262d67c052d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.14.0-pre", + "version": "2.15.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3108,7 +3108,7 @@ }, "query-string": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "resolved": "http://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", "dev": true, "requires": { @@ -3480,7 +3480,7 @@ }, "commander": { "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true }, @@ -4585,7 +4585,7 @@ "engine.io": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", - "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", + "integrity": "sha1-tgKBw1SEpw7gNR6g6/+D7IyVIqI=", "dev": true, "requires": { "accepts": "~1.3.4", @@ -4599,7 +4599,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -4615,7 +4615,7 @@ }, "engine.io-client": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", + "resolved": "http://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", "dev": true, "requires": { @@ -4635,7 +4635,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -5326,7 +5326,7 @@ }, "event-stream": { "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", "dev": true, "requires": { @@ -5865,7 +5865,7 @@ "flatted": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", - "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", + "integrity": "sha1-VRIrZTbqSWtLRIk+4mCBQdENmRY=", "dev": true }, "flush-write-stream": { @@ -6093,14 +6093,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6115,20 +6113,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -6245,8 +6240,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -6258,7 +6252,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6273,7 +6266,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6281,14 +6273,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -6307,7 +6297,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -6388,8 +6377,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -6401,7 +6389,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -6523,7 +6510,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6596,9 +6582,9 @@ } }, "fun-hooks": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/fun-hooks/-/fun-hooks-0.8.1.tgz", - "integrity": "sha512-qhyQAO6vhmzzwOJ2SvqeCvL2dqBCw3NeuIpNOfMPv2bucFYXLur9UbXTiUAbm7EE2TrdLgIKJZkO0DfwEY+KVQ==" + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/fun-hooks/-/fun-hooks-0.9.2.tgz", + "integrity": "sha512-Bbhqg3zj/joiHsmU9z/DBPofMN8yN4P7m2cE4sqZqaL+C6YcAXKjwa7Cu8rUs3roBiAhgWwQOAALZZodpmBglw==" }, "function-bind": { "version": "1.1.1", @@ -7266,7 +7252,7 @@ "gulp-connect": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/gulp-connect/-/gulp-connect-5.7.0.tgz", - "integrity": "sha512-8tRcC6wgXMLakpPw9M7GRJIhxkYdgZsXwn7n56BA2bQYGLR9NOPhMzx7js+qYDy6vhNkbApGKURjAw1FjY4pNA==", + "integrity": "sha1-fpJfXkw06/7fnzGFdpZuj+iEDVo=", "dev": true, "requires": { "ansi-colors": "^2.0.5", @@ -7283,7 +7269,7 @@ "ansi-colors": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-2.0.5.tgz", - "integrity": "sha512-yAdfUZ+c2wetVNIFsNRn44THW+Lty6S5TwMpUfLA/UaGhiXbBv/F8E60/1hMLd0cnF/CDoWH8vzVaI5bAcHCjw==", + "integrity": "sha1-XaN4Jf7z51872kf3YNZL/RDhXhA=", "dev": true } } @@ -7986,7 +7972,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -8982,7 +8968,7 @@ "karma": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/karma/-/karma-3.1.4.tgz", - "integrity": "sha512-31Vo8Qr5glN+dZEVIpnPCxEGleqE0EY6CtC2X9TagRV3rRQ3SNrvfhddICkJgUK3AgqpeKSZau03QumTGhGoSw==", + "integrity": "sha1-OJDKlyKxDR0UtybhM1kxRVeISZ4=", "dev": true, "requires": { "bluebird": "^3.3.0", @@ -9607,7 +9593,7 @@ "log4js": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.6.tgz", - "integrity": "sha512-ezXZk6oPJCWL483zj64pNkMuY/NcRX5MPiB0zE6tjZM137aeusrOnW1ecxgF9cmwMWkBMhjteQxBPoZBh9FDxQ==", + "integrity": "sha1-5srO2Uln7uuc45n5+GgqSysoyP8=", "dev": true, "requires": { "circular-json": "^0.5.5", @@ -9620,7 +9606,7 @@ "circular-json": { "version": "0.5.9", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", - "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==", + "integrity": "sha1-kydjroj0996teg0JyKUaR0OlOx0=", "dev": true }, "debug": { @@ -10157,7 +10143,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -10193,7 +10179,7 @@ "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true } @@ -10801,7 +10787,7 @@ "dependencies": { "minimist": { "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", "dev": true }, @@ -12074,7 +12060,7 @@ "rfdc": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.2.tgz", - "integrity": "sha512-92ktAgvZhBzYTIK0Mja9uen5q5J3NRVMoDkJL2VMwq6SXjVCgqvQeVP2XAaUY6HT+XpQYeLSjb3UoitBryKmdA==", + "integrity": "sha1-5uctdPXcOd6PU49l4Aw2wYAY40k=", "dev": true }, "rgb2hex": { @@ -12543,7 +12529,7 @@ "socket.io": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", - "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", + "integrity": "sha1-oGnF/qvuPmshSnW0DOBlLhz7mYA=", "dev": true, "requires": { "debug": "~3.1.0", @@ -12557,7 +12543,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -12580,7 +12566,7 @@ "socket.io-client": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", - "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", + "integrity": "sha1-3LOBA0NqtFeN2wJmOK4vIbYjZx8=", "dev": true, "requires": { "backo2": "1.0.2", @@ -12602,7 +12588,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -12618,7 +12604,7 @@ }, "socket.io-parser": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", + "resolved": "http://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", "dev": true, "requires": { @@ -12630,7 +12616,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -12748,7 +12734,7 @@ }, "split": { "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "resolved": "http://registry.npmjs.org/split/-/split-0.3.3.tgz", "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", "dev": true, "requires": { @@ -12882,7 +12868,7 @@ }, "stream-combiner": { "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", "dev": true, "requires": { @@ -13883,7 +13869,7 @@ "useragent": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", - "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", + "integrity": "sha1-IX+UOtVAyyEoZYqyP8lg9qiMmXI=", "dev": true, "requires": { "lru-cache": "4.1.x", @@ -14523,7 +14509,7 @@ }, "webpack-dev-middleware": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz", + "resolved": "http://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz", "integrity": "sha512-tj5LLD9r4tDuRIDa5Mu9lnY2qBBehAITv6A9irqXhw/HQquZgTx3BCd57zYbU2gMDnncA49ufK2qVQSbaKJwOw==", "dev": true, "requires": { diff --git a/package.json b/package.json index b772f2eccdb..6d29154f310 100755 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "babel-plugin-transform-object-assign": "^6.22.0", "core-js": "^2.4.1", "crypto-js": "^3.1.9-1", - "fun-hooks": "^0.8.1", + "fun-hooks": "^0.9.2", "jsencrypt": "^3.0.0-rc.1", "just-clone": "^1.0.2" } diff --git a/src/hook.js b/src/hook.js index 7727155e8aa..ddf0d134357 100644 --- a/src/hook.js +++ b/src/hook.js @@ -1,5 +1,5 @@ -import funHooks from 'fun-hooks'; +import funHooks from 'fun-hooks/no-eval'; export let hook = funHooks({ ready: funHooks.SYNC | funHooks.ASYNC | funHooks.QUEUE From 9166db810dba45320c488d6cfe5b283816be4b53 Mon Sep 17 00:00:00 2001 From: Gaudeamus Date: Tue, 14 May 2019 18:17:47 +0300 Subject: [PATCH 063/146] mgid adapter: native support, minor changes (#3774) * native support & minor changes * native support & minor changes * increase test coverage * fix win price value * fix win price value tests * fix alias, fix bidfloor * remove alias --- modules/mgidBidAdapter.js | 435 +++++++++++++++++++---- test/spec/modules/mgidBidAdapter_spec.js | 370 ++++++++++++++++++- 2 files changed, 729 insertions(+), 76 deletions(-) diff --git a/modules/mgidBidAdapter.js b/modules/mgidBidAdapter.js index 5a91cccde9e..b7759ea1e0a 100644 --- a/modules/mgidBidAdapter.js +++ b/modules/mgidBidAdapter.js @@ -1,16 +1,70 @@ import {registerBidder} from 'src/adapters/bidderFactory'; import * as utils from '../src/utils'; import * as urlUtils from '../src/url'; -import { BANNER } from 'src/mediaTypes'; +import {BANNER, NATIVE} from 'src/mediaTypes'; +const DEFAULT_CUR = 'USD'; const BIDDER_CODE = 'mgid'; -const ENDPOINT_URL = '//dsp.mgid.com/prebid/'; +const ENDPOINT_URL = 'https://prebid.mgid.com/prebid/'; +const LOG_WARN_PREFIX = '[MGID warn]: '; +const LOG_INFO_PREFIX = '[MGID info]: '; +const NATIVE_ASSETS = { + 'TITLE': { ID: 1, KEY: 'title', TYPE: 0 }, + 'IMAGE': { ID: 2, KEY: 'image', TYPE: 0 }, + 'ICON': { ID: 3, KEY: 'icon', TYPE: 0 }, + 'SPONSOREDBY': { ID: 4, KEY: 'sponsoredBy', TYPE: 1 }, // please note that type of SPONSORED is also 1 + 'DESC': { ID: 5, KEY: 'data', TYPE: 2 }, // please note that type of BODY is also set to 2 + 'PRICE': { ID: 6, KEY: 'price', TYPE: 6 }, + 'SALEPRICE': { ID: 7, KEY: 'saleprice', TYPE: 7 }, + 'DISPLAYURL': { ID: 8, KEY: 'displayurl', TYPE: 11 }, + 'CTA': { ID: 9, KEY: 'cta', TYPE: 12 }, + 'BODY': { ID: 10, KEY: 'body', TYPE: 2 }, // please note that type of DESC is also set to 2 + 'SPONSORED': { ID: 11, KEY: 'sponsored', TYPE: 1 }, // please note that type of SPONSOREDBY is also set to 1 +}; +const NATIVE_ASSET_IMAGE_TYPE = { + 'ICON': 1, + 'IMAGE': 3 +}; +const DEFAULT_IMAGE_WIDTH = 492; +const DEFAULT_IMAGE_HEIGHT = 328; +const DEFAULT_ICON_WIDTH = 50; +const DEFAULT_ICON_HEIGHT = 50; +const DEFAULT_TITLE_LENGTH = 80; + +let isInvalidNativeRequest = false; + +// check if title, image can be added with mandatory field default values +const NATIVE_MINIMUM_REQUIRED_IMAGE_ASSETS = [ + { + id: NATIVE_ASSETS.SPONSOREDBY.ID, + required: true, + data: { + type: 1 + } + }, + { + id: NATIVE_ASSETS.TITLE.ID, + required: true, + }, + { + id: NATIVE_ASSETS.IMAGE.ID, + required: true, + } +]; +let _NATIVE_ASSET_ID_TO_KEY_MAP = {}; +let _NATIVE_ASSET_KEY_TO_ASSET_MAP = {}; + +// loading _NATIVE_ASSET_ID_TO_KEY_MAP +utils._each(NATIVE_ASSETS, anAsset => { _NATIVE_ASSET_ID_TO_KEY_MAP[anAsset.ID] = anAsset.KEY }); +// loading _NATIVE_ASSET_KEY_TO_ASSET_MAP +utils._each(NATIVE_ASSETS, anAsset => { _NATIVE_ASSET_KEY_TO_ASSET_MAP[anAsset.KEY] = anAsset }); export const spec = { - VERSION: '1.0', + VERSION: '1.1', code: BIDDER_CODE, - aliases: ['mgid'], // short code - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, NATIVE], reId: /^[0-9]+$/, + NATIVE_ASSET_ID_TO_KEY_MAP: _NATIVE_ASSET_ID_TO_KEY_MAP, + NATIVE_ASSET_KEY_TO_ASSET_MAP: _NATIVE_ASSET_KEY_TO_ASSET_MAP, /** * Determines whether or not the given bid request is valid. * @@ -19,16 +73,38 @@ export const spec = { */ isBidRequestValid: (bid) => { const banner = utils.deepAccess(bid, 'mediaTypes.banner'); - const sizes = utils.deepAccess(banner, 'sizes'); - let sizesOk = typeof (sizes) == 'object' && sizes.length > 0; - for (let f = 0; sizesOk && f < sizes.length; f++) { - sizesOk = sizes[f].length == 2; - } - return typeof (bid.params) == 'object' && !!bid.params.accountId && !!bid.params.placementId && - typeof (bid.params.accountId) == 'string' && typeof (bid.params.placementId) == 'string' && - bid.params.accountId.length > 0 && bid.params.placementId.length > 0 && + const native = utils.deepAccess(bid, 'mediaTypes.native'); + let nativeOk = utils.isPlainObject(native); + if (nativeOk) { + const nativeParams = utils.deepAccess(bid, 'nativeParams'); + let assetsCount = 0; + if (utils.isPlainObject(nativeParams)) { + for (let k in nativeParams) { + let v = nativeParams[k]; + const supportProp = spec.NATIVE_ASSET_KEY_TO_ASSET_MAP.hasOwnProperty(k); + if (supportProp) { + assetsCount++ + } + if (!utils.isPlainObject(v) || (!supportProp && utils.deepAccess(v, 'required'))) { + nativeOk = false; + break; + } + } + } + nativeOk = nativeOk && (assetsCount > 0); + } + let bannerOk = utils.isPlainObject(banner); + if (bannerOk) { + const sizes = utils.deepAccess(banner, 'sizes'); + bannerOk = utils.isArray(sizes) && sizes.length > 0; + for (let f = 0; bannerOk && f < sizes.length; f++) { + bannerOk = sizes[f].length === 2; + } + } + return utils.isPlainObject(bid.params) && !!bid.params.accountId && !!bid.params.placementId && + utils.isStr(bid.params.accountId) && utils.isStr(bid.params.placementId) && bid.params.accountId.toString().match(spec.reId) > 0 && bid.params.placementId.toString().match(spec.reId) && - typeof (banner) == 'object' && sizesOk; + (bannerOk || nativeOk); }, /** * Make a server request from the list of BidRequests. @@ -37,9 +113,9 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: (validBidRequests, bidderRequest) => { - utils.logInfo(`MGID DEBUG: buildRequests`); - if (validBidRequests.length == 0) { - return null; + utils.logInfo(LOG_INFO_PREFIX + `buildRequests`); + if (validBidRequests.length === 0) { + return; } const referer = utils.deepAccess(bidderRequest, 'refererInfo.referer'); const hostname = urlUtils.parse(referer).hostname; @@ -47,34 +123,45 @@ export const spec = { const accountId = setOnAny(validBidRequests, 'params.accountId'); const muid = getLocalStorageSafely('mgMuidn'); let url = (setOnAny(validBidRequests, 'params.bidUrl') || ENDPOINT_URL) + accountId; - if (muid != null && typeof (muid) == 'string' && muid.length > 0) { + if (utils.isStr(muid) && muid.length > 0) { url += '?muid=' + muid; } const page = utils.deepAccess(bidderRequest, 'refererInfo.canonicalUrl') || referer; const secure = window.location.protocol === 'https:' ? 1 : 0; - const imp = validBidRequests.map((bid, id) => { - const placeId = utils.deepAccess(bid, 'params.placementId'); - const sizes = utils.deepAccess(bid, 'mediaTypes.banner.sizes'); - let format = []; - if (sizes.length > 1) { - for (let f = 0; f < sizes.length; f++) { - if (sizes[f].length == 2) { - format.push({w: sizes[f][0], h: sizes[f][1]}); - } - } - } - return { + let imp = []; + validBidRequests.forEach(bid => { + let impObj = { id: bid.bidId, - tagid: placeId, - banner: { - w: sizes && sizes[0][0], - h: sizes && sizes[0][1], - format, - }, + tagid: utils.deepAccess(bid, 'params.placementId'), secure, }; + const bidFloor = utils.deepAccess(bid, 'params.bidFloor') || utils.deepAccess(bid, 'params.bidfloor') || 0; + if (bidFloor && utils.isNumber(bidFloor)) { + impObj.bidfloor = bidFloor; + } + for (let mediaTypes in bid.mediaTypes) { + switch (mediaTypes) { + case BANNER: + impObj.banner = createBannerRequest(bid); + imp.push(impObj); + break; + case NATIVE: + const native = createNativeRequest(bid.nativeParams); + if (!isInvalidNativeRequest) { + impObj.native = { + 'request': native + }; + imp.push(impObj); + } + break; + } + } }); + if (imp.length === 0) { + return; + } + let ext = {mgid_ver: spec.VERSION, prebid_ver: $$PREBID_GLOBAL$$.version}; let user = {}; let regs = {}; @@ -87,15 +174,14 @@ export const spec = { gdpr: (bidderRequest.gdprConsent.gdprApplies ? 1 : 0) }; } - - const request = { + let request = { id: utils.deepAccess(bidderRequest, 'bidderRequestId'), site: { domain, page }, - cur: ['USD'], + cur: [setOnAny(validBidRequests, 'params.currency') || setOnAny(validBidRequests, 'params.cur') || DEFAULT_CUR], device: { ua: navigator.userAgent, js: 1, - dnt: (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0, + dnt: (navigator.doNotTrack === 'yes' || navigator.doNotTrack === '1' || navigator.msDoNotTrack === '1') ? 1 : 0, h: screen.height, w: screen.width, language: getLanguage() @@ -105,7 +191,7 @@ export const spec = { ext, imp }; - utils.logInfo(`MGID DEBUG: buildRequests\n${request}`); + utils.logInfo(LOG_INFO_PREFIX + `buildRequest:`, request); return { method: 'POST', url: url, @@ -119,41 +205,55 @@ export const spec = { * @return {Bid[]} An array of bids which were nested inside the server. */ interpretResponse: (serverResponse, bidRequests) => { - if (serverResponse == null || serverResponse.body == null || serverResponse.body == '' || !serverResponse.body.seatbid || !serverResponse.body.seatbid[0]) { - return []; + utils.logInfo(LOG_INFO_PREFIX + `interpretResponse`, serverResponse); + if (serverResponse == null || serverResponse.body == null || serverResponse.body === '' || !utils.isArray(serverResponse.body.seatbid) || !serverResponse.body.seatbid.length) { + return; } - utils.logInfo(`MGID DEBUG: interpretResponse`); const returnedBids = []; const muidn = utils.deepAccess(serverResponse.body, 'ext.muidn') - if (muidn != null && typeof (muidn) == 'string' && muidn.length > 0) { + if (utils.isStr(muidn) && muidn.length > 0) { setLocalStorageSafely('mgMuidn', muidn) } - serverResponse.body.seatbid[0].bid.forEach((value, index) => { - returnedBids.push(prebidBid(value, serverResponse.body.cur)); + serverResponse.body.seatbid.forEach((bids) => { + bids.bid.forEach((bid) => { + const pbid = prebidBid(bid, serverResponse.body.cur); + if (pbid.mediaType === NATIVE && utils.isEmpty(pbid.native)) { + return; + } + returnedBids.push(pbid); + }) }); - utils.logInfo(`MGID DEBUG:\n${returnedBids}`); + utils.logInfo(LOG_INFO_PREFIX + `interpretedResponse`, returnedBids); return returnedBids; }, onBidWon: (bid) => { - const cpm = bid.pbMg; - if (bid.nurl != '') { + const cpm = utils.deepAccess(bid, 'adserverTargeting.hb_pb') || ''; + if (utils.isStr(bid.nurl) && bid.nurl !== '') { bid.nurl = bid.nurl.replace( - /\$\{AUCTION_PRICE\}/, + /\${AUCTION_PRICE}/, cpm ); pixel(bid.nurl); - }; + } if (bid.isBurl) { - bid.ad = bid.ad.replace( - /\$\{AUCTION_PRICE\}/, - cpm - ); + if (bid.mediaType === BANNER) { + bid.ad = bid.ad.replace( + /\${AUCTION_PRICE}/, + cpm + ) + } else { + bid.burl = bid.burl.replace( + /\${AUCTION_PRICE}/, + cpm + ); + pixel(bid.burl); + } } - utils.logInfo(`MGID DEBUG: onBidWon`); + utils.logInfo(LOG_INFO_PREFIX + `onBidWon`); }, getUserSyncs: (syncOptions, serverResponses) => { - utils.logInfo(`MGID DEBUG: getUserSyncs`); + utils.logInfo(LOG_INFO_PREFIX + `getUserSyncs`); } }; @@ -174,8 +274,8 @@ function setOnAny(collection, key) { * @return Bid */ function prebidBid(serverBid, cur) { - if (cur == null || cur == '') { - cur = 'USD'; + if (!utils.isStr(cur) || cur === '') { + cur = DEFAULT_CUR; } const bid = { requestId: serverBid.impid, @@ -190,12 +290,28 @@ function prebidBid(serverBid, cur) { netRevenue: true, ttl: serverBid.ttl || 300, nurl: serverBid.nurl || '', - isBurl: typeof (serverBid.burl) == 'string' && serverBid.burl.length > 0, + burl: serverBid.burl || '', + isBurl: utils.isStr(serverBid.burl) && serverBid.burl.length > 0, }; - + setMediaType(serverBid, bid); + switch (bid.mediaType) { + case BANNER: + break; + case NATIVE: + parseNativeResponse(serverBid, bid); + break; + } return bid; } +function setMediaType(bid, newBid) { + if (utils.deepAccess(bid, 'ext.crtype') === 'native') { + newBid.mediaType = NATIVE; + } else { + newBid.mediaType = BANNER; + } +} + function extractDomainFromHost(pageHost) { if (pageHost == 'localhost') { return 'localhost' @@ -224,7 +340,7 @@ function pixel(url) { function getLanguage() { const language = navigator.language ? 'language' : 'userLanguage'; const lang2 = navigator[language].split('-')[0]; - if (lang2.length == 2 || lang2.length == 3) { + if (lang2.length === 2 || lang2.length === 3) { return lang2; } return ''; @@ -245,3 +361,196 @@ function setLocalStorageSafely(key, val) { return null; } } + +function createBannerRequest(bid) { + const sizes = utils.deepAccess(bid, 'mediaTypes.banner.sizes'); + let format = []; + if (sizes.length > 1) { + for (let f = 0; f < sizes.length; f++) { + if (sizes[f].length === 2) { + format.push({w: sizes[f][0], h: sizes[f][1]}); + } + } + } + return { + w: sizes && sizes[0][0], + h: sizes && sizes[0][1], + format, + } +} + +function createNativeRequest(params) { + let nativeRequestObject = { + plcmtcnt: 1, + assets: [] + }; + for (let key in params) { + let assetObj = {}; + if (params.hasOwnProperty(key)) { + if (!(nativeRequestObject.assets && nativeRequestObject.assets.length > 0 && nativeRequestObject.assets.hasOwnProperty(key))) { + switch (key) { + case NATIVE_ASSETS.TITLE.KEY: + assetObj = { + id: NATIVE_ASSETS.TITLE.ID, + required: params[key].required ? 1 : 0, + title: { + len: params[key].len || params[key].length || DEFAULT_TITLE_LENGTH + } + }; + break; + case NATIVE_ASSETS.IMAGE.KEY: + const wmin = params[key].wmin || params[key].minimumWidth || (utils.isArray(params[key].minsizes) && params[key].minsizes.length > 0 ? params[key].minsizes[0] : 0); + const hmin = params[key].hmin || params[key].minimumHeight || (utils.isArray(params[key].minsizes) && params[key].minsizes.length > 1 ? params[key].minsizes[1] : 0); + assetObj = { + id: NATIVE_ASSETS.IMAGE.ID, + required: params[key].required ? 1 : 0, + img: { + type: NATIVE_ASSET_IMAGE_TYPE.IMAGE, + w: params[key].w || params[key].width || (utils.isArray(params[key].sizes) && params[key].sizes.length > 0 ? params[key].sizes[0] : 0), + h: params[key].h || params[key].height || (utils.isArray(params[key].sizes) && params[key].sizes.length > 1 ? params[key].sizes[1] : 0), + mimes: params[key].mimes, + ext: params[key].ext, + } + }; + if (wmin > 0) { + assetObj.img.wmin = wmin; + } + if (hmin > 0) { + assetObj.img.hmin = hmin; + } + if (!assetObj.img.w) { + assetObj.img.w = DEFAULT_IMAGE_WIDTH; + } + if (!assetObj.img.h) { + assetObj.img.h = DEFAULT_IMAGE_HEIGHT; + } + break; + case NATIVE_ASSETS.ICON.KEY: + assetObj = { + id: NATIVE_ASSETS.ICON.ID, + required: params[key].required ? 1 : 0, + img: { + type: NATIVE_ASSET_IMAGE_TYPE.ICON, + w: params[key].w || params[key].width || (utils.isArray(params[key].sizes) && params[key].sizes.length > 0 ? params[key].sizes[0] : 0), + h: params[key].h || params[key].height || (utils.isArray(params[key].sizes) && params[key].sizes.length > 0 ? params[key].sizes[1] : 0), + } + }; + if (!assetObj.img.w) { + assetObj.img.w = DEFAULT_ICON_WIDTH; + } + if (!assetObj.img.h) { + assetObj.img.h = DEFAULT_ICON_HEIGHT; + } + break; + case NATIVE_ASSETS.SPONSORED.KEY: + case NATIVE_ASSETS.SPONSOREDBY.KEY: + case NATIVE_ASSETS.PRICE.KEY: + case NATIVE_ASSETS.SALEPRICE.KEY: + case NATIVE_ASSETS.DESC.KEY: + case NATIVE_ASSETS.BODY.KEY: + case NATIVE_ASSETS.DISPLAYURL.KEY: + case NATIVE_ASSETS.CTA.KEY: + assetObj = commonNativeRequestObject(spec.NATIVE_ASSET_KEY_TO_ASSET_MAP[key], params); + break; + default: + if (params[key].required) { + isInvalidNativeRequest = true; + return; + } + } + } + } + if (assetObj.id) { + nativeRequestObject.assets[nativeRequestObject.assets.length] = assetObj; + } + } + + // for native image adtype prebid has to have few required assests i.e. title,sponsoredBy, image + // if any of these are missing from the request then request will not be sent + let requiredAssetCount = NATIVE_MINIMUM_REQUIRED_IMAGE_ASSETS.length; + let presentrequiredAssetCount = 0; + NATIVE_MINIMUM_REQUIRED_IMAGE_ASSETS.forEach(ele => { + let lengthOfExistingAssets = nativeRequestObject.assets.length; + for (let i = 0; i < lengthOfExistingAssets; i++) { + if (ele.id === nativeRequestObject.assets[i].id) { + presentrequiredAssetCount++; + break; + } else { + if (ele.id === 4 && nativeRequestObject.assets[i].id === 11) { + if (utils.deepAccess(nativeRequestObject.assets[i], 'data.type') === ele.data.type) { + presentrequiredAssetCount++; + break; + } + } + } + } + }); + isInvalidNativeRequest = requiredAssetCount !== presentrequiredAssetCount; + return nativeRequestObject; +} + +function commonNativeRequestObject(nativeAsset, params) { + const key = nativeAsset.KEY; + return { + id: nativeAsset.ID, + required: params[key].required ? 1 : 0, + data: { + type: nativeAsset.TYPE, + len: params[key].len, + ext: params[key].ext + } + }; +} + +function parseNativeResponse(bid, newBid) { + newBid.native = {}; + if (bid.hasOwnProperty('adm')) { + let adm = ''; + try { + adm = JSON.parse(bid.adm); + } catch (ex) { + utils.logWarn(LOG_WARN_PREFIX + 'Error: Cannot parse native response for ad response: ' + newBid.adm); + return; + } + if (adm && adm.native && adm.native.assets && adm.native.assets.length > 0) { + newBid.mediaType = NATIVE; + for (let i = 0, len = adm.native.assets.length; i < len; i++) { + switch (adm.native.assets[i].id) { + case NATIVE_ASSETS.TITLE.ID: + newBid.native.title = adm.native.assets[i].title && adm.native.assets[i].title.text; + break; + case NATIVE_ASSETS.IMAGE.ID: + newBid.native.image = { + url: adm.native.assets[i].img && adm.native.assets[i].img.url, + height: adm.native.assets[i].img && adm.native.assets[i].img.h, + width: adm.native.assets[i].img && adm.native.assets[i].img.w, + }; + break; + case NATIVE_ASSETS.ICON.ID: + newBid.native.icon = { + url: adm.native.assets[i].img && adm.native.assets[i].img.url, + height: adm.native.assets[i].img && adm.native.assets[i].img.h, + width: adm.native.assets[i].img && adm.native.assets[i].img.w, + }; + break; + case NATIVE_ASSETS.SPONSOREDBY.ID: + case NATIVE_ASSETS.SPONSORED.ID: + case NATIVE_ASSETS.PRICE: + case NATIVE_ASSETS.SALEPRICE.ID: + case NATIVE_ASSETS.DESC.ID: + case NATIVE_ASSETS.BODY.ID: + case NATIVE_ASSETS.DISPLAYURL.ID: + case NATIVE_ASSETS.CTA.ID: + newBid.native[spec.NATIVE_ASSET_ID_TO_KEY_MAP[adm.native.assets[i].id]] = adm.native.assets[i].data && adm.native.assets[i].data.value; + break; + } + } + newBid.native.clickUrl = adm.native.link && adm.native.link.url; + newBid.native.clickTrackers = (adm.native.link && adm.native.link.clicktrackers) || []; + newBid.native.impressionTrackers = adm.native.imptrackers || []; + newBid.native.jstracker = adm.native.jstracker || []; + newBid.width = 0; + newBid.height = 0; + } + } +} diff --git a/test/spec/modules/mgidBidAdapter_spec.js b/test/spec/modules/mgidBidAdapter_spec.js index 4c67447489d..2b1a0739537 100644 --- a/test/spec/modules/mgidBidAdapter_spec.js +++ b/test/spec/modules/mgidBidAdapter_spec.js @@ -1,16 +1,22 @@ -import {expect} from 'chai'; +import {assert, expect} from 'chai'; import {spec} from 'modules/mgidBidAdapter'; import * as utils from '../../../src/utils'; import * as urlUtils from '../../../src/url'; describe('Mgid bid adapter', function () { let sandbox; + let logErrorSpy; + let logWarnSpy; beforeEach(function () { sandbox = sinon.sandbox.create(); + logErrorSpy = sinon.spy(utils, 'logError'); + logWarnSpy = sinon.spy(utils, 'logWarn'); }); afterEach(function () { sandbox.restore(); + utils.logError.restore(); + utils.logWarn.restore(); }); const ua = navigator.userAgent; const screenHeight = screen.height; @@ -53,6 +59,30 @@ describe('Mgid bid adapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(false); }); + it('should return false when valid params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.mediaTypes = { + banner: { + sizes: [[300, 250]] + } + }; + bid.params = {accountId: 2, placementId: 1}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when valid params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.mediaTypes = { + native: { + sizes: [[300, 250]] + } + }; + bid.params = {accountId: '0', placementId: '00'}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + it('should return false when valid mediaTypes are not passed', function () { let bid = Object.assign({}, bid); delete bid.params; @@ -100,6 +130,76 @@ describe('Mgid bid adapter', function () { }; expect(spec.isBidRequestValid(bid)).to.equal(true); }); + + it('should return false when valid mediaTypes.native is not object', function () { + let bid = Object.assign({}, bid); + bid.params = {accountId: '1', placementId: '1'}; + bid.mediaTypes = { + native: [] + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when mediaTypes.native is empty object', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {accountId: '1', placementId: '1'}; + bid.mediaTypes = { + native: {} + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when mediaTypes.native is invalid object', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {accountId: '1', placementId: '1'}; + bid.mediaTypes = { + native: { + image: { + sizes: [80, 80] + }, + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when mediaTypes.native has unsupported required asset', function () { + let bid = Object.assign({}, bid); + bid.params = {accountId: '2', placementId: '1'}; + bid.mediaTypes = { + native: { + title: {required: true}, + image: {required: false, sizes: [80, 80]}, + sponsored: {required: false}, + }, + }; + bid.nativeParams = { + title: {required: true}, + image: {required: false, sizes: [80, 80]}, + sponsored: {required: false}, + unsupported: {required: true}, + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return true when mediaTypes.native all assets needed', function () { + let bid = Object.assign({}, bid); + bid.params = {accountId: '2', placementId: '1'}; + bid.mediaTypes = { + native: { + title: {required: true}, + image: {required: false, sizes: [80, 80]}, + sponsored: {required: false}, + }, + }; + bid.nativeParams = { + title: {required: true}, + image: {required: false, sizes: [80, 80]}, + sponsored: {required: false}, + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); }); describe('override defaults', function () { @@ -138,6 +238,46 @@ describe('Mgid bid adapter', function () { const request = spec.buildRequests(bidRequests); expect(request.url).to.include('//newbidurl.com/1'); }); + it('should return overwrite default bidFloor', function () { + let bid = Object.assign({}, bid); + bid.params = { + bidFloor: 1.1, + accountId: '1', + placementId: '2', + }; + bid.mediaTypes = { + banner: { + sizes: [[300, 250]] + } + }; + let bidRequests = [bid]; + const request = spec.buildRequests(bidRequests); + expect(request.data).to.be.a('string'); + const data = JSON.parse(request.data); + expect(data).to.be.a('object'); + expect(data.imp).to.be.a('array'); + expect(data.imp).to.have.lengthOf(1); + expect(data.imp[0].bidfloor).to.deep.equal(1.1); + }); + it('should return overwrite default currency', function () { + let bid = Object.assign({}, bid); + bid.params = { + cur: 'GBP', + accountId: '1', + placementId: '2', + }; + bid.mediaTypes = { + banner: { + sizes: [[300, 250]] + } + }; + let bidRequests = [bid]; + const request = spec.buildRequests(bidRequests); + expect(request.data).to.be.a('string'); + const data = JSON.parse(request.data); + expect(data).to.be.a('object'); + expect(data.cur).to.deep.equal(['GBP']); + }); }); describe('buildRequests', function () { @@ -148,7 +288,7 @@ describe('Mgid bid adapter', function () { placementId: '2', }, }; - it('should return proper imp', function () { + it('should return proper banner imp', function () { let bid = Object.assign({}, abid); bid.mediaTypes = { banner: { @@ -159,7 +299,7 @@ describe('Mgid bid adapter', function () { const referer = utils.deepAccess(bidRequests, 'refererInfo.referer'); const domain = urlUtils.parse(referer).hostname; const request = spec.buildRequests(bidRequests); - expect(request.url).deep.equal('//dsp.mgid.com/prebid/1'); + expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1'); expect(request.method).deep.equal('POST'); const data = JSON.parse(request.data); expect(data.site.domain).to.deep.equal(domain); @@ -174,11 +314,94 @@ describe('Mgid bid adapter', function () { expect(data.imp[0].secure).to.deep.equal(secure); expect(request).to.deep.equal({ 'method': 'POST', - 'url': '//dsp.mgid.com/prebid/1', - 'data': '{\"site\":{\"domain\":\"' + domain + '\"},\"cur\":[\"USD\"],\"device\":{\"ua\":\"' + ua + '\",\"js\":1,\"dnt\":' + dnt + ',\"h\":' + screenHeight + ',\"w\":' + screenWidth + ',\"language\":\"' + lang + '\"},\"user\":{},\"regs\":{},\"ext\":{\"mgid_ver\":\"' + mgid_ver + '\",\"prebid_ver\":\"' + prebid_ver + '\"},\"imp\":[{\"tagid\":\"2\",\"banner\":{\"w\":300,\"h\":250,\"format\":[]},\"secure\":' + secure + '}]}', + 'url': 'https://prebid.mgid.com/prebid/1', + 'data': '{\"site\":{\"domain\":\"' + domain + '\"},\"cur\":[\"USD\"],\"device\":{\"ua\":\"' + ua + '\",\"js\":1,\"dnt\":' + dnt + ',\"h\":' + screenHeight + ',\"w\":' + screenWidth + ',\"language\":\"' + lang + '\"},\"user\":{},\"regs\":{},\"ext\":{\"mgid_ver\":\"' + mgid_ver + '\",\"prebid_ver\":\"' + prebid_ver + '\"},\"imp\":[{\"tagid\":\"2\",\"secure\":' + secure + ',\"banner\":{\"w\":300,\"h\":250,\"format\":[]}}]}', }); }); - it('should return proper request', function () { + it('should not return native imp if minimum asset list not requested', function () { + let bid = Object.assign({}, abid); + bid.mediaTypes = { + native: '', + }; + bid.nativeParams = { + title: {required: true}, + image: {sizes: [80, 80]}, + }; + let bidRequests = [bid]; + const request = spec.buildRequests(bidRequests); + expect(request).to.be.undefined; + }); + it('should return proper native imp', function () { + let bid = Object.assign({}, abid); + bid.mediaTypes = { + native: '', + }; + bid.nativeParams = { + title: {required: true}, + image: {sizes: [80, 80]}, + sponsored: { }, + }; + + let bidRequests = [bid]; + const referer = utils.deepAccess(bidRequests, 'refererInfo.referer'); + const domain = urlUtils.parse(referer).hostname; + const request = spec.buildRequests(bidRequests); + expect(request).to.be.a('object'); + expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1'); + expect(request.method).deep.equal('POST'); + const data = JSON.parse(request.data); + expect(data.site.domain).to.deep.equal(domain); + expect(data.cur).to.deep.equal(['USD']); + expect(data.device.ua).to.deep.equal(ua); + expect(data.device.dnt).equal(dnt); + expect(data.device.h).equal(screenHeight); + expect(data.device.w).equal(screenWidth); + expect(data.device.language).to.deep.equal(lang); + expect(data.imp[0].tagid).to.deep.equal('2'); + expect(data.imp[0].native).is.a('object').and.to.deep.equal({'request': {'assets': [{'id': 1, 'required': 1, 'title': {'len': 80}}, {'id': 2, 'img': {'h': 80, 'type': 3, 'w': 80}, 'required': 0}, {'data': {'type': 1}, 'id': 11, 'required': 0}], 'plcmtcnt': 1}}); + expect(data.imp[0].secure).to.deep.equal(secure); + expect(request).to.deep.equal({ + 'method': 'POST', + 'url': 'https://prebid.mgid.com/prebid/1', + 'data': '{\"site\":{\"domain\":\"' + domain + '\"},\"cur\":[\"USD\"],\"device\":{\"ua\":\"' + ua + '\",\"js\":1,\"dnt\":' + dnt + ',\"h\":' + screenHeight + ',\"w\":' + screenWidth + ',\"language\":\"' + lang + '\"},\"user\":{},\"regs\":{},\"ext\":{\"mgid_ver\":\"' + mgid_ver + '\",\"prebid_ver\":\"' + prebid_ver + '\"},\"imp\":[{\"tagid\":\"2\",\"secure\":' + secure + ',\"native\":{\"request\":{\"plcmtcnt\":1,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}},{\"id\":2,\"required\":0,\"img\":{\"type\":3,\"w\":80,\"h\":80}},{\"id\":11,\"required\":0,\"data\":{\"type\":1}}]}}}]}', + }); + }); + it('should return proper native imp with sponsoredBy', function () { + let bid = Object.assign({}, abid); + bid.mediaTypes = { + native: '', + }; + bid.nativeParams = { + title: {required: true}, + image: {sizes: [80, 80]}, + sponsoredBy: { }, + }; + + let bidRequests = [bid]; + const referer = utils.deepAccess(bidRequests, 'refererInfo.referer'); + const domain = urlUtils.parse(referer).hostname; + const request = spec.buildRequests(bidRequests); + expect(request).to.be.a('object'); + expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1'); + expect(request.method).deep.equal('POST'); + const data = JSON.parse(request.data); + expect(data.site.domain).to.deep.equal(domain); + expect(data.cur).to.deep.equal(['USD']); + expect(data.device.ua).to.deep.equal(ua); + expect(data.device.dnt).equal(dnt); + expect(data.device.h).equal(screenHeight); + expect(data.device.w).equal(screenWidth); + expect(data.device.language).to.deep.equal(lang); + expect(data.imp[0].tagid).to.deep.equal('2'); + expect(data.imp[0].native).is.a('object').and.to.deep.equal({'request': {'assets': [{'id': 1, 'required': 1, 'title': {'len': 80}}, {'id': 2, 'img': {'h': 80, 'type': 3, 'w': 80}, 'required': 0}, {'data': {'type': 1}, 'id': 4, 'required': 0}], 'plcmtcnt': 1}}); + expect(data.imp[0].secure).to.deep.equal(secure); + expect(request).to.deep.equal({ + 'method': 'POST', + 'url': 'https://prebid.mgid.com/prebid/1', + 'data': '{\"site\":{\"domain\":\"' + domain + '\"},\"cur\":[\"USD\"],\"device\":{\"ua\":\"' + ua + '\",\"js\":1,\"dnt\":' + dnt + ',\"h\":' + screenHeight + ',\"w\":' + screenWidth + ',\"language\":\"' + lang + '\"},\"user\":{},\"regs\":{},\"ext\":{\"mgid_ver\":\"' + mgid_ver + '\",\"prebid_ver\":\"' + prebid_ver + '\"},\"imp\":[{\"tagid\":\"2\",\"secure\":' + secure + ',\"native\":{\"request\":{\"plcmtcnt\":1,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}},{\"id\":2,\"required\":0,\"img\":{\"type\":3,\"w\":80,\"h\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":1}}]}}}]}', + }); + }); + it('should return proper banner request', function () { let bid = Object.assign({}, abid); bid.mediaTypes = { banner: { @@ -190,7 +413,7 @@ describe('Mgid bid adapter', function () { const referer = utils.deepAccess(bidRequests, 'refererInfo.referer'); const domain = urlUtils.parse(referer).hostname; - expect(request.url).deep.equal('//dsp.mgid.com/prebid/1'); + expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1'); expect(request.method).deep.equal('POST'); const data = JSON.parse(request.data); expect(data.site.domain).to.deep.equal(domain); @@ -206,19 +429,19 @@ describe('Mgid bid adapter', function () { expect(request).to.deep.equal({ 'method': 'POST', - 'url': '//dsp.mgid.com/prebid/1', - 'data': '{\"site\":{\"domain\":\"' + domain + '\"},\"cur\":[\"USD\"],\"device\":{\"ua\":\"' + ua + '\",\"js\":1,\"dnt\":' + dnt + ',\"h\":' + screenHeight + ',\"w\":' + screenWidth + ',\"language\":\"' + lang + '\"},\"user\":{},\"regs\":{},\"ext\":{\"mgid_ver\":\"' + mgid_ver + '\",\"prebid_ver\":\"' + prebid_ver + '\"},\"imp\":[{\"tagid\":\"2\",\"banner\":{\"w\":300,\"h\":600,\"format\":[{\"w\":300,\"h\":600},{\"w\":300,\"h\":250}]},\"secure\":' + secure + '}]}', + 'url': 'https://prebid.mgid.com/prebid/1', + 'data': '{\"site\":{\"domain\":\"' + domain + '\"},\"cur\":[\"USD\"],\"device\":{\"ua\":\"' + ua + '\",\"js\":1,\"dnt\":' + dnt + ',\"h\":' + screenHeight + ',\"w\":' + screenWidth + ',\"language\":\"' + lang + '\"},\"user\":{},\"regs\":{},\"ext\":{\"mgid_ver\":\"' + mgid_ver + '\",\"prebid_ver\":\"' + prebid_ver + '\"},\"imp\":[{\"tagid\":\"2\",\"secure\":' + secure + ',\"banner\":{\"w\":300,\"h\":600,\"format\":[{\"w\":300,\"h\":600},{\"w\":300,\"h\":250}]}}]}', }); }); }); - describe('interpretResponse', function () { + describe('interpretResponse banner', function () { it('should not push bid response', function () { let bids = spec.interpretResponse(); - expect(bids).to.deep.equal([]); + expect(bids).to.be.undefined; }); - it('should push proper bid response', function () { + it('should push proper banner bid response', function () { let resp = { - body: {'id': '57c0c2b1b732ca', 'bidid': '57c0c2b1b732ca', 'cur': 'USD', 'seatbid': [{'bid': [{'price': 1.5, 'h': 600, 'w': 300, 'id': '1', 'impid': '61e40632c53fc2', 'adid': '2898532/2419121/2592854/2499195', 'nurl': 'http: nurl', 'burl': 'http: nurl', 'adm': 'html: adm', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2']}], 'seat': '44082'}]} + body: {'id': '57c0c2b1b732ca', 'bidid': '57c0c2b1b732ca', 'cur': '', 'seatbid': [{'bid': [{'price': 1.5, 'h': 600, 'w': 300, 'id': '1', 'impid': '61e40632c53fc2', 'adid': '2898532/2419121/2592854/2499195', 'nurl': 'http: nurl', 'burl': 'http: burl', 'adm': 'html: adm', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2']}], 'seat': '44082'}]} }; let bids = spec.interpretResponse(resp); expect(bids).to.deep.equal([ @@ -233,6 +456,7 @@ describe('Mgid bid adapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'nurl': 'http: nurl', + 'burl': 'http: burl', 'requestId': '61e40632c53fc2', 'ttl': 300, 'width': 300, @@ -240,4 +464,124 @@ describe('Mgid bid adapter', function () { ]); }); }); + describe('interpretResponse native', function () { + it('should not push proper native bid response if adm is missing', function () { + let resp = { + body: {'id': '57c0c2b1b732ca', 'bidid': '57c0c2b1b732ca', 'cur': 'GBP', 'seatbid': [{'bid': [{'price': 1.5, 'h': 600, 'w': 300, 'id': '1', 'impid': '61e40632c53fc2', 'adid': '2898532/2419121/2592854/2499195', 'nurl': 'http: nurl', 'burl': 'http: burl', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2'], 'ext': {'place': 0, 'crtype': 'native'}}], 'seat': '44082'}]} + }; + let bids = spec.interpretResponse(resp); + expect(bids).to.deep.equal([]) + }); + it('should not push proper native bid response if assets is empty', function () { + let resp = { + body: {'id': '57c0c2b1b732ca', 'bidid': '57c0c2b1b732ca', 'cur': 'GBP', 'seatbid': [{'bid': [{'price': 1.5, 'h': 600, 'w': 300, 'id': '1', 'impid': '61e40632c53fc2', 'adid': '2898532/2419121/2592854/2499195', 'nurl': 'http: nurl', 'burl': 'http: burl', 'adm': '{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"link_url\"},\"assets\":[],\"imptrackers\":[\"imptrackers1\"]}}', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2'], 'ext': {'place': 0, 'crtype': 'native'}}], 'seat': '44082'}]} + }; + let bids = spec.interpretResponse(resp); + expect(bids).to.deep.equal([]) + }); + it('should push proper native bid response', function () { + let resp = { + body: {'id': '57c0c2b1b732ca', 'bidid': '57c0c2b1b732ca', 'cur': 'GBP', 'seatbid': [{'bid': [{'price': 1.5, 'h': 600, 'w': 300, 'id': '1', 'impid': '61e40632c53fc2', 'adid': '2898532/2419121/2592854/2499195', 'nurl': 'http: nurl', 'burl': 'http: burl', 'adm': '{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"link_url\"},\"assets\":[{\"id\":1,\"required\":0,\"title\":{\"text\":\"title1\"}},{\"id\":2,\"required\":0,\"img\":{\"w\":80,\"h\":80,\"type\":3,\"url\":\"image_src\"}},{\"id\":3,\"required\":0,\"img\":{\"w\":50,\"h\":50,\"type\":1,\"url\":\"icon_src\"}},{\"id\":4,\"required\":0,\"data\":{\"type\":4,\"value\":\"sponsored\"}},{\"id\":5,\"required\":0,\"data\":{\"type\":6,\"value\":\"price1\"}},{\"id\":6,\"required\":0,\"data\":{\"type\":7,\"value\":\"price2\"}}],\"imptrackers\":[\"imptrackers1\"]}}', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2'], 'ext': {'place': 0, 'crtype': 'native'}}], 'seat': '44082'}], ext: {'muidn': 'userid'}} + }; + let bids = spec.interpretResponse(resp); + expect(bids).to.deep.equal([{ + 'ad': '{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"link_url\"},\"assets\":[{\"id\":1,\"required\":0,\"title\":{\"text\":\"title1\"}},{\"id\":2,\"required\":0,\"img\":{\"w\":80,\"h\":80,\"type\":3,\"url\":\"image_src\"}},{\"id\":3,\"required\":0,\"img\":{\"w\":50,\"h\":50,\"type\":1,\"url\":\"icon_src\"}},{\"id\":4,\"required\":0,\"data\":{\"type\":4,\"value\":\"sponsored\"}},{\"id\":5,\"required\":0,\"data\":{\"type\":6,\"value\":\"price1\"}},{\"id\":6,\"required\":0,\"data\":{\"type\":7,\"value\":\"price2\"}}],\"imptrackers\":[\"imptrackers1\"]}}', + 'burl': 'http: burl', + 'cpm': 1.5, + 'creativeId': '2898532/2419121/2592854/2499195', + 'currency': 'GBP', + 'dealId': '', + 'height': 0, + 'isBurl': true, + 'mediaType': 'native', + 'native': { + 'clickTrackers': [], + 'clickUrl': 'link_url', + 'data': 'price1', + 'icon': { + 'height': 50, + 'url': 'icon_src', + 'width': 50 + }, + 'image': { + 'height': 80, + 'url': 'image_src', + 'width': 80 + }, + 'impressionTrackers': [ + 'imptrackers1' + ], + 'jstracker': [], + 'sponsoredBy': 'sponsored', + 'title': 'title1' + }, + 'netRevenue': true, + 'nurl': 'http: nurl', + 'requestId': '61e40632c53fc2', + 'ttl': 300, + 'width': 0 + }]) + }); + it('should push proper native bid response', function () { + let resp = { + body: {'id': '57c0c2b1b732ca', 'bidid': '57c0c2b1b732ca', 'cur': 'GBP', 'seatbid': [{'bid': [{'price': 1.5, 'h': 600, 'w': 300, 'id': '1', 'impid': '61e40632c53fc2', 'adid': '2898532/2419121/2592854/2499195', 'nurl': 'http: nurl', 'burl': 'http: burl', 'adm': '{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"link_url\"},\"assets\":[{\"id\":1,\"required\":0,\"title\":{\"text\":\"title1\"}},{\"id\":2,\"required\":0,\"img\":{\"w\":80,\"h\":80,\"type\":3,\"url\":\"image_src\"}},{\"id\":3,\"required\":0,\"img\":{\"w\":50,\"h\":50,\"type\":1,\"url\":\"icon_src\"}}],\"imptrackers\":[\"imptrackers1\"]}}', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2'], 'ext': {'place': 0, 'crtype': 'native'}}], 'seat': '44082'}]} + }; + let bids = spec.interpretResponse(resp); + expect(bids).to.deep.equal([ + { + 'ad': '{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"link_url\"},\"assets\":[{\"id\":1,\"required\":0,\"title\":{\"text\":\"title1\"}},{\"id\":2,\"required\":0,\"img\":{\"w\":80,\"h\":80,\"type\":3,\"url\":\"image_src\"}},{\"id\":3,\"required\":0,\"img\":{\"w\":50,\"h\":50,\"type\":1,\"url\":\"icon_src\"}}],\"imptrackers\":[\"imptrackers1\"]}}', + 'cpm': 1.5, + 'creativeId': '2898532/2419121/2592854/2499195', + 'currency': 'GBP', + 'dealId': '', + 'height': 0, + 'isBurl': true, + 'mediaType': 'native', + 'netRevenue': true, + 'nurl': 'http: nurl', + 'burl': 'http: burl', + 'requestId': '61e40632c53fc2', + 'ttl': 300, + 'width': 0, + 'native': { + clickTrackers: [], + title: 'title1', + image: { + url: 'image_src', + width: 80, + height: 80, + }, + icon: { + url: 'icon_src', + width: 50, + height: 50, + }, + impressionTrackers: ['imptrackers1'], + jstracker: [], + clickUrl: 'link_url', + } + } + ]); + }); + }); + + describe('on bidWon', function () { + it('should replace nurl and burl for native', function () { + const burl = 'burl&s=${' + 'AUCTION_PRICE}'; + const nurl = 'nurl&s=${' + 'AUCTION_PRICE}'; + const bid = {'bidderCode': 'mgid', 'width': 0, 'height': 0, 'statusMessage': 'Bid available', 'adId': '3d0b6ff1dda89', 'requestId': '2a423489e058a1', 'mediaType': 'native', 'source': 'client', 'ad': '{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"LinkURL\"},\"assets\":[{\"id\":1,\"required\":0,\"title\":{\"text\":\"TITLE\"}},{\"id\":2,\"required\":0,\"img\":{\"w\":80,\"h\":80,\"type\":3,\"url\":\"ImageURL\"}},{\"id\":3,\"required\":0,\"img\":{\"w\":50,\"h\":50,\"type\":1,\"url\":\"IconURL\"}},{\"id\":11,\"required\":0,\"data\":{\"type\":1,\"value\":\"sponsored\"}}],\"imptrackers\":[\"ImpTrackerURL\"]}}', 'cpm': 0.66, 'creativeId': '353538_591471', 'currency': 'USD', 'dealId': '', 'netRevenue': true, 'ttl': 300, 'nurl': nurl, 'burl': burl, 'isBurl': true, 'native': {'title': 'TITLE', 'image': {'url': 'ImageURL', 'height': 80, 'width': 80}, 'icon': {'url': 'IconURL', 'height': 50, 'width': 50}, 'sponsored': 'sponsored', 'clickUrl': 'LinkURL', 'clickTrackers': [], 'impressionTrackers': ['ImpTrackerURL'], 'jstracker': []}, 'auctionId': 'a92bffce-14d2-4f8f-a78a-7b9b5e4d28fa', 'responseTimestamp': 1556867386065, 'requestTimestamp': 1556867385916, 'bidder': 'mgid', 'adUnitCode': 'div-gpt-ad-1555415275793-0', 'timeToRespond': 149, 'pbLg': '0.50', 'pbMg': '0.60', 'pbHg': '0.66', 'pbAg': '0.65', 'pbDg': '0.66', 'pbCg': '', 'size': '0x0', 'adserverTargeting': {'hb_bidder': 'mgid', 'hb_adid': '3d0b6ff1dda89', 'hb_pb': '0.66', 'hb_size': '0x0', 'hb_source': 'client', 'hb_format': 'native', 'hb_native_title': 'TITLE', 'hb_native_image': 'hb_native_image:3d0b6ff1dda89', 'hb_native_icon': 'IconURL', 'hb_native_linkurl': 'hb_native_linkurl:3d0b6ff1dda89'}, 'status': 'targetingSet', 'params': [{'accountId': '184', 'placementId': '353538'}]}; + spec.onBidWon(bid); + expect(bid.nurl).to.deep.equal('nurl&s=0.66'); + expect(bid.burl).to.deep.equal('burl&s=0.66'); + }); + it('should replace nurl and burl for banner', function () { + const burl = 'burl&s=${' + 'AUCTION_PRICE}'; + const nurl = 'nurl&s=${' + 'AUCTION_PRICE}'; + const bid = {'bidderCode': 'mgid', 'width': 0, 'height': 0, 'statusMessage': 'Bid available', 'adId': '3d0b6ff1dda89', 'requestId': '2a423489e058a1', 'mediaType': 'banner', 'source': 'client', 'ad': burl, 'cpm': 0.66, 'creativeId': '353538_591471', 'currency': 'USD', 'dealId': '', 'netRevenue': true, 'ttl': 300, 'nurl': nurl, 'burl': burl, 'isBurl': true, 'auctionId': 'a92bffce-14d2-4f8f-a78a-7b9b5e4d28fa', 'responseTimestamp': 1556867386065, 'requestTimestamp': 1556867385916, 'bidder': 'mgid', 'adUnitCode': 'div-gpt-ad-1555415275793-0', 'timeToRespond': 149, 'pbLg': '0.50', 'pbMg': '0.60', 'pbHg': '0.66', 'pbAg': '0.65', 'pbDg': '0.66', 'pbCg': '', 'size': '0x0', 'adserverTargeting': {'hb_bidder': 'mgid', 'hb_adid': '3d0b6ff1dda89', 'hb_pb': '0.66', 'hb_size': '0x0', 'hb_source': 'client', 'hb_format': 'banner', 'hb_banner_title': 'TITLE', 'hb_banner_image': 'hb_banner_image:3d0b6ff1dda89', 'hb_banner_icon': 'IconURL', 'hb_banner_linkurl': 'hb_banner_linkurl:3d0b6ff1dda89'}, 'status': 'targetingSet', 'params': [{'accountId': '184', 'placementId': '353538'}]}; + spec.onBidWon(bid); + expect(bid.nurl).to.deep.equal('nurl&s=0.66'); + expect(bid.burl).to.deep.equal(burl); + expect(bid.ad).to.deep.equal('burl&s=0.66'); + }); + }); }); From af729970d9df46ff37f8afc4d3ea9fceecbe02c0 Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Tue, 14 May 2019 15:51:06 -0400 Subject: [PATCH 064/146] Prebid 2.15.0 release --- package-lock.json | 200 ++++++++++++++++++++++++++++++---------------- package.json | 2 +- 2 files changed, 134 insertions(+), 68 deletions(-) diff --git a/package-lock.json b/package-lock.json index 262d67c052d..b00be53f565 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.15.0-pre", + "version": "2.15.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -6065,24 +6065,28 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, "optional": true, "requires": { @@ -6092,12 +6096,14 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, "brace-expansion": { "version": "1.1.11", - "bundled": true, + "resolved": false, + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -6106,34 +6112,40 @@ }, "chownr": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, "concat-map": { "version": "0.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true }, "core-util-is": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true }, "debug": { "version": "2.6.9", - "bundled": true, + "resolved": false, + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "optional": true, "requires": { @@ -6142,25 +6154,29 @@ }, "deep-extend": { "version": "0.6.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "optional": true, "requires": { @@ -6169,13 +6185,15 @@ }, "fs.realpath": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "bundled": true, + "resolved": false, + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, "requires": { @@ -6191,7 +6209,8 @@ }, "glob": { "version": "7.1.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "optional": true, "requires": { @@ -6205,13 +6224,15 @@ }, "has-unicode": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.24", - "bundled": true, + "resolved": false, + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "optional": true, "requires": { @@ -6220,7 +6241,8 @@ }, "ignore-walk": { "version": "3.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, "requires": { @@ -6229,7 +6251,8 @@ }, "inflight": { "version": "1.0.6", - "bundled": true, + "resolved": false, + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, "requires": { @@ -6239,18 +6262,21 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, "ini": { "version": "1.3.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { "number-is-nan": "^1.0.0" @@ -6258,13 +6284,15 @@ }, "isarray": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -6272,12 +6300,14 @@ }, "minimist": { "version": "0.0.8", - "bundled": true, + "resolved": false, + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, "minipass": { "version": "2.3.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, "requires": { "safe-buffer": "^5.1.2", @@ -6286,7 +6316,8 @@ }, "minizlib": { "version": "1.2.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "dev": true, "optional": true, "requires": { @@ -6295,7 +6326,8 @@ }, "mkdirp": { "version": "0.5.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { "minimist": "0.0.8" @@ -6303,13 +6335,15 @@ }, "ms": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true, "optional": true }, "needle": { "version": "2.2.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==", "dev": true, "optional": true, "requires": { @@ -6320,7 +6354,8 @@ }, "node-pre-gyp": { "version": "0.10.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A==", "dev": true, "optional": true, "requires": { @@ -6338,7 +6373,8 @@ }, "nopt": { "version": "4.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, "requires": { @@ -6348,13 +6384,15 @@ }, "npm-bundled": { "version": "1.0.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-7Mni4Z8Xkx0/oegoqlcao/JpPCPEMtUvsmB0q7mgvlMinykJLSRTYuFqoQLYgGY8biuxIeiHO+QNJKbCfljewQ==", "dev": true, "optional": true, "requires": { @@ -6364,7 +6402,8 @@ }, "npmlog": { "version": "4.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, "requires": { @@ -6376,18 +6415,21 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, "object-assign": { "version": "4.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { "wrappy": "1" @@ -6395,19 +6437,22 @@ }, "os-homedir": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, "requires": { @@ -6417,19 +6462,22 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true, "optional": true }, "rc": { "version": "1.2.8", - "bundled": true, + "resolved": false, + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "optional": true, "requires": { @@ -6441,7 +6489,8 @@ "dependencies": { "minimist": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true } @@ -6449,7 +6498,8 @@ }, "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, "requires": { @@ -6464,7 +6514,8 @@ }, "rimraf": { "version": "2.6.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "dev": true, "optional": true, "requires": { @@ -6473,42 +6524,49 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, "safer-buffer": { "version": "2.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true }, "semver": { "version": "5.6.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { "code-point-at": "^1.0.0", @@ -6518,7 +6576,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "optional": true, "requires": { @@ -6527,7 +6586,8 @@ }, "strip-ansi": { "version": "3.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -6535,13 +6595,15 @@ }, "strip-json-comments": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "4.4.8", - "bundled": true, + "resolved": false, + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", "dev": true, "optional": true, "requires": { @@ -6556,13 +6618,15 @@ }, "util-deprecate": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true }, "wide-align": { "version": "1.1.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "optional": true, "requires": { @@ -6571,12 +6635,14 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, "yallist": { "version": "3.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "dev": true } } diff --git a/package.json b/package.json index 6d29154f310..5469fc5880a 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.15.0-pre", + "version": "2.15.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From b58d49c1cc582370d3bd7f30a8c0c676d9c7d29e Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Tue, 14 May 2019 16:03:47 -0400 Subject: [PATCH 065/146] increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5469fc5880a..6bb0b49fab0 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.15.0", + "version": "2.16.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 90accd8dcb28d9239db6a0059b9074d73817c77b Mon Sep 17 00:00:00 2001 From: jacekburys-quantcast <44467819+jacekburys-quantcast@users.noreply.github.com> Date: Wed, 15 May 2019 14:11:13 +0100 Subject: [PATCH 066/146] updating maintainer email address in quantcastBidAdapter.md (#3830) --- modules/quantcastBidAdapter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/quantcastBidAdapter.md b/modules/quantcastBidAdapter.md index 5d0c2e10fc0..2b55eae9026 100644 --- a/modules/quantcastBidAdapter.md +++ b/modules/quantcastBidAdapter.md @@ -3,7 +3,7 @@ ``` Module Name: Quantcast Bidder Adapter Module Type: Bidder Adapter -Maintainer: igor.soarez@quantcast.com +Maintainer: inventoryteam@quantcast.com ``` # Description From 3b9f9e11dbe90e78deb582cbe4fda72ac00c3bf7 Mon Sep 17 00:00:00 2001 From: mafernandez80 <50341807+mafernandez80@users.noreply.github.com> Date: Wed, 15 May 2019 13:19:49 -0300 Subject: [PATCH 067/146] Reload Adapter: New (#3812) * Reload Adapter: New * Reload Adapter - Spec * Reload Adapter and Spec - Changes according comments * Reload Adapter & Spec: lint errors fixed * Reload Adapter: Example updated --- modules/reloadBidAdapter.js | 422 +++++++++++++++++++++ modules/reloadBidAdapter.md | 48 +++ test/spec/modules/reloadBidAdapter_spec.js | 293 ++++++++++++++ 3 files changed, 763 insertions(+) create mode 100644 modules/reloadBidAdapter.js create mode 100644 modules/reloadBidAdapter.md create mode 100644 test/spec/modules/reloadBidAdapter_spec.js diff --git a/modules/reloadBidAdapter.js b/modules/reloadBidAdapter.js new file mode 100644 index 00000000000..a50949825a9 --- /dev/null +++ b/modules/reloadBidAdapter.js @@ -0,0 +1,422 @@ +import { + BANNER +} + from 'src/mediaTypes'; +import { + registerBidder +} + from 'src/adapters/bidderFactory'; + +const BIDDER_CODE = 'reload'; + +const VERSION_ADAPTER = '1.0'; + +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 && bid.params.plcmID && bid.params.partID && 'opdomID' in bid.params && + 'bsrvID' in bid.params && bid.params.bsrvID >= 0 && bid.params.bsrvID <= 99); + }, + /** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests, bidderRequest) { + let vRequests = []; + + let bidReq = { + id: Math.random().toString(10).substring(2), + imp: [] + }; + + let vPrxClientTool = null; + for (let vIdx = 0; vIdx < validBidRequests.length; vIdx++) { + let bidRequest = validBidRequests[vIdx]; + + if (BANNER in bidRequest.mediaTypes !== true) continue; + if (bidRequest.mediaTypes.banner.sizes.length <= 0) continue; + + let vDim = bidRequest.mediaTypes.banner.sizes[0]; + + vPrxClientTool = new ReloadClientTool({ + prxVer: VERSION_ADAPTER, + prxType: 'bd', + + plcmID: bidRequest.params.plcmID, + partID: bidRequest.params.partID, + opdomID: bidRequest.params.opdomID, + bsrvID: bidRequest.params.bsrvID + }); + + let vImpression = { + id: bidRequest.bidId, + bidId: bidRequest.bidId, + adUnitCode: bidRequest.adUnitCode, + transactionId: bidRequest.transactionId, + bidderRequestId: bidRequest.bidderRequestId, + auctionId: bidRequest.auctionId, + + banner: { + h: vDim[1], + w: vDim[0], + ext: { + type: bidRequest.params.type || 'pcm', + pcmdata: vPrxClientTool.getPCMObj() + } + } + }; + bidReq.imp.push(vImpression); + } + + if (bidReq.imp.length > 0) { + const payloadString = JSON.stringify(bidReq); + vRequests.push({ + method: 'POST', + url: vPrxClientTool.getSrvUrl(), + data: payloadString + }); + } + return vRequests; + }, + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse, bidRequest) { + const serverBody = serverResponse.body; + + const bidResponses = []; + + for (let vIdx = 0; vIdx < serverBody.seatbid.length; vIdx++) { + let vSeatBid = serverBody.seatbid[vIdx]; + + for (let vIdxBid = 0; vIdxBid < vSeatBid.bid.length; vIdxBid++) { + let vBid = vSeatBid.bid[vIdxBid]; + + let vPrxClientTool = new ReloadClientTool({ + plcmID: vBid.ext.plcmID, + partID: vBid.ext.partID, + opdomID: vBid.ext.opdomID, + bsrvID: vBid.ext.bsrvID + }); + + vPrxClientTool.setPCMObj(vBid.ext.pcmdata); + + if (vPrxClientTool.getBP() > 0) { + let bidResponse = { + requestId: vBid.impid, + ad: vPrxClientTool.getAM(), + cpm: vPrxClientTool.getBP() / 100, + width: vBid.ext.banner.w, + height: vBid.ext.banner.h, + creativeId: vBid.id, + currency: vPrxClientTool.getBC(), + ttl: 300, + netRevenue: true + }; + bidResponses.push(bidResponse); + } + } + } + + return bidResponses; + } +}; + +/** + * Reload Client Tool + * @param {json} args + */ + +function ReloadClientTool(args) { + var that = this; + + var _pcmClientVersion = '120'; + var _pcmFilePref = 'prx_root_'; + var _resFilePref = 'prx_pnws_'; + + var _pcmInputObjVers = '100'; + + var _instObj = null; + var _status = 'NA'; + var _message = ''; + var _log = ''; + + var _memFile = _getMemFile(); + + if (_memFile.status !== 'ok') { + _log += 'WARNING: clnt-int mem file initialized\n'; + } + + that.getPCMObj = function () { + return { + thisVer: _pcmInputObjVers, + + statStr: _memFile.statStr, + plcmData: _getPlcmData(), + clntData: _getClientData(), + resultData: _getRD(), + + proxetString: null, + dboData: null, + plcmSett: null, + }; + }; + + that.setPCMObj = function (obj) { + if (obj.thisVer !== '100') { + _status = 'error'; + _message = 'incomp_output_obj_version'; + _log += ' ERROR incomp_output_obj_version'; + return; + } + + _status = obj.status; + _message = obj.message; + _log += ' ' + obj.log; + + if (obj.status !== 'ok') return; + + _saveMemFile(obj.statStr, obj.srvUrl); + _instObj = obj.instr; + }; + + that.getSrvUrl = function () { + var effSrvUrl = getBidServerUrl(0); + + if (isNaN(parseInt(args.bsrvID)) !== true) effSrvUrl = getBidServerUrl(parseInt(args.bsrvID)); + + if (typeof _memFile.srvUrl === 'string' && _memFile.srvUrl !== '') effSrvUrl = _memFile.srvUrl; + + return _getProtocolString() + effSrvUrl + '/bid'; + + function getBidServerUrl (idx) { + return 'bidsrv' + getTwoDigitString(idx) + '.reload.net'; + + function getTwoDigitString (idx) { + if (idx >= 10) return '' + idx; + else return '0' + idx; + } + } + }; + + that.getBP = function () { + if (_instObj === null) return 0; + if (typeof _instObj === 'undefined') return 0; + if (_instObj.go !== true) return 0; + return _instObj.prc; + }; + + that.getBC = function () { + if (_instObj === null) return 0; + if (typeof _instObj === 'undefined') return 0; + if (_instObj.go !== true) return 0; + return _instObj.cur; + }; + + that.getAM = function () { + if (_instObj === null) return null; + if (typeof _instObj === 'undefined') return null; + if (_instObj.go !== true) return null; + return _instObj.am; + }; + + that.getPM = function () { + if (_instObj === null) return null; + if (typeof _instObj === 'undefined') return null; + if (_instObj.go === true) return null; + return _instObj.pbm; + }; + + that.setRD = function (data) { + return _setRD(data); + }; + + that.getStat = function () { + return _status; + }; + + that.getMsg = function () { + return _message; + }; + + that.getLog = function () { + return _log; + }; + + function _getPlcmData () { + return { + prxVer: args.prxVer, + prxType: args.prxType, + plcmID: args.plcmID, + partID: args.partID, + opdomID: args.opdomID, + bsrvID: args.bsrvID, + dmod: args.dmod, + lmod: args.lmod, + lplcmID: args.lplcmID, + }; + } + + function _getClientData () { + return { + version: 100, + locTime: Date.now(), + + winInfo: _genWinInfo(), + envInfo: getEnvInfo(), + confined: detectConfined(), + protStr: _getProtocolString(), + + hostDomain: decodeURIComponent(window.location.host), + hostPagePath: decodeURIComponent(window.location.pathname), + hostPageUrl: decodeURIComponent(window.location.href), + hostPageTitle: document.title, + }; + + function _genWinInfo () { + var winInfo = { + physicalWidth: window.screen.width, + physicalHeight: window.screen.height, + screenWidth: window.screen.availWidth, + screenHeight: window.screen.availHeight, + windowWidth: window.innerWidth, + windowHeight: window.innerHeight, + bodyHeight: document.body.clientHeight + }; + return winInfo; + } + + function getEnvInfo() { + return { + userAgent: navigator.userAgent, + appName: navigator.appName, + appVersion: navigator.appVersion + }; + } + + function detectConfined () { + var confined = true; + try { if (window.top === window.self) confined = false; } catch (err) {} + return confined; + } + } + + function _getMemFile () { + try { + var memFileObj = _getItem(_getMemFileName()); + + if (memFileObj === null) throw { s: 'init' }; + + if (typeof memFileObj.statStr !== 'string') throw { s: 'error' }; + if (typeof memFileObj.srvUrl !== 'string') throw { s: 'error' }; + + memFileObj.status = 'ok'; + + return memFileObj; + } catch (err) { + var retObj = { + statStr: null, + srvUrl: null + }; + retObj.status = err.s; + + return retObj; + } + } + + function _saveMemFile (statStr, srvUrl) { + try { + var fileData = { + statStr: statStr, + srvUrl: srvUrl, + }; + _setItem(_getMemFileName(), fileData); + return true; + } catch (err) { + return false; + } + } + + function _getMemFileName () { + return _pcmFilePref + args.plcmID + '_' + args.partID; + } + + function _getRD () { + try { + return _getItem(_getResltStatusFileName()); + } catch (err) { + return null; + } + } + + function _setRD (fileData) { + try { + _setItem(_getResltStatusFileName(), fileData); + return true; + } catch (err) { + return false; + } + } + + function _getResltStatusFileName () { + return _resFilePref + args.plcmID + '_' + args.partID; + } + + function _setItem (name, data) { + var stgFileObj = { + ver: _pcmClientVersion, + ts: Date.now(), + }; + + if (typeof data === 'string') { + stgFileObj.objtype = false; + stgFileObj.memdata = data; + } else { + stgFileObj.objtype = true; + stgFileObj.memdata = JSON.stringify(data); + } + + var stgFileStr = JSON.stringify(stgFileObj); + + localStorage.setItem(name, stgFileStr); + + return true; + } + + function _getItem (name) { + try { + var obStgFileStr = localStorage.getItem(name); + if (obStgFileStr === null) return null; + + var stgFileObj = JSON.parse(obStgFileStr); + + if (stgFileObj.ver !== _pcmClientVersion) throw { message: 'version_error' }; + + if (stgFileObj.objtype === true) return JSON.parse(stgFileObj.memdata); + else return '' + stgFileObj.memdata; + } catch (err) { + return null; + } + } + + function _getProtocolString () { + var wnd = null; + try { wnd = top; } catch (err) { wnd = window; } + + if (wnd.location.protocol.toLowerCase().indexOf('http:') >= 0) return 'http://'; + else return 'https://'; + } +}; + +registerBidder(spec); diff --git a/modules/reloadBidAdapter.md b/modules/reloadBidAdapter.md new file mode 100644 index 00000000000..42fe11b40b3 --- /dev/null +++ b/modules/reloadBidAdapter.md @@ -0,0 +1,48 @@ +# Overview + +Module Name: Reload Bid Adapter + +Module Type: Bidder Adapter + +Maintainer: prebid@reload.net + +# Description + +Prebid module for connecting to Reload + +# Parameters +## Banner + +| Name | Scope | Description | Example | +| :------------ | :------- | :---------------------------------------------- | :--------------------------------- | +| `plcmID` | required | Placement ID (provided by Reload) | "4234897234" | +| `partID` | required | Partition ID (provided by Reload) | "part_01" | +| `opdomID` | required | Internal parameter (provided by Reload) | 0 | +| `bsrvID` | required | Internal parameter (provided by Reload) | 12 | +| `type` | optional | Internal parameter (provided by Reload) | "pcm" | + +# Example ad units +# Test Parameters +``` +var adUnits = [ + // Banner adUnit + { + code: 'banner-div', + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ], + } + }, + bids: [{ + bidder: 'reload', + params: { + plcmID: 'prebid_check', + partID: 'part_4', + opdomID: '0', + bsrvID: 0, + type: 'pcm' + } + }] + }]; \ No newline at end of file diff --git a/test/spec/modules/reloadBidAdapter_spec.js b/test/spec/modules/reloadBidAdapter_spec.js new file mode 100644 index 00000000000..ebf7308caf2 --- /dev/null +++ b/test/spec/modules/reloadBidAdapter_spec.js @@ -0,0 +1,293 @@ +import { expect } from 'chai'; +import { spec } from 'modules/reloadBidAdapter'; + +let getParams = () => { + return JSON.parse(JSON.stringify({ + 'plcmID': 'placement_01', + 'partID': 'part00', + 'opdomID': 1, + 'bsrvID': 1, + 'type': 'pcm' + })); +}; + +let getBidderRequest = () => { + return JSON.parse(JSON.stringify({ + bidderCode: 'reload', + auctionId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', + bidderRequestId: '7101db09af0db2', + start: new Date().getTime(), + bids: [{ + bidder: 'reload', + bidId: '84ab500420319d', + bidderRequestId: '7101db09af0db2', + auctionId: 'd3e07445-ab06-44c8-a9dd-5ef9af06d2a6', + params: getParams() + }] + })); +}; + +let getValidBidRequests = () => { + return JSON.parse(JSON.stringify([ + { + 'bidder': 'reload', + 'params': getParams(), + 'mediaTypes': { + 'banner': { + 'sizes': [[160, 600]] + } + }, + 'adUnitCode': '1b243858-3c53-43dc-9fdf-89f839ea4a0f', + 'transactionId': '8cbafa10-123d-4673-a1a5-04a1c7d62ded', + 'sizes': [[160, 600]], + 'bidId': '2236e11dc09931', + 'bidderRequestId': '1266bb886c2267', + 'auctionId': '4fb72c4d-94dc-4db1-8fac-3c2090ceeec0', + 'src': 'client', + 'bidRequestsCount': 1 + } + ])); +} + +let getExt1ServerResponse = () => { + return JSON.parse(JSON.stringify({ + 'pcmdata': { + 'thisVer': '100', + 'plcmSett': { + 'name': 'zz_test_mariano_adapter', + 'Version': '210', + 'lifeSpan': '100', + 'versionFolder': 'v4.14q', + 'versionFolderA': 'v4.14q', + 'versionFolderB': '', + 'stage': 'zz_test_mariano_adapter', + 'synchro': 1556916507000, + 'localCache': 'true', + 'testCase': 'A:00_B:100', + 'opdomain': '1', + 'checksum': '6378', + 'cmp': '0', + 'bstfct': '100', + 'totstop': 'false', + 'pcmurl': 'bidsrv01.reload.net' + }, + 'srvUrl': 'bidsrv01.reload.net', + 'instr': {'go': true, 'prc': 32, 'cur': 'USD'}, + 'statStr': 'eyN4aHYnQCk5OTotOC', + 'status': 'ok', + 'message': '', + 'log': '---- LOG ----' + }, + 'plcmID': 'zz_test_mariano_adapter', + 'partID': 'prx_part', + 'opdomID': '0', + 'bsrvID': 1, + 'banner': {'w': 300, 'h': 250} + })); +} + +let getExt2ServerResponse = () => { + return JSON.parse(JSON.stringify({ + 'pcmdata': { + 'thisVer': '100', + 'plcmSett': { + 'name': 'placement_01', + 'Version': '210', + 'lifeSpan': '100', + 'versionFolder': 'v4.14q', + 'versionFolderA': 'v4.14q', + 'versionFolderB': '', + 'stage': 'placement_01', + 'synchro': 1556574760000, + 'localCache': 'true', + 'testCase': 'A:00_B:100', + 'opdomain': '1', + 'checksum': '6378', + 'cmp': '0', + 'bstfct': '100', + 'totstop': 'false', + 'pcmurl': 'bidsrv00.reload.net' + }, + 'srvUrl': 'bidsrv00.reload.net', + 'log': 'incomp_input_obj_version', + 'message': 'incomp_input_obj_version', + 'status': 'error' + }, + 'plcmID': 'placement_01', + 'partID': 'prx_part', + 'opdomID': '0', + 'bsrvID': 1, + 'banner': {'w': 160, 'h': 600} + })); +} + +let getServerResponse = (pExt) => { + return JSON.parse(JSON.stringify({ + 'body': { + 'id': '2759340f70210d', + 'bidid': 'fbs-br-3mzdbycetjv8f8079', + 'seatbid': [ + { + 'bid': [ + { + 'id': 'fbs-br-stbd-bd-3mzdbycetjv8f807b', + 'price': 0, + 'nurl': '', + 'adm': '', + 'ext': pExt + } + ], + 'seat': 'fbs-br-stbd-3mzdbycetjv8f807a', + 'group': 0 + } + ] + }, + 'headers': {} + })); +} + +describe('ReloadAdapter', function () { + describe('isBidRequestValid', function () { + var bid = { + 'bidder': 'reload', + 'params': { + 'plcmID': 'placement_01', + 'partID': 'part00', + 'opdomID': 1, + 'bsrvID': 23, + 'type': 'pcm' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when bsrvID is not number', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'plcmID': 'placement_01', + 'partID': 'part00', + 'opdomID': 1, + 'bsrvID': 'abc' + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when bsrvID > 99', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'plcmID': 'placement_01', + 'partID': 'part00', + 'opdomID': 1, + 'bsrvID': 230 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when bsrvID < 0', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'plcmID': 'placement_01', + 'partID': 'part00', + 'opdomID': 1, + 'bsrvID': -3, + 'type': 'pcm' + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'plcmID': 'placement_01' + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests()', function () { + let vRequests = spec.buildRequests(getValidBidRequests(), {}); + let vData = JSON.parse(vRequests[0].data); + + it('should send one requests', () => { + expect(vRequests.length).to.equal(1); + }); + + it('should send one requests, one impression', () => { + expect(vData.imp.length).to.equal(1); + }); + + it('should exists ext.type and ext.pcmdata', () => { + expect(vData.imp[0].banner).to.exist; + expect(vData.imp[0].banner.ext).to.exist; + expect(vData.imp[0].banner.ext.type).to.exist; + expect(vData.imp[0].banner.ext.pcmdata).to.exist; + expect(vData.imp[0].banner.ext.type).to.equal('pcm'); + }); + }); + + describe('interpretResponse()', function () { + it('Returns an empty array', () => { + let vData = spec.interpretResponse(getServerResponse(getExt2ServerResponse()), {}); + + expect(vData.length).to.equal(0); + }); + + it('Returns an array with one response', () => { + let vData = spec.interpretResponse(getServerResponse(getExt1ServerResponse()), {}); + expect(vData.length).to.equal(1); + }); + + it('required fileds', () => { + let vData = spec.interpretResponse(getServerResponse(getExt1ServerResponse()), {}); + expect(vData.length).to.equal(1); + expect(vData[0]).to.have.all.keys(['requestId', 'ad', 'cpm', 'width', 'height', 'creativeId', 'currency', 'ttl', 'netRevenue']); + }); + + it('CPM great than 0', () => { + let vData = spec.interpretResponse(getServerResponse(getExt1ServerResponse()), {}); + expect(vData[0].cpm).to.greaterThan(0); + }); + + it('instruction empty', () => { + let vResponse = Object.assign({}, getServerResponse(getExt1ServerResponse())); + vResponse.body.seatbid[0].bid[0].ext.pcmdata.instr = null; + let vData = spec.interpretResponse(vResponse, {}); + expect(vData.length).to.equal(0); + + vResponse = Object.assign({}, getServerResponse(getExt1ServerResponse())); + vResponse.body.seatbid[0].bid[0].ext.pcmdata.instr = undefined; + vData = spec.interpretResponse(vResponse, {}); + expect(vData.length).to.equal(0); + + vResponse = Object.assign({}, getServerResponse(getExt1ServerResponse())); + vResponse.body.seatbid[0].bid[0].ext.pcmdata.instr.go = undefined; + vData = spec.interpretResponse(vResponse, {}); + expect(vData.length).to.equal(0); + }); + + it('instruction with go = false', () => { + let vResponse = getServerResponse(getExt1ServerResponse()); + vResponse.body.seatbid[0].bid[0].ext.pcmdata.instr.go = false; + let vData = spec.interpretResponse(vResponse, {}); + expect(vData.length).to.equal(0); + }); + + it('incompatibility output object version (thisVer)', () => { + let vResponse = getServerResponse(getExt1ServerResponse()); + vResponse.body.seatbid[0].bid[0].ext.pcmdata.thisVer = '200'; + let vData = spec.interpretResponse(vResponse, {}); + expect(vData.length).to.equal(0); + }); + }); +}); From 141ae9fc56c6c3d2858c4d76599ad8b13d80225c Mon Sep 17 00:00:00 2001 From: Valentin Date: Thu, 16 May 2019 15:04:41 +0200 Subject: [PATCH 068/146] Teads-Adapter: Update way to find referrer (#3829) * Update way to find referrer * Add test * Delete npm-debug.log --- modules/teadsBidAdapter.js | 10 +++++++++- test/spec/modules/teadsBidAdapter_spec.js | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/modules/teadsBidAdapter.js b/modules/teadsBidAdapter.js index 13e6da40d97..92cfce312b1 100644 --- a/modules/teadsBidAdapter.js +++ b/modules/teadsBidAdapter.js @@ -40,7 +40,7 @@ export const spec = { buildRequests: function(validBidRequests, bidderRequest) { const bids = validBidRequests.map(buildRequestObject); const payload = { - referrer: utils.getTopWindowUrl(), + referrer: getReferrerInfo(bidderRequest), data: bids, deviceWidth: screen.width }; @@ -102,6 +102,14 @@ export const spec = { } }; +function getReferrerInfo(bidderRequest) { + let ref = ''; + if (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { + ref = bidderRequest.refererInfo.referer; + } + return ref; +} + function findGdprStatus(gdprApplies, gdprData) { let status = gdprStatus.GDPR_APPLIES_PUBLISHER; diff --git a/test/spec/modules/teadsBidAdapter_spec.js b/test/spec/modules/teadsBidAdapter_spec.js index 20fdbe947c0..57484d79b05 100644 --- a/test/spec/modules/teadsBidAdapter_spec.js +++ b/test/spec/modules/teadsBidAdapter_spec.js @@ -132,6 +132,22 @@ describe('teadsBidAdapter', function() { expect(payload.gdpr_iab.status).to.equal(12); }); + it('should add referer info to payload', function () { + const bidRequest = Object.assign({}, bidRequests[0]) + const bidderRequest = { + refererInfo: { + referer: 'http://example.com/page.html', + reachedTop: true, + numIframes: 2 + } + } + const request = spec.buildRequests([bidRequest], bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.referrer).to.exist; + expect(payload.referrer).to.deep.equal('http://example.com/page.html') + }); + it('should send GDPR to endpoint with 11 status', function() { let consentString = 'JRJ8RKfDeBNsERRDCSAAZ+A=='; let bidderRequest = { From d0b391f316a6ea4c69380802f9d3b47b6173f039 Mon Sep 17 00:00:00 2001 From: hdeodhar <35999856+hdeodhar@users.noreply.github.com> Date: Mon, 20 May 2019 13:32:55 +0100 Subject: [PATCH 069/146] Added size id 265 (1920x1080) (#3839) --- modules/rubiconBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 55937a3feda..eaa5b322811 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -79,7 +79,8 @@ var sizeMap = { 214: '980x360', 229: '320x180', 232: '580x400', - 257: '400x600' + 257: '400x600', + 265: '1920x1080' }; utils._each(sizeMap, (item, key) => sizeMap[item] = key); From 034b57e5f4e3a02419e6a9ba6705cafef3ec605e Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Mon, 20 May 2019 08:37:41 -0400 Subject: [PATCH 070/146] add renderer param in appnexus adapter request (#3836) --- modules/appnexusBidAdapter.js | 4 ++ test/spec/modules/appnexusBidAdapter_spec.js | 42 ++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 4fe5aec3f7a..a27a10d4372 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -538,6 +538,10 @@ function bidToTag(bid) { .forEach(param => tag.video[param] = bid.params.video[param]); } + if (bid.renderer) { + tag.video = Object.assign({}, tag.video, {custom_renderer_present: true}); + } + if ( (utils.isEmpty(bid.mediaType) && utils.isEmpty(bid.mediaTypes)) || (bid.mediaType === BANNER || (bid.mediaTypes && bid.mediaTypes[BANNER])) diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index a800d4b0f93..312201e347d 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -150,6 +150,48 @@ describe('AppNexusAdapter', function () { }); }); + it('should add video property when adUnit includes a renderer', function () { + const videoData = { + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mp4'] + } + }, + params: { + placementId: '10433394', + video: { + skippable: true, + playback_method: ['auto_play_sound_off'] + } + } + }; + + let bidRequest1 = deepClone(bidRequests[0]); + bidRequest1 = Object.assign({}, bidRequest1, videoData, { + renderer: { + url: 'http://test.renderer.url', + render: function () {} + } + }); + + let bidRequest2 = deepClone(bidRequests[0]); + bidRequest2.adUnitCode = 'adUnit_code_2'; + bidRequest2 = Object.assign({}, bidRequest2, videoData); + + const request = spec.buildRequests([bidRequest1, bidRequest2]); + const payload = JSON.parse(request.data); + expect(payload.tags[0].video).to.deep.equal({ + skippable: true, + playback_method: ['auto_play_sound_off'], + custom_renderer_present: true + }); + expect(payload.tags[1].video).to.deep.equal({ + skippable: true, + playback_method: ['auto_play_sound_off'] + }); + }); + it('should attach valid user params to the tag', function () { let bidRequest = Object.assign({}, bidRequests[0], From 45b519a9b589376f8f496789bf94b46600500ee4 Mon Sep 17 00:00:00 2001 From: Alexander Pykhteyev Date: Mon, 20 May 2019 15:51:37 +0300 Subject: [PATCH 071/146] Project Limelight bidder adapter (#3835) * Add Project Limelight adapter * Change to relative paths --- modules/projectLimeLightBidAdapter.js | 125 +++++++++++++ modules/projectLimeLightBidAdapter.md | 55 ++++++ .../projectLimeLightBidAdapter_spec.js | 170 ++++++++++++++++++ 3 files changed, 350 insertions(+) create mode 100644 modules/projectLimeLightBidAdapter.js create mode 100644 modules/projectLimeLightBidAdapter.md create mode 100644 test/spec/modules/projectLimeLightBidAdapter_spec.js diff --git a/modules/projectLimeLightBidAdapter.js b/modules/projectLimeLightBidAdapter.js new file mode 100644 index 00000000000..b0de181315e --- /dev/null +++ b/modules/projectLimeLightBidAdapter.js @@ -0,0 +1,125 @@ +import { registerBidder } from '../src/adapters/bidderFactory'; +import { BANNER, VIDEO } from '../src/mediaTypes'; +import {ajax} from '../src/ajax'; +import * as utils from '../src/utils'; + +const BIDDER_CODE = 'project-limelight'; +const URL = '//ads.project-limelight.com/hb'; + +/** + * Determines whether or not the given bid response is valid. + * + * @param {object} bid The bid to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ +function isBidResponseValid(bid) { + if (!bid.requestId || !bid.cpm || !bid.creativeId || !bid.ttl || !bid.currency) { + return false; + } + switch (bid.mediaType) { + case BANNER: + return Boolean(bid.width && bid.height && bid.ad); + case VIDEO: + return Boolean(bid.vastXml || bid.vastUrl); + } + return false; +} + +function extractBidSizes(bid) { + const bidSizes = []; + + bid.sizes.forEach(size => { + bidSizes.push({ + width: size[0], + height: size[1] + }); + }); + + return bidSizes; +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO], + + /** + * Determines whether or not the given bid request is valid. + * + * @param {object} bid The bid to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: (bid) => { + return Boolean(bid.bidId && bid.params); + }, + + /** + * Make a server request from the list of BidRequests. + * + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: (validBidRequests, bidderRequest) => { + let winTop; + try { + winTop = window.top; + winTop.location.toString(); + } catch (e) { + utils.logMessage(e); + winTop = window; + }; + const placements = []; + const request = { + 'secure': (location.protocol === 'https:'), + 'deviceWidth': winTop.screen.width, + 'deviceHeight': winTop.screen.height, + 'adUnits': placements + }; + for (let i = 0; i < validBidRequests.length; i++) { + const bid = validBidRequests[i]; + const params = bid.params; + placements.push({ + id: params.adUnitId, + bidId: bid.bidId, + transactionId: bid.transactionId, + sizes: extractBidSizes(bid), + type: params.adUnitType.toUpperCase() + }); + } + return { + method: 'POST', + url: URL, + data: request + }; + }, + + onBidWon: (bid) => { + const cpm = bid.pbMg; + if (bid.nurl !== '') { + bid.nurl = bid.nurl.replace( + /\$\{AUCTION_PRICE\}/, + cpm + ); + ajax(bid.nurl, null); + }; + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: (bidResponses) => { + const res = []; + const bidResponsesBody = bidResponses.body; + const len = bidResponsesBody.length; + for (let i = 0; i < len; i++) { + const bid = bidResponsesBody[i]; + if (isBidResponseValid(bid)) { + res.push(bid); + } + } + return res; + }, +}; + +registerBidder(spec); diff --git a/modules/projectLimeLightBidAdapter.md b/modules/projectLimeLightBidAdapter.md new file mode 100644 index 00000000000..71621983b89 --- /dev/null +++ b/modules/projectLimeLightBidAdapter.md @@ -0,0 +1,55 @@ +# Overview + +``` +Module Name: Project LimeLight SSP Adapter +Module Type: Bidder Adapter +Maintainer: engineering@project-limelight.com +``` + +# Description + +Module that connects to Project Limelight SSP demand sources + +# Test Parameters for banner +``` + var adUnits = [{ + code: 'placementCode', + sizes: [[300, 250]], + bids: [{ + bidder: 'project-limelight', + params: { + adUnitId: 0, + adUnitType: 'banner' + } + }] + } + ]; +``` + +# Test Parameters for video +``` +var videoAdUnit = [{ + code: 'video1', + sizes: [[300, 250]], + bids: [{ + bidder: 'project-limelight', + params: { + adUnitId: 0, + adUnitType: 'video' + } + }] + }]; +``` + +# Configuration + +The Project Limelight Bid Adapter expects Prebid Cache(for video) to be enabled so that we can store and retrieve a single vastXml. + +``` +pbjs.setConfig({ + usePrebidCache: true, + cache: { + url: 'https://prebid.adnxs.com/pbc/v1/cache' + } +}); +``` diff --git a/test/spec/modules/projectLimeLightBidAdapter_spec.js b/test/spec/modules/projectLimeLightBidAdapter_spec.js new file mode 100644 index 00000000000..434b3fbccb5 --- /dev/null +++ b/test/spec/modules/projectLimeLightBidAdapter_spec.js @@ -0,0 +1,170 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/projectLimeLightBidAdapter'; + +describe('ProjectLimeLightAdapter', function () { + let bid = { + bidId: '2dd581a2b6281d', + bidder: 'project-limelight', + bidderRequestId: '145e1d6a7837c9', + params: { + adUnitId: 123, + adUnitType: 'banner' + }, + placementCode: 'placement_0', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + sizes: [[300, 250]], + transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' + }; + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests([bid]); + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('//ads.project-limelight.com/hb'); + }); + it('Returns valid data if array of bids is valid', function () { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'secure', 'adUnits'); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.secure).to.be.a('boolean'); + let adUnits = data['adUnits']; + for (let i = 0; i < adUnits.length; i++) { + let adUnit = adUnits[i]; + expect(adUnit).to.have.all.keys('id', 'bidId', 'type', 'sizes', 'transactionId'); + expect(adUnit.id).to.be.a('number'); + expect(adUnit.bidId).to.be.a('string'); + expect(adUnit.type).to.be.a('string'); + expect(adUnit.transactionId).to.be.a('string'); + expect(adUnit.sizes).to.be.an('array'); + } + }); + it('Returns empty data if no valid requests are passed', function () { + serverRequest = spec.buildRequests([]); + let data = serverRequest.data; + expect(data.adUnits).to.be.an('array').that.is.empty; + }); + }); + describe('interpretBannerResponse', function () { + let resObject = { + body: [ { + requestId: '123', + mediaType: 'banner', + cpm: 0.3, + width: 320, + height: 50, + ad: '

    Hello ad

    ', + ttl: 1000, + creativeId: '123asd', + netRevenue: true, + currency: 'USD' + } ] + }; + let serverResponses = spec.interpretResponse(resObject); + it('Returns an array of valid server responses if response object is valid', function () { + expect(serverResponses).to.be.an('array').that.is.not.empty; + for (let i = 0; i < serverResponses.length; i++) { + let dataItem = serverResponses[i]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'mediaType'); + expect(dataItem.requestId).to.be.a('string'); + expect(dataItem.cpm).to.be.a('number'); + expect(dataItem.width).to.be.a('number'); + expect(dataItem.height).to.be.a('number'); + expect(dataItem.ad).to.be.a('string'); + expect(dataItem.ttl).to.be.a('number'); + expect(dataItem.creativeId).to.be.a('string'); + expect(dataItem.netRevenue).to.be.a('boolean'); + expect(dataItem.currency).to.be.a('string'); + expect(dataItem.mediaType).to.be.a('string'); + } + it('Returns an empty array if invalid response is passed', function () { + serverResponses = spec.interpretResponse('invalid_response'); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + }); + describe('interpretVideoResponse', function () { + let resObject = { + body: [ { + requestId: '123', + mediaType: 'video', + cpm: 0.3, + width: 320, + height: 50, + vastXml: '', + ttl: 1000, + creativeId: '123asd', + netRevenue: true, + currency: 'USD' + } ] + }; + let serverResponses = spec.interpretResponse(resObject); + it('Returns an array of valid server responses if response object is valid', function () { + expect(serverResponses).to.be.an('array').that.is.not.empty; + for (let i = 0; i < serverResponses.length; i++) { + let dataItem = serverResponses[i]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'vastXml', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'mediaType'); + expect(dataItem.requestId).to.be.a('string'); + expect(dataItem.cpm).to.be.a('number'); + expect(dataItem.width).to.be.a('number'); + expect(dataItem.height).to.be.a('number'); + expect(dataItem.vastXml).to.be.a('string'); + expect(dataItem.ttl).to.be.a('number'); + expect(dataItem.creativeId).to.be.a('string'); + expect(dataItem.netRevenue).to.be.a('boolean'); + expect(dataItem.currency).to.be.a('string'); + expect(dataItem.mediaType).to.be.a('string'); + } + it('Returns an empty array if invalid response is passed', function () { + serverResponses = spec.interpretResponse('invalid_response'); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + }); + describe('isBidRequestValid', function() { + let bid = { + bidId: '2dd581a2b6281d', + bidder: 'project-limelight', + bidderRequestId: '145e1d6a7837c9', + params: { + adUnitId: 123, + adUnitType: 'banner' + }, + placementCode: 'placement_0', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + sizes: [[300, 250]], + transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' + }; + + it('should return true when required params found', function() { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function() { + let bidFailed = { + bidder: 'project-limelight', + bidderRequestId: '145e1d6a7837c9', + params: { + adUnitId: 123, + adUnitType: 'banner' + }, + placementCode: 'placement_0', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + sizes: [[300, 250]], + transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' + }; + expect(spec.isBidRequestValid(bidFailed)).to.equal(false); + }); + }); +}); From 006eecc004a30c82b678ffd9d9b5b84dd009da63 Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Mon, 20 May 2019 14:12:38 -0700 Subject: [PATCH 072/146] PubMatic adapter support to read TTD Id from UserId module (#3834) * in-dev * added unit test cases * adding isStr check on userId.tdid * review suggestion --- modules/pubmaticBidAdapter.js | 19 ++++-- test/spec/modules/pubmaticBidAdapter_spec.js | 70 ++++++++++++++++++++ 2 files changed, 83 insertions(+), 6 deletions(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index c791acd9485..1f9ea06cad2 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -601,13 +601,20 @@ function _handleDigitrustId(eids) { } } -function _handleTTDId(eids) { +function _handleTTDId(eids, validBidRequests) { + let ttdId = null; let adsrvrOrgId = config.getConfig('adsrvrOrgId'); - if (adsrvrOrgId && utils.isStr(adsrvrOrgId.TDID)) { + if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.tdid'))) { + ttdId = validBidRequests[0].userId.tdid; + } else if (adsrvrOrgId && utils.isStr(adsrvrOrgId.TDID)) { + ttdId = adsrvrOrgId.TDID; + } + + if (ttdId !== null) { eids.push({ 'source': 'adserver.org', 'uids': [{ - 'id': adsrvrOrgId.TDID, + 'id': ttdId, 'atype': 1, 'ext': { 'rtiPartner': 'TDID' @@ -617,10 +624,10 @@ function _handleTTDId(eids) { } } -function _handleEids(payload) { +function _handleEids(payload, validBidRequests) { let eids = []; _handleDigitrustId(eids); - _handleTTDId(eids); + _handleTTDId(eids, validBidRequests); if (eids.length > 0) { payload.user.eids = eids; } @@ -877,7 +884,7 @@ export const spec = { } } - _handleEids(payload); + _handleEids(payload, validBidRequests); _blockedIabCategoriesValidation(payload, blockedIabCategories); return { method: 'POST', diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index b25fd22f822..6126c0f9fd8 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -1235,6 +1235,76 @@ describe('PubMatic adapter', function () { }); }); + describe('AdsrvrOrgId from userId module', function() { + let sandbox; + beforeEach(() => { + sandbox = sinon.sandbox.create(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('Request should have AdsrvrOrgId config params', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.tdid = 'TTD_ID_FROM_USER_ID_MODULE'; + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'adserver.org', + 'uids': [{ + 'id': 'TTD_ID_FROM_USER_ID_MODULE', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + }] + }]); + }); + + it('Request should have adsrvrOrgId from UserId Module if config and userId module both have TTD ID', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID': 'TTD_ID_FROM_CONFIG', + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2018-10-01T07:05:40' + } + }; + return config[key]; + }); + bidRequests[0].userId = {}; + bidRequests[0].userId.tdid = 'TTD_ID_FROM_USER_ID_MODULE'; + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'adserver.org', + 'uids': [{ + 'id': 'TTD_ID_FROM_USER_ID_MODULE', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + }] + }]); + }); + + it('Request should NOT have adsrvrOrgId params if userId is NOT object', function() { + 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 params if userId.tdid is NOT string', function() { + bidRequests[0].userId = { + tdid: 1234 + }; + 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; From d3faa6ac65381bb29375c105a11ba73a999c64d1 Mon Sep 17 00:00:00 2001 From: hdeodhar <35999856+hdeodhar@users.noreply.github.com> Date: Mon, 20 May 2019 22:15:34 +0100 Subject: [PATCH 073/146] Added 240x400 size (#3809) --- modules/rubiconBidAdapter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index eaa5b322811..aeb6418eea8 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -25,6 +25,7 @@ var sizeMap = { 14: '250x250', 15: '300x250', 16: '336x280', + 17: '240x400', 19: '300x100', 31: '980x120', 32: '250x360', From 7846560af7f8f982aed1d48e64d68b8b9b4ae28c Mon Sep 17 00:00:00 2001 From: CPMStar Date: Tue, 21 May 2019 08:35:26 -0700 Subject: [PATCH 074/146] CPMStar Bid Adapter (#3820) * Added CPMStar Bid Adapter * Updated getPlayerSize for cpmstarBidAdapter * Improved cpmstarBidAdapter code coverage * updated test spec, removed empty functions, made imports relative, added warnings to erroneous server responses, and removed the default value for ad in bid response. * added test video ad unit --- modules/cpmstarBidAdapter.js | 118 +++++++++++++++ modules/cpmstarBidAdapter.md | 50 +++++++ test/spec/modules/cpmstarBidAdapter_spec.js | 158 ++++++++++++++++++++ 3 files changed, 326 insertions(+) create mode 100755 modules/cpmstarBidAdapter.js create mode 100755 modules/cpmstarBidAdapter.md create mode 100755 test/spec/modules/cpmstarBidAdapter_spec.js diff --git a/modules/cpmstarBidAdapter.js b/modules/cpmstarBidAdapter.js new file mode 100755 index 00000000000..84b76cbbc35 --- /dev/null +++ b/modules/cpmstarBidAdapter.js @@ -0,0 +1,118 @@ + +import * as utils from '../src/utils'; +import { registerBidder } from '../src/adapters/bidderFactory'; +import {VIDEO, BANNER} from '../src/mediaTypes'; + +const BIDDER_CODE = 'cpmstar'; + +const ENDPOINT_DEV = '//dev.server.cpmstar.com/view.aspx'; +const ENDPOINT_STAGING = '//staging.server.cpmstar.com/view.aspx'; +const ENDPOINT_PRODUCTION = '//server.cpmstar.com/view.aspx'; + +const DEFAULT_TTL = 300; +const DEFAULT_CURRENCY = 'USD'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO], + pageID: Math.floor(Math.random() * 10e6), + + getMediaType: function(bidRequest) { + if (bidRequest == null) return BANNER; + return !utils.deepAccess(bidRequest, 'mediaTypes.video') ? BANNER : VIDEO; + }, + + getPlayerSize: function(bidRequest) { + var playerSize = utils.deepAccess(bidRequest, 'mediaTypes.video.playerSize'); + if (playerSize == null) return [640, 440]; + if (playerSize[0] != null) playerSize = playerSize[0]; + if (playerSize == null || playerSize[0] == null || playerSize[1] == null) return [640, 440]; + return playerSize; + }, + + isBidRequestValid: function (bid) { + return ((typeof bid.params.placementId === 'string') && !!bid.params.placementId.length) || (typeof bid.params.placementId === 'number'); + }, + + buildRequests: function (validBidRequests, bidderRequest) { + var requests = []; + // This reference to window.top can cause issues when loaded in an iframe if not protected with a try/catch. + + for (var i = 0; i < validBidRequests.length; i++) { + var bidRequest = validBidRequests[i]; + var referer = encodeURIComponent(bidderRequest.refererInfo.referer); + var e = utils.getBidIdParameter('endpoint', bidRequest.params); + var ENDPOINT = e == 'dev' ? ENDPOINT_DEV : e == 'staging' ? ENDPOINT_STAGING : ENDPOINT_PRODUCTION; + var mediaType = spec.getMediaType(bidRequest); + var playerSize = spec.getPlayerSize(bidRequest); + var videoArgs = '&fv=0' + (playerSize ? ('&w=' + playerSize[0] + '&h=' + playerSize[1]) : ''); + requests.push({ + method: 'GET', + url: ENDPOINT + '?media=' + mediaType + (mediaType == VIDEO ? videoArgs : '') + '&json=c_b&mv=1&poolid=' + utils.getBidIdParameter('placementId', bidRequest.params) + '&reachedTop=' + encodeURIComponent(bidderRequest.refererInfo.reachedTop) + '&requestid=' + bidRequest.bidId + '&referer=' + referer, + bidRequest: bidRequest, + }); + } + + return requests; + }, + + interpretResponse: function (serverResponse, request) { + var bidRequest = request.bidRequest; + var mediaType = spec.getMediaType(bidRequest); + + var bidResponses = []; + + if (!Array.isArray(serverResponse.body)) { + serverResponse.body = [serverResponse.body]; + } + + for (var i = 0; i < serverResponse.body.length; i++) { + var raw = serverResponse.body[i]; + var rawBid = raw.creatives[0]; + if (!rawBid) { + utils.logWarn('cpmstarBidAdapter: server response failed check'); + return; + } + var cpm = (parseFloat(rawBid.cpm) || 0); + + if (!cpm) { + utils.logWarn('cpmstarBidAdapter: server response failed check. Missing cpm') + return; + } + + var bidResponse = { + requestId: rawBid.requestid, + cpm: cpm, + width: rawBid.width || 0, + height: rawBid.height || 0, + currency: rawBid.currency ? rawBid.currency : DEFAULT_CURRENCY, + netRevenue: rawBid.netRevenue ? rawBid.netRevenue : true, + ttl: rawBid.ttl ? rawBid.ttl : DEFAULT_TTL, + creativeId: rawBid.creativeid || 0, + }; + + if (rawBid.hasOwnProperty('dealId')) { + bidResponse.dealId = rawBid.dealId + } + + if (mediaType == BANNER && rawBid.code) { + bidResponse.ad = rawBid.code + (rawBid.px_cr ? "\n" : ''); + } else if (mediaType == VIDEO && rawBid.creativemacros && rawBid.creativemacros.HTML5VID_VASTSTRING) { + var playerSize = spec.getPlayerSize(bidRequest); + if (playerSize != null) { + bidResponse.width = playerSize[0]; + bidResponse.height = playerSize[1]; + } + bidResponse.mediaType = VIDEO; + bidResponse.vastXml = rawBid.creativemacros.HTML5VID_VASTSTRING; + } else { + return utils.logError('bad response', rawBid); + } + + bidResponses.push(bidResponse); + } + + return bidResponses; + } +}; +registerBidder(spec); diff --git a/modules/cpmstarBidAdapter.md b/modules/cpmstarBidAdapter.md new file mode 100755 index 00000000000..7dab435b0f0 --- /dev/null +++ b/modules/cpmstarBidAdapter.md @@ -0,0 +1,50 @@ +# Overview + +``` +Module Name: Cpmstar Bidder Adapter +Module Type: Bidder Adapter +Maintainer: josh@cpmstar.com +``` + +# Description + +Module that connects to Cpmstar's demand sources + +# Test Parameters +``` +var adUnits = [ + { + code: 'banner-ad-div', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [ + { + bidder: 'cpmstar', + params: { + placementId: 81006 + } + }, + ] + }, + { + code: 'video-ad-div', + mediaTypes: { + video: { + context: 'instream', + sizes: [[640, 480]] + } + }, + bids:[ + { + bidder: 'cpmstar', + params: { + placementId: 81007 + } + } + ] + } +]; +``` \ No newline at end of file diff --git a/test/spec/modules/cpmstarBidAdapter_spec.js b/test/spec/modules/cpmstarBidAdapter_spec.js new file mode 100755 index 00000000000..3dd06a484d9 --- /dev/null +++ b/test/spec/modules/cpmstarBidAdapter_spec.js @@ -0,0 +1,158 @@ +import { expect } from 'chai'; +import { spec } from 'modules/cpmstarBidAdapter'; +import { deepClone } from 'src/utils'; + +describe('Cpmstar Bid Adapter', function () { + describe('isBidRequestValid', function () { + it('should return true since the bid is valid', + function () { + var bid = { params: { placementId: 123456 } }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }) + + it('should return false since the bid is invalid', function () { + var bid = { params: { placementId: '' } }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }) + + it('should return a valid player size', function() { + var bid = { mediaTypes: { + video: { + playerSize: [[960, 540]] + } + }} + expect(spec.getPlayerSize(bid)[0]).to.equal(960); + expect(spec.getPlayerSize(bid)[1]).to.equal(540); + }) + + it('should return a default player size', function() { + var bid = { mediaTypes: { + video: { + playerSize: null + } + }} + expect(spec.getPlayerSize(bid)[0]).to.equal(640); + expect(spec.getPlayerSize(bid)[1]).to.equal(440); + }) + }); + + describe('buildRequests', function () { + const valid_bid_requests = [{ + 'bidder': 'cpmstar', + 'params': { + 'placementId': '57' + }, + 'sizes': [[300, 250]], + 'bidId': 'bidId' + }]; + + const bidderRequest = { + refererInfo: { + referer: 'referer', + reachedTop: false, + } + + }; + + it('should produce a valid production request', function () { + var requests = spec.buildRequests(valid_bid_requests, bidderRequest); + expect(requests[0]).to.have.property('method'); + expect(requests[0]).to.have.property('url'); + expect(requests[0]).to.have.property('bidRequest'); + expect(requests[0].url).to.include('//server.cpmstar.com/view.aspx'); + }); + it('should produce a valid staging request', function () { + var stgReq = deepClone(valid_bid_requests); + stgReq[0].params.endpoint = 'staging'; + var requests = spec.buildRequests(stgReq, bidderRequest); + expect(requests[0]).to.have.property('method'); + expect(requests[0]).to.have.property('url'); + expect(requests[0]).to.have.property('bidRequest'); + expect(requests[0].url).to.include('//staging.server.cpmstar.com/view.aspx'); + }); + it('should produce a valid dev request', function () { + var devReq = deepClone(valid_bid_requests); + devReq[0].params.endpoint = 'dev'; + var requests = spec.buildRequests(devReq, bidderRequest); + expect(requests[0]).to.have.property('method'); + expect(requests[0]).to.have.property('url'); + expect(requests[0]).to.have.property('bidRequest'); + expect(requests[0].url).to.include('//dev.server.cpmstar.com/view.aspx'); + }); + }) + + describe('interpretResponse', function () { + const request = { + bidRequest: { + mediaType: 'BANNER' + } + }; + const serverResponse = { + body: [{ + creatives: [{ + cpm: 1, + width: 0, + height: 0, + currency: 'USD', + netRevenue: true, + ttl: 1, + creativeid: '1234', + requestid: '11123', + code: 'no idea', + media: 'banner', + } + ], + }] + }; + + it('should return a valid bidresponse array', function () { + var r = spec.interpretResponse(serverResponse, request) + var c = serverResponse.body[0].creatives[0]; + expect(r[0].length).to.not.equal(0); + expect(r[0].requestId).equal(c.requestid); + expect(r[0].creativeId).equal(c.creativeid); + expect(r[0].cpm).equal(c.cpm); + expect(r[0].width).equal(c.width); + expect(r[0].height).equal(c.height); + expect(r[0].currency).equal(c.currency); + expect(r[0].netRevenue).equal(c.netRevenue); + expect(r[0].ttl).equal(c.ttl); + expect(r[0].ad).equal(c.code); + }); + + it('should return a valid bidresponse array from a non-array-body', function () { + var r = spec.interpretResponse({ body: serverResponse.body[0] }, request) + var c = serverResponse.body[0].creatives[0]; + expect(r[0].length).to.not.equal(0); + expect(r[0].requestId).equal(c.requestid); + expect(r[0].creativeId).equal(c.creativeid); + expect(r[0].cpm).equal(c.cpm); + expect(r[0].width).equal(c.width); + expect(r[0].height).equal(c.height); + expect(r[0].currency).equal(c.currency); + expect(r[0].netRevenue).equal(c.netRevenue); + expect(r[0].ttl).equal(c.ttl); + expect(r[0].ad).equal(c.code); + }); + + it('should return undefined due to an invalid cpm value', function () { + var badServer = deepClone(serverResponse); + badServer.body[0].creatives[0].cpm = 0; + var c = spec.interpretResponse(badServer, request); + expect(c).to.be.undefined; + }); + + it('should return undefined due to a bad response', function () { + var badServer = deepClone(serverResponse); + badServer.body[0].creatives[0].code = null; + var c = spec.interpretResponse(badServer, request); + expect(c).to.be.undefined; + }); + + it('should return a valid response with a dealId', function () { + var dealServer = deepClone(serverResponse); + dealServer.body[0].creatives[0].dealId = 'deal'; + expect(spec.interpretResponse(dealServer, request)[0].dealId).to.equal('deal'); + }); + }); +}); From d55b253dd52fb7f4708e10da60393de55d6c8b6f Mon Sep 17 00:00:00 2001 From: Isaac Dettman Date: Tue, 21 May 2019 12:08:39 -0700 Subject: [PATCH 075/146] support external userId sub-modules (#3831) --- modules/pubCommonIdSystem.js | 42 ++++ modules/unifiedIdSystem.js | 55 ++++ modules/userId.js | 419 +++++++++++++++---------------- modules/userId.md | 4 +- test/spec/modules/userId_spec.js | 226 ++++++++++++----- 5 files changed, 464 insertions(+), 282 deletions(-) create mode 100644 modules/pubCommonIdSystem.js create mode 100644 modules/unifiedIdSystem.js diff --git a/modules/pubCommonIdSystem.js b/modules/pubCommonIdSystem.js new file mode 100644 index 00000000000..39d1feea0ad --- /dev/null +++ b/modules/pubCommonIdSystem.js @@ -0,0 +1,42 @@ +/** + * This module adds PubCommonId to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/pubCommonIdSystem + * @requires module:modules/userId + */ + +import * as utils from '../src/utils.js' + +/** @type {Submodule} */ +export const pubCommonIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: 'pubCommonId', + /** + * decode the stored id value for passing to bid requests + * @function + * @param {string} value + * @returns {{pubcid:string}} + */ + decode(value) { + return { 'pubcid': value } + }, + /** + * performs action to obtain id + * @function + * @returns {string} + */ + getId() { + // If the page includes its own pubcid object, then use that instead. + let pubcid; + try { + if (typeof window['PublisherCommonId'] === 'object') { + pubcid = window['PublisherCommonId'].getId(); + } + } catch (e) {} + // check pubcid and return if valid was otherwise create a new id + return (pubcid) || utils.generateUUID(); + } +}; diff --git a/modules/unifiedIdSystem.js b/modules/unifiedIdSystem.js new file mode 100644 index 00000000000..6b67b7bf5f1 --- /dev/null +++ b/modules/unifiedIdSystem.js @@ -0,0 +1,55 @@ +/** + * This module adds UnifiedId to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/unifiedIdSystem + * @requires module:modules/userId + */ + +import * as utils from '../src/utils.js' +import {ajax} from '../src/ajax.js'; + +/** @type {Submodule} */ +export const unifiedIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: 'unifiedId', + /** + * decode the stored id value for passing to bid requests + * @function + * @param {{TDID:string}} value + * @returns {{tdid:Object}} + */ + decode(value) { + return (value && typeof value['TDID'] === 'string') ? { 'tdid': value['TDID'] } : undefined; + }, + /** + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleParams} [configParams] + * @returns {function(callback:function)} + */ + getId(configParams) { + if (!configParams || (typeof configParams.partner !== 'string' && typeof configParams.url !== 'string')) { + utils.logError('User ID - unifiedId submodule requires either partner or url to be defined'); + return; + } + // use protocol relative urls for http or https + const url = configParams.url || `//match.adsrvr.org/track/rid?ttd_pid=${configParams.partner}&fmt=json`; + + return function (callback) { + ajax(url, response => { + let responseObj; + if (response) { + try { + responseObj = JSON.parse(response); + } catch (error) { + utils.logError(error); + } + } + callback(responseObj); + }, undefined, { method: 'GET' }); + } + } +}; diff --git a/modules/userId.js b/modules/userId.js index bddada7ffbc..ae06dfc4027 100644 --- a/modules/userId.js +++ b/modules/userId.js @@ -1,138 +1,122 @@ /** * This module adds User ID support to prebid.js + * @module modules/userId */ -import {ajax} from '../src/ajax.js'; -import {config} from '../src/config.js'; -import events from '../src/events.js'; -import * as utils from '../src/utils.js'; -import find from 'core-js/library/fn/array/find'; -import {gdprDataHandler} from '../src/adapterManager.js'; -const CONSTANTS = require('../src/constants.json'); +/** + * @interface Submodule + */ /** - * @typedef {Object} SubmoduleConfig - * @property {string} name - the User ID submodule name - * @property {SubmoduleStorage} storage - browser storage config - * @property {SubmoduleParams} params - params config for use by the submodule.getId function - * @property {Object} value - all object properties will be appended to the User ID bid data + * @function + * @summary performs action to obtain id and return a value in the callback's response argument + * @name Submodule#getId + * @param {SubmoduleParams} configParams + * @param {ConsentData} consentData + * @return {(Object|function} id data or a callback, the callback is called on the auction end event */ /** - * @typedef {Object} SubmoduleStorage - * @property {string} type - browser storage type (html5 or cookie) - * @property {string} name - key name to use when saving/reading to local storage or cookies - * @property {number} expires - time to live for browser cookie + * @function + * @summary decode a stored value for passing to bid requests + * @name Submodule#decode + * @param {Object|string} value + * @return {(Object|undefined} */ /** - * @typedef {Object} SubmoduleParams - * @property {string} partner - partner url param value - * @property {string} url - webservice request url used to load Id data + * @property + * @summary used to link submodule with config + * @name Submodule#name + * @type {string} */ /** - * @typedef {Object} Submodule - * @property {string} name - submodule and config have matching name prop - * @property {decode} decode - decode a stored value for passing to bid requests - * @property {getId} getId - performs action to obtain id and return a value in the callback's response argument + * @typedef {Object} SubmoduleConfig + * @property {string} name - the User ID submodule name (used to link submodule with config) + * @property {(SubmoduleStorage|undefined)} storage - browser storage config + * @property {(SubmoduleParams|undefined)} params - params config for use by the submodule.getId function + * @property {(Object|undefined)} value - if not empty, this value is added to bid requests for access in adapters */ /** - * @callback getId - * @param {SubmoduleParams} [submoduleConfigParams] - * @param {Object} [consentData] - * @returns {(Function|Object|string)} + * @typedef {Object} SubmoduleStorage + * @property {string} type - browser storage type (html5 or cookie) + * @property {string} name - key name to use when saving/reading to local storage or cookies + * @property {(number|undefined)} expires - time to live for browser cookie */ /** - * @callback decode - * @param {Object|string} idData - * @returns {Object} + * @typedef {Object} SubmoduleParams + * @property {(string|undefined)} partner - partner url param value + * @property {(string|undefined)} url - webservice request url used to load Id data */ /** * @typedef {Object} SubmoduleContainer * @property {Submodule} submodule - * @property {SubmoduleConfig} submoduleConfig - * @property {Object} idObj - decoded User ID data that will be appended to bids - * @property {function} callback + * @property {SubmoduleConfig} config + * @property {(Object|undefined)} idObj - cache decoded id value (this is copied to every adUnit bid) + * @property {(function|undefined)} callback - holds reference to submodule.getId() result if it returned a function. Will be set to undefined after callback executes */ +/** + * @typedef {Object} ConsentData + * @property {(string|undefined)} consentString + * @property {(Object|undefined)} vendorData + * @property {(boolean|undefined)} gdprApplies + */ + +import find from 'core-js/library/fn/array/find'; +import {config} from '../src/config.js'; +import events from '../src/events.js'; +import * as utils from '../src/utils.js'; +import {getGlobal} from '../src/prebidGlobal.js'; +import {gdprDataHandler} from '../src/adapterManager.js'; +import {unifiedIdSubmodule} from './unifiedIdSystem.js'; +import {pubCommonIdSubmodule} from './pubCommonIdSystem.js'; +import CONSTANTS from '../src/constants.json'; + const MODULE_NAME = 'User ID'; const COOKIE = 'cookie'; const LOCAL_STORAGE = 'html5'; const DEFAULT_SYNC_DELAY = 500; -// @type {number} delay after auction to make webrequests for id data -export let syncDelay; +/** @type {string[]} */ +let validStorageTypes = []; -// @type {SubmoduleContainer[]} -export let submodules; +/** @type {boolean} */ +let addedUserIdHook = false; -// @type {SubmoduleContainer[]} +/** @type {SubmoduleContainer[]} */ +let submodules = []; + +/** @type {SubmoduleContainer[]} */ let initializedSubmodules; -// @type {Submodule} -export const unifiedIdSubmodule = { - name: 'unifiedId', - decode(value) { - return (value && typeof value['TDID'] === 'string') ? { 'tdid': value['TDID'] } : undefined; - }, - getId(submoduleConfigParams, consentData) { - if (!submoduleConfigParams || (typeof submoduleConfigParams.partner !== 'string' && typeof submoduleConfigParams.url !== 'string')) { - utils.logError(`${MODULE_NAME} - unifiedId submodule requires either partner or url to be defined`); - return; - } - const url = submoduleConfigParams.url || `http://match.adsrvr.org/track/rid?ttd_pid=${submoduleConfigParams.partner}&fmt=json`; - - return function (callback) { - ajax(url, response => { - let responseObj; - if (response) { - try { - responseObj = JSON.parse(response); - } catch (error) { - utils.logError(error); - } - } - callback(responseObj); - }, undefined, { method: 'GET' }); - } - } -}; - -// @type {Submodule} -export const pubCommonIdSubmodule = { - name: 'pubCommonId', - decode(value) { - return { - 'pubcid': value - } - }, - getId() { - // If the page includes its own pubcid object, then use that instead. - let pubcid; - try { - if (typeof window['PublisherCommonId'] === 'object') { - pubcid = window['PublisherCommonId'].getId(); - } - } catch (e) {} - // check pubcid and return if valid was otherwise create a new id - return (pubcid) || utils.generateUUID(); - } -}; +/** @type {SubmoduleConfig[]} */ +let configRegistry = []; + +/** @type {Submodule[]} */ +let submoduleRegistry = []; + +/** @type {(number|undefined)} */ +export let syncDelay; + +/** @param {Submodule[]} submodules */ +export function setSubmoduleRegistry(submodules) { + submoduleRegistry = submodules; +} /** * @param {SubmoduleStorage} storage * @param {string} value - * @param {number|string} expires + * @param {(number|string)} expires */ -export function setStoredValue(storage, value, expires) { +function setStoredValue(storage, value, expires) { try { - const valueStr = (typeof value === 'object') ? JSON.stringify(value) : value; + const valueStr = utils.isPlainObject(value) ? JSON.stringify(value) : value; const expiresStr = (new Date(Date.now() + (expires * (60 * 60 * 24 * 1000)))).toUTCString(); - if (storage.type === COOKIE) { utils.setCookie(storage.name, valueStr, expiresStr); } else if (storage.type === LOCAL_STORAGE) { @@ -148,7 +132,7 @@ export function setStoredValue(storage, value, expires) { * @param {SubmoduleStorage} storage * @returns {string} */ -export function getStoredValue(storage) { +function getStoredValue(storage) { let storedValue; try { if (storage.type === COOKIE) { @@ -164,8 +148,7 @@ export function getStoredValue(storage) { } } } - // we support storing either a string or a stringified object, - // so we test if the string contains an stringified object, and if so convert to an object + // support storing a string or a stringified object if (typeof storedValue === 'string' && storedValue.charAt(0) === '{') { storedValue = JSON.parse(storedValue); } @@ -177,10 +160,10 @@ export function getStoredValue(storage) { /** * test if consent module is present, applies, and is valid for local storage or cookies (purpose 1) - * @param {Object} consentData + * @param {ConsentData} consentData * @returns {boolean} */ -export function hasGDPRConsent(consentData) { +function hasGDPRConsent(consentData) { if (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) { if (!consentData.consentString) { return false; @@ -193,18 +176,16 @@ export function hasGDPRConsent(consentData) { } /** - * @param {Object[]} submodules + * @param {SubmoduleContainer[]} submodules */ -export function processSubmoduleCallbacks(submodules) { +function processSubmoduleCallbacks(submodules) { submodules.forEach(function(submodule) { - submodule.callback(function callbackCompleted (idObj) { + submodule.callback(function callbackCompleted(idObj) { // clear callback, this prop is used to test if all submodule callbacks are complete below submodule.callback = undefined; - // if valid, id data should be saved to cookie/html storage if (idObj) { setStoredValue(submodule.config.storage, idObj, submodule.config.storage.expires); - // cache decoded value (this is copied to every adUnit bid) submodule.idObj = submodule.submodule.decode(idObj); } else { @@ -215,25 +196,26 @@ export function processSubmoduleCallbacks(submodules) { } /** - * @param {Object[]} adUnits - * @param {Object[]} submodules + * @param {AdUnit[]} adUnits + * @param {SubmoduleContainer[]} submodules */ -export function addIdDataToAdUnitBids(adUnits, submodules) { - const submodulesWithIds = submodules.filter(item => typeof item.idObj === 'object' && item.idObj !== null); - if (submodulesWithIds.length) { - if (adUnits) { - adUnits.forEach(adUnit => { - adUnit.bids.forEach(bid => { - // append the User ID property to bid - bid.userId = submodulesWithIds.reduce((carry, item) => { - Object.keys(item.idObj).forEach(key => { - carry[key] = item.idObj[key]; - }); - return carry; - }, {}); - }); +function addIdDataToAdUnitBids(adUnits, submodules) { + if ([adUnits, submodules].some(i => !Array.isArray(i) || !i.length)) { + return; + } + const combinedSubmoduleIds = submodules.filter(i => utils.isPlainObject(i.idObj) && Object.keys(i.idObj).length).reduce((carry, i) => { + Object.keys(i.idObj).forEach(key => { + carry[key] = i.idObj[key]; + }); + return carry; + }, {}); + if (Object.keys(combinedSubmoduleIds).length) { + adUnits.forEach(adUnit => { + adUnit.bids.forEach(bid => { + // create a User ID object on the bid, + bid.userId = combinedSubmoduleIds; }); - } + }); } } @@ -243,7 +225,7 @@ export function addIdDataToAdUnitBids(adUnits, submodules) { * The two main actions handled by the hook are: * 1. check gdpr consentData and handle submodule initialization. * 2. append user id data (loaded from cookied/html or from the getId method) to bids to be accessed in adapters. - * @param {object} reqBidsConfigObj required; This is the same param that's used in pbjs.requestBids. + * @param {Object} reqBidsConfigObj required; This is the same param that's used in pbjs.requestBids. * @param {function} fn required; The next function in the chain, used by hook.js */ export function requestBidsHook(fn, reqBidsConfigObj) { @@ -252,7 +234,7 @@ export function requestBidsHook(fn, reqBidsConfigObj) { initializedSubmodules = initSubmodules(submodules, gdprDataHandler.getConsentData()); if (initializedSubmodules.length) { // list of sumodules that have callbacks that need to be executed - const submodulesWithCallbacks = initializedSubmodules.filter(item => typeof item.callback === 'function'); + const submodulesWithCallbacks = initializedSubmodules.filter(item => utils.isFn(item.callback)); if (submodulesWithCallbacks.length) { // wait for auction complete before processing submodule callbacks @@ -273,53 +255,51 @@ export function requestBidsHook(fn, reqBidsConfigObj) { } // pass available user id data to bid adapters - addIdDataToAdUnitBids(reqBidsConfigObj.adUnits || $$PREBID_GLOBAL$$.adUnits, initializedSubmodules); + addIdDataToAdUnitBids(reqBidsConfigObj.adUnits || getGlobal().adUnits, initializedSubmodules); // calling fn allows prebid to continue processing return fn.call(this, reqBidsConfigObj); } /** - * @param {Object[]} submodules - * @param {Object} consentData - * @returns {string[]} initialized submodules + * @param {SubmoduleContainer[]} submodules + * @param {ConsentData} consentData + * @returns {SubmoduleContainer[]} initialized submodules */ -export function initSubmodules(submodules, consentData) { +function initSubmodules(submodules, consentData) { // gdpr consent with purpose one is required, otherwise exit immediately if (!hasGDPRConsent(consentData)) { utils.logWarn(`${MODULE_NAME} - gdpr permission not valid for local storage or cookies, exit module`); return []; } - return submodules.reduce((carry, item) => { + return submodules.reduce((carry, submodule) => { // There are two submodule configuration types to handle: storage or value // 1. storage: retrieve user id data from cookie/html storage or with the submodule's getId method // 2. value: pass directly to bids - if (item.config && item.config.storage) { - const storedId = getStoredValue(item.config.storage); + if (submodule.config.storage) { + const storedId = getStoredValue(submodule.config.storage); if (storedId) { // cache decoded value (this is copied to every adUnit bid) - item.idObj = item.submodule.decode(storedId); + submodule.idObj = submodule.submodule.decode(storedId); } else { // getId will return user id data or a function that will load the data - const getIdResult = item.submodule.getId(item.config.params, consentData); + const getIdResult = submodule.submodule.getId(submodule.config.params, consentData); // If the getId result has a type of function, it is asynchronous and cannot be called until later if (typeof getIdResult === 'function') { - item.callback = getIdResult; + submodule.callback = getIdResult; } else { // A getId result that is not a function is assumed to be valid user id data, which should be saved to users local storage or cookies - setStoredValue(item.config.storage, getIdResult, item.config.storage.expires); - + setStoredValue(submodule.config.storage, getIdResult, submodule.config.storage.expires); // cache decoded value (this is copied to every adUnit bid) - item.idObj = item.submodule.decode(getIdResult); + submodule.idObj = submodule.submodule.decode(getIdResult); } } - } else if (item.config.value) { + } else if (submodule.config.value) { // cache decoded value (this is copied to every adUnit bid) - item.idObj = item.config.value; + submodule.idObj = submodule.config.value; } - - carry.push(item); + carry.push(submodule); return carry; }, []); } @@ -328,102 +308,117 @@ export function initSubmodules(submodules, consentData) { * list of submodule configurations with valid 'storage' or 'value' obj definitions * * storage config: contains values for storing/retrieving User ID data in browser storage * * value config: object properties that are copied to bids (without saving to storage) - * @param {SubmoduleConfig[]} submoduleConfigs - * @param {Submodule[]} enabledSubmodules + * @param {SubmoduleConfig[]} configRegistry + * @param {Submodule[]} submoduleRegistry + * @param {string[]} activeStorageTypes * @returns {SubmoduleConfig[]} */ -export function getValidSubmoduleConfigs(submoduleConfigs, enabledSubmodules) { - if (!Array.isArray(submoduleConfigs)) { +function getValidSubmoduleConfigs(configRegistry, submoduleRegistry, activeStorageTypes) { + if (!Array.isArray(configRegistry)) { return []; } - - // list of browser enabled storage types - const validStorageTypes = []; - if (utils.localStorageIsEnabled()) { - // check if optout exists in local storage (null if returned if key does not exist) - if (!localStorage.getItem('_pbjs_id_optout') && !localStorage.getItem('_pubcid_optout')) { - validStorageTypes.push(LOCAL_STORAGE); - } else { - utils.logInfo(`${MODULE_NAME} - opt-out localStorage found, exit module`); - } - } - if (utils.cookiesAreEnabled()) { - validStorageTypes.push(COOKIE); - } - - return submoduleConfigs.reduce((carry, submoduleConfig) => { + return configRegistry.reduce((carry, config) => { // every submodule config obj must contain a valid 'name' - if (!submoduleConfig || typeof submoduleConfig.name !== 'string' || !submoduleConfig.name) { + if (!config || utils.isEmptyStr(config.name)) { return carry; } - - // Validate storage config - // contains 'type' and 'name' properties with non-empty string values + // alidate storage config contains 'type' and 'name' properties with non-empty string values // 'type' must be a value currently enabled in the browser - if (submoduleConfig.storage && - typeof submoduleConfig.storage.type === 'string' && submoduleConfig.storage.type && - typeof submoduleConfig.storage.name === 'string' && submoduleConfig.storage.name && - validStorageTypes.indexOf(submoduleConfig.storage.type) !== -1) { - carry.push(submoduleConfig); - } else if (submoduleConfig.value !== null && typeof submoduleConfig.value === 'object') { - // Validate value config - // must be valid object with at least one property - carry.push(submoduleConfig); + if (config.storage && + !utils.isEmptyStr(config.storage.type) && + !utils.isEmptyStr(config.storage.name) && + activeStorageTypes.indexOf(config.storage.type) !== -1) { + carry.push(config); + } else if (utils.isPlainObject(config.value)) { + carry.push(config); } return carry; }, []); } /** - * @param config - * @param {Submodule[]} enabledSubmodules + * update submodules by validating against existing configs and storage types */ -export function init (config, enabledSubmodules) { +function updateSubmodules() { + const configs = getValidSubmoduleConfigs(configRegistry, submoduleRegistry, validStorageTypes); + if (!configs.length) { + return; + } + // do this to avoid reprocessing submodules + const addedSubmodules = submoduleRegistry.filter(i => !find(submodules, j => j.name === i.name)); + + // find submodule and the matching configuration, if found create and append a SubmoduleContainer + submodules = addedSubmodules.map(i => { + const submoduleConfig = find(configs, j => j.name === i.name); + return submoduleConfig ? { + submodule: i, + config: submoduleConfig, + callback: undefined, + idObj: undefined + } : null; + }).filter(submodule => submodule !== null); + + if (!addedUserIdHook && submodules.length) { + // priority value 40 will load after consentManagement with a priority of 50 + getGlobal().requestBids.before(requestBidsHook, 40); + utils.logInfo(`${MODULE_NAME} - usersync config updated for ${submodules.length} submodules`); + addedUserIdHook = true; + } +} + +/** + * enable submodule in User ID + * @param {Submodule} submodule + */ +export function attachIdSystem(submodule) { + if (!find(submoduleRegistry, i => i.name === submodule.name)) { + submoduleRegistry.push(submodule); + updateSubmodules(); + } +} + +/** + * test browser support for storage config types (local storage or cookie), initializes submodules but consentManagement is required, + * so a callback is added to fire after the consentManagement module. + * @param {{getConfig:function}} config + */ +export function init(config) { submodules = []; + configRegistry = []; + addedUserIdHook = false; initializedSubmodules = undefined; - // exit immediately if opt out cookie exists. _pubcid_optout is checked for compatiblility with pubCommonId module opt out - if (utils.getCookie('_pbjs_id_optout')) { + // list of browser enabled storage types + validStorageTypes = [ + utils.localStorageIsEnabled() ? LOCAL_STORAGE : null, + utils.cookiesAreEnabled() ? COOKIE : null + ].filter(i => i !== null); + + // exit immediately if opt out cookie or local storage keys exists. + if (validStorageTypes.indexOf(COOKIE) !== -1 && utils.getCookie('_pbjs_id_optout')) { utils.logInfo(`${MODULE_NAME} - opt-out cookie found, exit module`); return; } - + // _pubcid_optout is checked for compatiblility with pubCommonId + if (validStorageTypes.indexOf(LOCAL_STORAGE) !== -1 && (localStorage.getItem('_pbjs_id_optout') && localStorage.getItem('_pubcid_optout'))) { + utils.logInfo(`${MODULE_NAME} - opt-out localStorage found, exit module`); + return; + } // listen for config userSyncs to be set - config.getConfig('usersync', ({usersync}) => { - if (usersync) { - syncDelay = (typeof usersync.syncDelay !== 'undefined') ? usersync.syncDelay : DEFAULT_SYNC_DELAY; - - // filter any invalid configs out - const submoduleConfigs = getValidSubmoduleConfigs(usersync.userIds, enabledSubmodules); - if (submoduleConfigs.length === 0) { - // exit module, if no valid configurations exist - return; - } - - // get list of submodules with valid configurations - submodules = enabledSubmodules.reduce((carry, enabledSubmodule) => { - // try to find submodule configuration for submodule, if config exists it should be enabled - const submoduleConfig = find(submoduleConfigs, item => item.name === enabledSubmodule.name); - - if (submoduleConfig) { - // append {SubmoduleContainer} containing the submodule and config - carry.push({ - submodule: enabledSubmodule, - config: submoduleConfig, - idObj: undefined - }); - } - return carry; - }, []); - - // complete initialization if any submodules exist - if (submodules.length) { - // priority has been set so it loads after consentManagement (which has a priority of 50) - $$PREBID_GLOBAL$$.requestBids.before(requestBidsHook, 40); - utils.logInfo(`${MODULE_NAME} - usersync config updated for ${submodules.length} submodules`); - } + config.getConfig(conf => { + // Note: support for both 'userSync' and 'usersync' will be deprecated with Prebid.js 3.0 + const userSync = conf.userSync || conf.usersync; + if (userSync && userSync.userIds) { + configRegistry = userSync.userIds; + syncDelay = utils.isNumber(userSync.syncDelay) ? userSync.syncDelay : DEFAULT_SYNC_DELAY; + updateSubmodules(); } - }); + }) } -init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); +// init config update listener to start the application +init(config); + +// add submodules after init has been called +attachIdSystem(pubCommonIdSubmodule); +attachIdSystem(unifiedIdSubmodule); diff --git a/modules/userId.md b/modules/userId.md index a873a76674f..782e7782554 100644 --- a/modules/userId.md +++ b/modules/userId.md @@ -3,12 +3,12 @@ Example showing `cookie` storage for user id data for both submodules ``` pbjs.setConfig({ - usersync: { + userSync: { userIds: [{ name: "unifiedId", params: { partner: "prebid", - url: "http://match.adsrvr.org/track/rid?ttd_pid=prebid&fmt=json" + url: "//match.adsrvr.org/track/rid?ttd_pid=prebid&fmt=json" }, storage: { type: "cookie", diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index c7901106538..d0f5e06cdad 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -1,24 +1,34 @@ import { init, + requestBidsHook, + setSubmoduleRegistry, syncDelay, - pubCommonIdSubmodule, - unifiedIdSubmodule, - requestBidsHook + attachIdSystem } from 'modules/userId'; import {config} from 'src/config'; import * as utils from 'src/utils'; - +import {unifiedIdSubmodule} from 'modules/unifiedIdSystem'; +import {pubCommonIdSubmodule} from 'modules/pubCommonIdSystem'; let assert = require('chai').assert; let expect = require('chai').expect; +const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; describe('User ID', function() { - const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; - - function createStorageConfig(name = 'pubCommonId', key = 'pubcid', type = 'cookie', expires = 30) { + function getConfigMock(configArr1, configArr2) { + return { + userSync: { + syncDelay: 0, + userIds: [ + (configArr1 && configArr1.length === 3) ? getStorageMock.apply(null, configArr1) : null, + (configArr2 && configArr2.length === 3) ? getStorageMock.apply(null, configArr2) : null + ].filter(i => i)} + } + } + function getStorageMock(name = 'pubCommonId', key = 'pubcid', type = 'cookie', expires = 30) { return { name: name, storage: { name: key, type: type, expires: expires } } } - function createAdUnit(code = 'adUnit-code') { + function getAdUnitMock(code = 'adUnit-code') { return { code, mediaTypes: {banner: {}, native: {}}, @@ -29,6 +39,8 @@ describe('User ID', function() { before(function() { utils.setCookie('_pubcid_optout', '', EXPIRED_COOKIE_DATE); + localStorage.removeItem('_pbjs_id_optout'); + localStorage.removeItem('_pubcid_optout'); }); describe('Decorate Ad Units', function() { @@ -48,16 +60,17 @@ describe('User ID', function() { }); it('Check same cookie behavior', function () { - let adUnits1 = [createAdUnit()]; - let adUnits2 = [createAdUnit()]; + let adUnits1 = [getAdUnitMock()]; + let adUnits2 = [getAdUnitMock()]; let innerAdUnits1; let innerAdUnits2; let pubcid = utils.getCookie('pubcid'); expect(pubcid).to.be.null; // there should be no cookie initially - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); - config.setConfig({ usersync: { syncDelay: 0, userIds: [ createStorageConfig() ] } }); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); requestBidsHook(config => { innerAdUnits1 = config.adUnits }, {adUnits: adUnits1}); pubcid = utils.getCookie('pubcid'); // cookies is created after requestbidHook @@ -74,15 +87,16 @@ describe('User ID', function() { }); it('Check different cookies', function () { - let adUnits1 = [createAdUnit()]; - let adUnits2 = [createAdUnit()]; + let adUnits1 = [getAdUnitMock()]; + let adUnits2 = [getAdUnitMock()]; let innerAdUnits1; let innerAdUnits2; let pubcid1; let pubcid2; - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); - config.setConfig({ usersync: { syncDelay: 0, userIds: [ createStorageConfig() ] } }); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); requestBidsHook((config) => { innerAdUnits1 = config.adUnits }, {adUnits: adUnits1}); pubcid1 = utils.getCookie('pubcid'); // get first cookie utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); // erase cookie @@ -94,8 +108,9 @@ describe('User ID', function() { }); }); - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); - config.setConfig({ usersync: { syncDelay: 0, userIds: [ createStorageConfig() ] } }); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); requestBidsHook((config) => { innerAdUnits2 = config.adUnits }, {adUnits: adUnits2}); pubcid2 = utils.getCookie('pubcid'); // get second cookie @@ -111,15 +126,12 @@ describe('User ID', function() { }); it('Check new cookie', function () { - let adUnits = [createAdUnit()]; + let adUnits = [getAdUnitMock()]; let innerAdUnits; - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); - config.setConfig({ - usersync: { - syncDelay: 0, - userIds: [createStorageConfig('pubCommonId', 'pubcid_alt', 'cookie')]} - }); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid_alt', 'cookie'])); requestBidsHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); innerAdUnits.forEach((unit) => { unit.bids.forEach((bid) => { @@ -152,14 +164,16 @@ describe('User ID', function() { }); it('fails initialization if opt out cookie exists', function () { - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); - config.setConfig({ usersync: { syncDelay: 0, userIds: [ createStorageConfig() ] } }); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - opt-out cookie found, exit module'); }); it('initializes if no opt out cookie exists', function () { - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); - config.setConfig({ usersync: { syncDelay: 0, userIds: [ createStorageConfig() ] } }); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 1 submodules'); }); }); @@ -176,20 +190,23 @@ describe('User ID', function() { }); it('handles config with no usersync object', function () { - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); config.setConfig({}); // usersync is undefined, and no logInfo message for 'User ID - usersync config updated' expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with empty usersync object', function () { - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); config.setConfig({ usersync: {} }); expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with usersync and userIds that are empty objs', function () { - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); config.setConfig({ usersync: { userIds: [{}] @@ -199,7 +216,8 @@ describe('User ID', function() { }); it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); config.setConfig({ usersync: { userIds: [{ @@ -215,21 +233,16 @@ describe('User ID', function() { }); it('config with 1 configurations should create 1 submodules', function () { - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); - config.setConfig({ - usersync: { - syncDelay: 0, - userIds: [{ - name: 'unifiedId', - storage: { name: 'unifiedid', type: 'cookie' } - }] - } - }); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['unifiedId', 'unifiedid', 'cookie'])); + expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 1 submodules'); }); it('config with 2 configurations should result in 2 submodules add', function () { - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); config.setConfig({ usersync: { syncDelay: 0, @@ -245,7 +258,8 @@ describe('User ID', function() { }); it('config syncDelay updates module correctly', function () { - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); config.setConfig({ usersync: { syncDelay: 99, @@ -263,19 +277,15 @@ describe('User ID', function() { let adUnits; beforeEach(function() { - adUnits = [createAdUnit()]; + adUnits = [getAdUnitMock()]; }); it('test hook from pubcommonid cookie', function(done) { utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 100000).toUTCString())); - init(config, [pubCommonIdSubmodule]); - config.setConfig({ - usersync: { - syncDelay: 0, - userIds: [createStorageConfig('pubCommonId', 'pubcid', 'cookie')] - } - }); + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); requestBidsHook(function() { adUnits.forEach(unit => { @@ -290,7 +300,8 @@ describe('User ID', function() { }); it('test hook from pubcommonid config value object', function(done) { - init(config, [pubCommonIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule]); + init(config); config.setConfig({ usersync: { syncDelay: 0, @@ -316,12 +327,9 @@ describe('User ID', function() { localStorage.setItem('unifiedid_alt', JSON.stringify({'TDID': 'testunifiedid_alt'})); localStorage.setItem('unifiedid_alt_exp', ''); - init(config, [unifiedIdSubmodule]); - config.setConfig({ - usersync: { - syncDelay: 0, - userIds: [createStorageConfig('unifiedId', 'unifiedid_alt', 'html5')]} - }); + setSubmoduleRegistry([unifiedIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['unifiedId', 'unifiedid_alt', 'html5'])); requestBidsHook(function() { adUnits.forEach(unit => { @@ -340,29 +348,111 @@ describe('User ID', function() { utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); utils.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'])); + + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + // verify that the PubCommonId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + // also check that UnifiedId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.tdid'); + expect(bid.userId.tdid).to.equal('testunifiedid'); + }); + }); + utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + + it('test hook when pubCommonId and unifiedId have their modules added before and after init', function(done) { + utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); + utils.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); + + setSubmoduleRegistry([]); + + // attaching before init + attachIdSystem(pubCommonIdSubmodule); + + init(config); + + // attaching after init + attachIdSystem(unifiedIdSubmodule); + + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'])); + + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + // verify that the PubCommonId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.pubcid'); + expect(bid.userId.pubcid).to.equal('testpubcid'); + // also check that UnifiedId id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.tdid'); + expect(bid.userId.tdid).to.equal('cookie-value-add-module-variations'); + }); + }); + utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + + it('should add new id system ', function(done) { + utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); + utils.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); + utils.setCookie('MOCKID', JSON.stringify({'MOCKID': '123456778'}), new Date(Date.now() + 5000).toUTCString()); + + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config); + config.setConfig({ usersync: { syncDelay: 0, - userIds: [ - createStorageConfig('pubCommonId', 'pubcid', 'cookie'), - createStorageConfig('unifiedId', 'unifiedid', 'cookie') - ]} + userIds: [{ + name: 'pubCommonId', storage: { name: 'pubcid', type: 'cookie' } + }, { + name: 'unifiedId', storage: { name: 'unifiedid', type: 'cookie' } + }, { + name: 'mockId', storage: { name: 'MOCKID', type: 'cookie' } + }] + } + }); + + // Add new submodule named 'mockId' + attachIdSystem({ + name: 'mockId', + decode: function(value) { + return { + 'mid': value['MOCKID'] + }; + }, + getId: function() { + return {'MOCKID': '1234'} + } }); requestBidsHook(function() { adUnits.forEach(unit => { unit.bids.forEach(bid => { - // verify that the PubCommonId id data was copied to bid + // check PubCommonId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.pubcid'); expect(bid.userId.pubcid).to.equal('testpubcid'); - // also check that UnifiedId id data was copied to bid + // check UnifiedId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.tdid'); - expect(bid.userId.tdid).to.equal('testunifiedid'); + expect(bid.userId.tdid).to.equal('cookie-value-add-module-variations'); + // check MockId data was copied to bid + expect(bid).to.have.deep.nested.property('userId.mid'); + expect(bid.userId.mid).to.equal('123456778'); }); }); utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); From 72c8e4f7f88ef7a95d4cacb909e965b0275e1f27 Mon Sep 17 00:00:00 2001 From: Jaimin Panchal <7393273+jaiminpanchal27@users.noreply.github.com> Date: Wed, 22 May 2019 09:33:11 -0400 Subject: [PATCH 076/146] Adding advertiserId to appnexus adapter (#3833) --- modules/appnexusBidAdapter.js | 9 +- package-lock.json | 231 ++++++++++--------- test/spec/modules/appnexusBidAdapter_spec.js | 14 ++ 3 files changed, 144 insertions(+), 110 deletions(-) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index a27a10d4372..2dafd8fb293 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -368,6 +368,10 @@ function newBid(serverBid, rtbBid, bidderRequest) { } }; + if (rtbBid.advertiser_id) { + bid.meta = Object.assign({}, bid.meta, { advertiserId: rtbBid.advertiser_id }); + } + if (rtbBid.rtb.video) { Object.assign(bid, { width: rtbBid.rtb.video.player_width, @@ -380,10 +384,7 @@ function newBid(serverBid, rtbBid, bidderRequest) { const videoContext = utils.deepAccess(bidRequest, 'mediaTypes.video.context'); if (videoContext === ADPOD) { const iabSubCatId = getIabSubCategory(bidRequest.bidder, rtbBid.brand_category_id); - bid.meta = { - iabSubCatId - }; - + bid.meta = Object.assign({}, bid.meta, { iabSubCatId }); bid.video = { context: ADPOD, durationSeconds: Math.floor(rtbBid.rtb.video.duration_ms / 1000), diff --git a/package-lock.json b/package-lock.json index b00be53f565..d313ff22ea0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.15.0", + "version": "2.16.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3108,7 +3108,7 @@ }, "query-string": { "version": "5.1.1", - "resolved": "http://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", "dev": true, "requires": { @@ -3480,7 +3480,7 @@ }, "commander": { "version": "2.15.1", - "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true }, @@ -4585,7 +4585,7 @@ "engine.io": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", - "integrity": "sha1-tgKBw1SEpw7gNR6g6/+D7IyVIqI=", + "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", "dev": true, "requires": { "accepts": "~1.3.4", @@ -4599,7 +4599,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -4615,7 +4615,7 @@ }, "engine.io-client": { "version": "3.2.1", - "resolved": "http://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", "dev": true, "requires": { @@ -4635,7 +4635,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -5326,7 +5326,7 @@ }, "event-stream": { "version": "3.3.4", - "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", "dev": true, "requires": { @@ -5865,7 +5865,7 @@ "flatted": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", - "integrity": "sha1-VRIrZTbqSWtLRIk+4mCBQdENmRY=", + "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", "dev": true }, "flush-write-stream": { @@ -6065,27 +6065,28 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, "optional": true, @@ -6096,15 +6097,17 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", - "resolved": false, + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6112,39 +6115,42 @@ }, "chownr": { "version": "1.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true }, "debug": { "version": "2.6.9", - "resolved": false, + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "optional": true, @@ -6154,28 +6160,28 @@ }, "deep-extend": { "version": "0.6.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "optional": true, @@ -6185,14 +6191,14 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, @@ -6209,7 +6215,7 @@ }, "glob": { "version": "7.1.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "optional": true, @@ -6224,14 +6230,14 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.24", - "resolved": false, + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "optional": true, @@ -6241,7 +6247,7 @@ }, "ignore-walk": { "version": "3.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, @@ -6251,7 +6257,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, @@ -6262,53 +6268,58 @@ }, "inherits": { "version": "2.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } }, "isarray": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "resolved": false, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -6316,7 +6327,7 @@ }, "minizlib": { "version": "1.2.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "dev": true, "optional": true, @@ -6326,23 +6337,24 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } }, "ms": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true, "optional": true }, "needle": { "version": "2.2.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.4.tgz", "integrity": "sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==", "dev": true, "optional": true, @@ -6354,7 +6366,7 @@ }, "node-pre-gyp": { "version": "0.10.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz", "integrity": "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A==", "dev": true, "optional": true, @@ -6373,7 +6385,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, @@ -6384,14 +6396,14 @@ }, "npm-bundled": { "version": "1.0.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz", "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.2.0.tgz", "integrity": "sha512-7Mni4Z8Xkx0/oegoqlcao/JpPCPEMtUvsmB0q7mgvlMinykJLSRTYuFqoQLYgGY8biuxIeiHO+QNJKbCfljewQ==", "dev": true, "optional": true, @@ -6402,7 +6414,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, @@ -6415,43 +6427,45 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, + "optional": true, "requires": { "wrappy": "1" } }, "os-homedir": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, @@ -6462,21 +6476,21 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true, "optional": true }, "rc": { "version": "1.2.8", - "resolved": false, + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "optional": true, @@ -6489,7 +6503,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true @@ -6498,7 +6512,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, @@ -6514,7 +6528,7 @@ }, "rimraf": { "version": "2.6.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "dev": true, "optional": true, @@ -6524,50 +6538,52 @@ }, "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true }, "semver": { "version": "5.6.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6576,7 +6592,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "optional": true, @@ -6586,23 +6602,24 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } }, "strip-json-comments": { "version": "2.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "4.4.8", - "resolved": false, + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", "dev": true, "optional": true, @@ -6618,14 +6635,14 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true }, "wide-align": { "version": "1.1.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "optional": true, @@ -6635,15 +6652,17 @@ }, "wrappy": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true + "dev": true, + "optional": true } } }, @@ -7318,7 +7337,7 @@ "gulp-connect": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/gulp-connect/-/gulp-connect-5.7.0.tgz", - "integrity": "sha1-fpJfXkw06/7fnzGFdpZuj+iEDVo=", + "integrity": "sha512-8tRcC6wgXMLakpPw9M7GRJIhxkYdgZsXwn7n56BA2bQYGLR9NOPhMzx7js+qYDy6vhNkbApGKURjAw1FjY4pNA==", "dev": true, "requires": { "ansi-colors": "^2.0.5", @@ -7335,7 +7354,7 @@ "ansi-colors": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-2.0.5.tgz", - "integrity": "sha1-XaN4Jf7z51872kf3YNZL/RDhXhA=", + "integrity": "sha512-yAdfUZ+c2wetVNIFsNRn44THW+Lty6S5TwMpUfLA/UaGhiXbBv/F8E60/1hMLd0cnF/CDoWH8vzVaI5bAcHCjw==", "dev": true } } @@ -8038,7 +8057,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -9034,7 +9053,7 @@ "karma": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/karma/-/karma-3.1.4.tgz", - "integrity": "sha1-OJDKlyKxDR0UtybhM1kxRVeISZ4=", + "integrity": "sha512-31Vo8Qr5glN+dZEVIpnPCxEGleqE0EY6CtC2X9TagRV3rRQ3SNrvfhddICkJgUK3AgqpeKSZau03QumTGhGoSw==", "dev": true, "requires": { "bluebird": "^3.3.0", @@ -9659,7 +9678,7 @@ "log4js": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.6.tgz", - "integrity": "sha1-5srO2Uln7uuc45n5+GgqSysoyP8=", + "integrity": "sha512-ezXZk6oPJCWL483zj64pNkMuY/NcRX5MPiB0zE6tjZM137aeusrOnW1ecxgF9cmwMWkBMhjteQxBPoZBh9FDxQ==", "dev": true, "requires": { "circular-json": "^0.5.5", @@ -9672,7 +9691,7 @@ "circular-json": { "version": "0.5.9", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", - "integrity": "sha1-kydjroj0996teg0JyKUaR0OlOx0=", + "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==", "dev": true }, "debug": { @@ -10209,7 +10228,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -10245,7 +10264,7 @@ "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true } @@ -10853,7 +10872,7 @@ "dependencies": { "minimist": { "version": "0.0.10", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", "dev": true }, @@ -12126,7 +12145,7 @@ "rfdc": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.2.tgz", - "integrity": "sha1-5uctdPXcOd6PU49l4Aw2wYAY40k=", + "integrity": "sha512-92ktAgvZhBzYTIK0Mja9uen5q5J3NRVMoDkJL2VMwq6SXjVCgqvQeVP2XAaUY6HT+XpQYeLSjb3UoitBryKmdA==", "dev": true }, "rgb2hex": { @@ -12595,7 +12614,7 @@ "socket.io": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", - "integrity": "sha1-oGnF/qvuPmshSnW0DOBlLhz7mYA=", + "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", "dev": true, "requires": { "debug": "~3.1.0", @@ -12609,7 +12628,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -12632,7 +12651,7 @@ "socket.io-client": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", - "integrity": "sha1-3LOBA0NqtFeN2wJmOK4vIbYjZx8=", + "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", "dev": true, "requires": { "backo2": "1.0.2", @@ -12654,7 +12673,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -12670,7 +12689,7 @@ }, "socket.io-parser": { "version": "3.2.0", - "resolved": "http://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", "dev": true, "requires": { @@ -12682,7 +12701,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -12800,7 +12819,7 @@ }, "split": { "version": "0.3.3", - "resolved": "http://registry.npmjs.org/split/-/split-0.3.3.tgz", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", "dev": true, "requires": { @@ -12934,7 +12953,7 @@ }, "stream-combiner": { "version": "0.0.4", - "resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", "dev": true, "requires": { @@ -13935,7 +13954,7 @@ "useragent": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", - "integrity": "sha1-IX+UOtVAyyEoZYqyP8lg9qiMmXI=", + "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", "dev": true, "requires": { "lru-cache": "4.1.x", @@ -14575,7 +14594,7 @@ }, "webpack-dev-middleware": { "version": "2.0.6", - "resolved": "http://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz", "integrity": "sha512-tj5LLD9r4tDuRIDa5Mu9lnY2qBBehAITv6A9irqXhw/HQquZgTx3BCd57zYbU2gMDnncA49ufK2qVQSbaKJwOw==", "dev": true, "requires": { diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 312201e347d..c912122b25d 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -915,5 +915,19 @@ describe('AppNexusAdapter', function () { let result = spec.interpretResponse({ body: responseWithDeal }, {bidderRequest}); expect(Object.keys(result[0].appnexus)).to.include.members(['buyerMemberId', 'dealPriority', 'dealCode']); }); + + it('should add advertiser id', function() { + let responseAdvertiserId = deepClone(response); + responseAdvertiserId.tags[0].ads[0].advertiser_id = '123'; + + let bidderRequest = { + bids: [{ + bidId: '3db3773286ee59', + adUnitCode: 'code' + }] + } + let result = spec.interpretResponse({ body: responseAdvertiserId }, {bidderRequest}); + expect(Object.keys(result[0].meta)).to.include.members(['advertiserId']); + }) }); }); From 50de3d4280d60f81da50caf47a25d3144ced63f1 Mon Sep 17 00:00:00 2001 From: afsheenb Date: Wed, 22 May 2019 11:28:21 -0400 Subject: [PATCH 077/146] updated ozone adapter from v1.4.4 -> v2.0.0 (#3828) * updated ozone adapter from v1.4.4 -> v2.0.0 * unruly renderer fixes after prebid team feedback, md update * updating docs and spec to account for customData changes * fixing whitespace --- modules/ozoneBidAdapter.js | 254 ++++++++++++++++------ modules/ozoneBidAdapter.md | 42 +++- test/spec/modules/ozoneBidAdapter_spec.js | 116 +++++++++- 3 files changed, 336 insertions(+), 76 deletions(-) diff --git a/modules/ozoneBidAdapter.js b/modules/ozoneBidAdapter.js index 406bffdf4df..c5d9f16ef58 100644 --- a/modules/ozoneBidAdapter.js +++ b/modules/ozoneBidAdapter.js @@ -2,14 +2,20 @@ import * as utils from '../src/utils'; import { registerBidder } from '../src/adapters/bidderFactory'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes'; import {config} from '../src/config'; +import {getPriceBucketString} from '../src/cpmBucketManager'; +import { Renderer } from '../src/Renderer' const BIDDER_CODE = 'ozone'; const OZONEURI = 'https://elb.the-ozone-project.com/openrtb2/auction'; +const OZONE_RENDERER_URL = 'https://prebid.the-ozone-project.com/ozone-renderer.js' + const OZONECOOKIESYNC = 'https://elb.the-ozone-project.com/static/load-cookie.html'; -const OZONEVERSION = '1.4.7'; +const OZONEVERSION = '2.0.0'; + export const spec = { code: BIDDER_CODE, + supportedMediaTypes: [VIDEO, BANNER], /** * Basic check to see whether required parameters are in the request. @@ -63,19 +69,26 @@ export const spec = { return false; } } + if (bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(VIDEO)) { + if (!bid.mediaTypes.video.hasOwnProperty('context')) { + utils.logInfo('OZONE: [WARNING] No context key/value in bid. Rejecting bid: ', ozoneBidRequest); + return false; + } + if (bid.mediaTypes.video.context !== 'outstream') { + utils.logInfo('OZONE: [WARNING] Only outstream video is supported. Rejecting bid: ', ozoneBidRequest); + return false; + } + } return true; }, + buildRequests(validBidRequests, bidderRequest) { utils.logInfo('OZONE: ozone v' + OZONEVERSION + ' validBidRequests', validBidRequests, 'bidderRequest', bidderRequest); - utils.logInfo('OZONE: buildRequests setting auctionId', bidderRequest.auctionId); let singleRequest = config.getConfig('ozone.singleRequest'); - singleRequest = singleRequest !== false; // undefined & true will be true utils.logInfo('OZONE: config ozone.singleRequest : ', singleRequest); let htmlParams = validBidRequests[0].params; // the html page config params will be included in each element let ozoneRequest = {}; // we only want to set specific properties on this, not validBidRequests[0].params - // ozoneRequest['id'] = utils.generateUUID(); - delete ozoneRequest.test; // don't allow test to be set in the config - ONLY use $_GET['pbjs_debug'] if (bidderRequest.gdprConsent) { utils.logInfo('OZONE: ADDING GDPR info'); @@ -92,8 +105,7 @@ export const spec = { ozoneRequest.device = {'w': window.innerWidth, 'h': window.innerHeight}; let tosendtags = validBidRequests.map(ozoneBidRequest => { var obj = {}; - obj.id = ozoneBidRequest.bidId; // this causes a failure if we change it to something else - // obj.id = ozoneBidRequest.adUnitCode; // (eg. 'mpu' or 'leaderboard') A unique identifier for this impression within the context of the bid request (typically, starts with 1 and increments. + obj.id = ozoneBidRequest.bidId; // this causes an error if we change it to something else, even if you update the bidRequest object: "WARNING: Bidder ozone made bid for unknown request ID: mb7953.859498327448. Ignoring." obj.tagid = (ozoneBidRequest.params.placementId).toString(); obj.secure = window.location.protocol === 'https:' ? 1 : 0; // is there a banner (or nothing declared, so banner is the default)? @@ -141,6 +153,7 @@ export const spec = { obj.ext = {'prebid': {'storedrequest': {'id': (ozoneBidRequest.params.placementId).toString()}}, 'ozone': {}}; obj.ext.ozone.adUnitCode = ozoneBidRequest.adUnitCode; // eg. 'mpu' obj.ext.ozone.transactionId = ozoneBidRequest.transactionId; // this is the transactionId PER adUnit, common across bidders for this unit + obj.ext.ozone.oz_pb_v = OZONEVERSION; if (ozoneBidRequest.params.hasOwnProperty('customData')) { obj.ext.ozone.customData = ozoneBidRequest.params.customData; } @@ -150,16 +163,14 @@ export const spec = { if (ozoneBidRequest.params.hasOwnProperty('lotameData')) { obj.ext.ozone.lotameData = ozoneBidRequest.params.lotameData; } - if (ozoneBidRequest.hasOwnProperty('crumbs') && ozoneBidRequest.crumbs.hasOwnProperty('pubcid')) { + if (utils.deepAccess(ozoneBidRequest, 'crumbs.pubcid')) { obj.ext.ozone.pubcid = ozoneBidRequest.crumbs.pubcid; } return obj; }); - utils.logInfo('tosendtags = ', tosendtags); ozoneRequest.site = {'publisher': {'id': htmlParams.publisherId}, 'page': document.location.href}; ozoneRequest.test = parseInt(getTestQuerystringValue()); // will be 1 or 0 - // utils.logInfo('_ozoneInternal is', _ozoneInternal); // return the single request object OR the array: if (singleRequest) { utils.logInfo('OZONE: buildRequests starting to generate response for a single request'); @@ -177,7 +188,6 @@ export const spec = { utils.logInfo('OZONE: buildRequests going to return for single: ', ret); return ret; } - // not single request - pull apart the tosendtags array & return an array of objects each containing one element in the imp array. let arrRet = tosendtags.map(imp => { utils.logInfo('OZONE: buildRequests starting to generate non-single response, working on imp : ', imp); @@ -201,67 +211,65 @@ export const spec = { /** * Interpret the response if the array contains BIDDER elements, in the format: [ [bidder1 bid 1, bidder1 bid 2], [bidder2 bid 1, bidder2 bid 2] ] * NOte that in singleRequest mode this will be called once, else it will be called for each adSlot's response + * + * Updated April 2019 to return all bids, not just the one we decide is the 'winner' + * * @param serverResponse * @param request * @returns {*} */ interpretResponse(serverResponse, request) { - utils.logInfo('OZONE: version' + OZONEVERSION + ' interpretResponse', serverResponse, request); serverResponse = serverResponse.body || {}; - if (serverResponse.seatbid) { - if (utils.isArray(serverResponse.seatbid)) { - // serverResponse seems good, let's get the list of bids from the request object: - let arrRequestBids = request.bidderRequest.bids; - // build up a list of winners, one for each bidId in arrBidIds - let arrWinners = []; - for (let i = 0; i < arrRequestBids.length; i++) { - let thisBid = arrRequestBids[i]; - let ozoneInternalKey = thisBid.bidId; - let {seat: winningSeat, bid: winningBid} = ozoneGetWinnerForRequestBid(thisBid, serverResponse.seatbid); - - if (winningBid == null) { - utils.logInfo('OZONE: FAILED to get winning bid for bid : ', thisBid, 'will skip. Possibly a non-single request, which will be missing some bid IDs'); - continue; - } - - const {defaultWidth, defaultHeight} = defaultSize(arrRequestBids[i]); - winningBid = ozoneAddStandardProperties(winningBid, defaultWidth, defaultHeight); - - utils.logInfo('OZONE: Going to add the adserverTargeting custom parameters for key: ', ozoneInternalKey); - let adserverTargeting = {}; - let allBidsForThisBidid = ozoneGetAllBidsForBidId(ozoneInternalKey, serverResponse.seatbid); - // add all the winning & non-winning bids for this bidId: - Object.keys(allBidsForThisBidid).forEach(function(bidderName, index, ar2) { - utils.logInfo('OZONE: inside allBidsForThisBidid:foreach', bidderName, index, ar2, allBidsForThisBidid); - adserverTargeting['oz_' + bidderName] = bidderName; - adserverTargeting['oz_' + bidderName + '_pb'] = String(allBidsForThisBidid[bidderName].price); - adserverTargeting['oz_' + bidderName + '_crid'] = String(allBidsForThisBidid[bidderName].crid); - adserverTargeting['oz_' + bidderName + '_adv'] = String(allBidsForThisBidid[bidderName].adomain); - adserverTargeting['oz_' + bidderName + '_imp_id'] = String(allBidsForThisBidid[bidderName].impid); - }); - // now add the winner data: - adserverTargeting['oz_auc_id'] = String(request.bidderRequest.auctionId); - adserverTargeting['oz_winner'] = String(winningSeat); - adserverTargeting['oz_winner_auc_id'] = String(winningBid.id); - adserverTargeting['oz_winner_imp_id'] = String(winningBid.impid); - adserverTargeting['oz_response_id'] = String(serverResponse.id); + if (!serverResponse.hasOwnProperty('seatbid')) { return []; } + if (typeof serverResponse.seatbid !== 'object') { return []; } + let arrAllBids = []; + serverResponse.seatbid = injectAdIdsIntoAllBidResponses(serverResponse.seatbid); // we now make sure that each bid in the bidresponse has a unique (within page) adId attribute. + for (let i = 0; i < serverResponse.seatbid.length; i++) { + let sb = serverResponse.seatbid[i]; + const {defaultWidth, defaultHeight} = defaultSize(request.bidderRequest.bids[i]); + for (let j = 0; j < sb.bid.length; j++) { + let thisBid = ozoneAddStandardProperties(sb.bid[j], defaultWidth, defaultHeight); - winningBid.adserverTargeting = adserverTargeting; - utils.logInfo('OZONE: winner is', winningBid); - arrWinners.push(winningBid); - utils.logInfo('OZONE: arrWinners is', arrWinners); + // from https://github.com/prebid/Prebid.js/pull/1082 + if (utils.deepAccess(thisBid, 'ext.prebid.type') === VIDEO) { + utils.logInfo('OZONE: going to attach a renderer to:', j); + let renderConf = createObjectForInternalVideoRender(thisBid); + thisBid.renderer = Renderer.install(renderConf); + } else { + utils.logInfo('OZONE: bid is not a video, will not attach a renderer: ', j); } - let winnersClean = arrWinners.filter(w => { - return (w.bidId); // will be cast to boolean + + let ozoneInternalKey = thisBid.bidId; + let adserverTargeting = {}; + // all keys for all bidders for this bid have to be added to all objects returned, else some keys will not be sent to ads? + let allBidsForThisBidid = ozoneGetAllBidsForBidId(ozoneInternalKey, serverResponse.seatbid); + // add all the winning & non-winning bids for this bidId: + utils.logInfo('OZONE: Going to iterate allBidsForThisBidId', allBidsForThisBidid); + Object.keys(allBidsForThisBidid).forEach(function(bidderName, index, ar2) { + adserverTargeting['oz_' + bidderName] = bidderName; + adserverTargeting['oz_' + bidderName + '_pb'] = String(allBidsForThisBidid[bidderName].price); + adserverTargeting['oz_' + bidderName + '_crid'] = String(allBidsForThisBidid[bidderName].crid); + adserverTargeting['oz_' + bidderName + '_adv'] = String(allBidsForThisBidid[bidderName].adomain); + adserverTargeting['oz_' + bidderName + '_imp_id'] = String(allBidsForThisBidid[bidderName].impid); + adserverTargeting['oz_' + bidderName + '_adId'] = String(allBidsForThisBidid[bidderName].adId); + adserverTargeting['oz_' + bidderName + '_pb_r'] = getRoundedBid(allBidsForThisBidid[bidderName].price, allBidsForThisBidid[bidderName].ext.prebid.type); + if (allBidsForThisBidid[bidderName].hasOwnProperty('dealid')) { + adserverTargeting['oz_' + bidderName + '_dealid'] = String(allBidsForThisBidid[bidderName].dealid); + } }); - utils.logInfo('OZONE: going to return winnersClean:', winnersClean); - return winnersClean; - } else { - return []; + // also add in the winning bid, to be sent to dfp + let {seat: winningSeat, bid: winningBid} = ozoneGetWinnerForRequestBid(ozoneInternalKey, serverResponse.seatbid); + adserverTargeting['oz_auc_id'] = String(request.bidderRequest.auctionId); + adserverTargeting['oz_winner'] = String(winningSeat); + adserverTargeting['oz_winner_auc_id'] = String(winningBid.id); + adserverTargeting['oz_winner_imp_id'] = String(winningBid.impid); + adserverTargeting['oz_response_id'] = String(serverResponse.id); + adserverTargeting['oz_pb_v'] = OZONEVERSION; + thisBid.adserverTargeting = adserverTargeting; + arrAllBids.push(thisBid); } - } else { - return []; } + return arrAllBids; }, getUserSyncs(optionsType, serverResponse) { if (!serverResponse || serverResponse.length === 0) { @@ -275,6 +283,21 @@ export const spec = { } } } +/** + * add a page-level-unique adId element to all server response bids. + * NOTE that this is distructive - it mutates the serverResponse object sent in as a parameter + * @param seatbid object (serverResponse.seatbid) + * @returns seatbid object + */ +export function injectAdIdsIntoAllBidResponses(seatbid) { + for (let i = 0; i < seatbid.length; i++) { + let sb = seatbid[i]; + for (let j = 0; j < sb.bid.length; j++) { + sb.bid[j]['adId'] = sb.bid[j]['impid'] + '-' + i; // modify the bidId per-bid, so each bid has a unique adId within this response, and dfp can select one. + } + } + return seatbid; +} export function checkDeepArray(Arr) { if (Array.isArray(Arr)) { if (Array.isArray(Arr[0])) { @@ -300,15 +323,14 @@ export function defaultSize(thebidObj) { * @param serverResponseSeatBid * @returns {*} bid object */ -export function ozoneGetWinnerForRequestBid(requestBid, serverResponseSeatBid) { +export function ozoneGetWinnerForRequestBid(requestBidId, serverResponseSeatBid) { let thisBidWinner = null; let winningSeat = null; for (let j = 0; j < serverResponseSeatBid.length; j++) { let theseBids = serverResponseSeatBid[j].bid; let thisSeat = serverResponseSeatBid[j].seat; for (let k = 0; k < theseBids.length; k++) { - if (theseBids[k].impid === requestBid.bidId) { // we've found a matching server response bid for this request bid - // if (theseBids[k].impid === requestBid.adUnitCode) { // we've found a matching server response bid for this request bid + if (theseBids[k].impid === requestBidId) { // we've found a matching server response bid for this request bid if ((thisBidWinner == null) || (thisBidWinner.price < theseBids[k].price)) { thisBidWinner = theseBids[k]; winningSeat = thisSeat; @@ -327,22 +349,88 @@ export function ozoneGetWinnerForRequestBid(requestBid, serverResponseSeatBid) { * @returns {} = {ozone:{obj}, appnexus:{obj}, ... } */ export function ozoneGetAllBidsForBidId(matchBidId, serverResponseSeatBid) { - utils.logInfo('OZONE: ozoneGetAllBidsForBidId - starting, with: ', matchBidId, serverResponseSeatBid); let objBids = {}; for (let j = 0; j < serverResponseSeatBid.length; j++) { let theseBids = serverResponseSeatBid[j].bid; let thisSeat = serverResponseSeatBid[j].seat; for (let k = 0; k < theseBids.length; k++) { if (theseBids[k].impid === matchBidId) { // we've found a matching server response bid for the request bid we're looking for - utils.logInfo('ozoneGetAllBidsForBidId - found matching bid: ', matchBidId, theseBids[k]); objBids[thisSeat] = theseBids[k]; } } } - utils.logInfo('OZONE: ozoneGetAllBidsForBidId - going to return: ', objBids); return objBids; } +/** + * Round the bid price down according to the granularity + * @param price + * @param mediaType = video, banner or native + */ +export function getRoundedBid(price, mediaType) { + const mediaTypeGranularity = config.getConfig(`mediaTypePriceGranularity.${mediaType}`); // might be string or object or nothing; if set then this takes precedence over 'priceGranularity' + let objBuckets = config.getConfig('customPriceBucket'); // this is always an object - {} if strBuckets is not 'custom' + let strBuckets = config.getConfig('priceGranularity'); // priceGranularity value, always a string ** if priceGranularity is set to an object then it's always 'custom' + let theConfigObject = getGranularityObject(mediaType, mediaTypeGranularity, strBuckets, objBuckets); + let theConfigKey = getGranularityKeyName(mediaType, mediaTypeGranularity, strBuckets); + + utils.logInfo('getRoundedBid. price:', price, 'mediaType:', mediaType, 'configkey:', theConfigKey, 'configObject:', theConfigObject, 'mediaTypeGranularity:', mediaTypeGranularity, 'strBuckets:', strBuckets); + + let priceStringsObj = getPriceBucketString( + price, + theConfigObject, + config.getConfig('currency.granularityMultiplier') + ); + utils.logInfo('priceStringsObj', priceStringsObj); + // by default, without any custom granularity set, you get granularity name : 'medium' + let granularityNamePriceStringsKeyMapping = { + 'medium': 'med', + 'custom': 'custom', + 'high': 'high', + 'low': 'low', + 'dense': 'dense' + }; + if (granularityNamePriceStringsKeyMapping.hasOwnProperty(theConfigKey)) { + let priceStringsKey = granularityNamePriceStringsKeyMapping[theConfigKey]; + utils.logInfo('OZONE: looking for priceStringsKey:', priceStringsKey); + return priceStringsObj[priceStringsKey]; + } + return priceStringsObj['auto']; +} + +/** + * return the key to use to get the value out of the priceStrings object, taking into account anything at + * config.priceGranularity level or config.mediaType.xxx level + * I've noticed that the key specified by prebid core : config.getConfig('priceGranularity') does not properly + * take into account the 2-levels of config + */ +export function getGranularityKeyName(mediaType, mediaTypeGranularity, strBuckets) { + if (typeof mediaTypeGranularity === 'string') { + return mediaTypeGranularity; + } + if (typeof mediaTypeGranularity === 'object') { + return 'custom'; + } + if (typeof strBuckets === 'string') { + return strBuckets; + } + return 'auto'; // fall back to a default key - should literally never be needed though. +} + +/** + * return the object to use to create the custom value of the priceStrings object, taking into account anything at + * config.priceGranularity level or config.mediaType.xxx level + */ +export function getGranularityObject(mediaType, mediaTypeGranularity, strBuckets, objBuckets) { + if (typeof mediaTypeGranularity === 'object') { + return mediaTypeGranularity; + } + if (strBuckets === 'custom') { + return objBuckets; + } + return ''; +} + /** * We expect to be able to find a standard set of properties on winning bid objects; add them here. * @param seatBid @@ -379,5 +467,37 @@ export function getTestQuerystringValue() { return 0; } +/** + * Generate a random number per ad; I'll use the current ms timestamp, then append 8 random alpha/numeric characters + * Randomness : 1 in 208 billion random combinations per-millisecond, non-repeating sequence. + * + * @returns {*} + */ +export function pgGuid() { + return new Date().getTime() + 'xxxxxxxx'.replace(/x/g, function(c) { + return Math.round((Math.random() * 36)).toString(36); + }); +} + +function createObjectForInternalVideoRender(bid) { + let obj = { + url: OZONE_RENDERER_URL, + callback: () => onOutstreamRendererLoaded(bid) + } + return obj; +} + +function onOutstreamRendererLoaded(bid) { + try { + bid.renderer.setRender(outstreamRender); + } catch (err) { + utils.logWarn('Prebid Error calling setRender on renderer', err) + } +} + +function outstreamRender(bid) { + window.ozoneVideo.outstreamRender(bid); +} + registerBidder(spec); utils.logInfo('OZONE: ozoneBidAdapter ended'); diff --git a/modules/ozoneBidAdapter.md b/modules/ozoneBidAdapter.md index c013a4d558c..89760697088 100644 --- a/modules/ozoneBidAdapter.md +++ b/modules/ozoneBidAdapter.md @@ -1,4 +1,4 @@ - + # Overview ``` @@ -12,7 +12,7 @@ Maintainer: engineering@ozoneproject.com Module that connects to the Ozone Project's demand source(s). -The Ozone Project bid adapter supports Banner mediaTypes ONLY. +The Ozone Project bid adapter supports Banner and Outstream Video mediaTypes ONLY. # Test Parameters @@ -36,9 +36,43 @@ adUnits = [{ publisherId: 'OZONENUK0001', /* an ID to identify the publisher account - required */ siteId: '4204204201', /* An ID used to identify a site within a publisher account - required */ placementId: '0420420421', /* an ID used to identify the piece of inventory - required - for appnexus test use 13144370. */ - customData: {"key1": "value1", "key2": "value2}, /* optional JSON placeholder for passing publisher key-values for targeting. */ + customData": [{"settings": {}, "targeting": {"key": "value", "key2": ["value1", "value2"],}}] /* optional array with 'targeting' placeholder for passing publisher specific key-values for targeting. */ + ozoneData: {"key1": "value1", "key2": "value2"}, /* optional JSON placeholder for for passing ozone project key-values for targeting. */ + lotameData: {"key1": "value1", "key2": "value2"} /* optional JSON placeholder for passing Lotame DMP data */ + } + }] + }]; +``` + + +``` + +//Outstream Video adUnit + +adUnits = [{ + code: 'id-of-your-video-div', + mediaTypes: { + video: { + playerSize: [640, 480], + mimes: ['video/mp4'], + context: 'outstream', + } + }, + bids: [{ + bidder: 'ozone', + params: { + publisherId: 'OZONENUK0001', /* an ID to identify the publisher account - required */ + siteId: '4204204201', /* An ID used to identify a site within a publisher account - required */ + customData: [{"settings": {}, "targeting": { "key": "value", "key2": ["value1", "value2"]}}] + placementId: '0440440442', /* an ID used to identify the piece of inventory - required - for unruly test use 0440440442. */ + customData": [{"settings": {}, "targeting": {"key": "value", "key2": ["value1", "value2"],}}] /* optional array with 'targeting' placeholder for passing publisher specific key-values for targeting. */ ozoneData: {"key1": "value1", "key2": "value2"}, /* optional JSON placeholder for for passing ozone project key-values for targeting. */ - lotameData: {"key1": "value1", "key2": "value2} /* optional JSON placeholder for passing Lotame DMP data */ + lotameData: {"key1": "value1", "key2": "value2"}, /* optional JSON placeholder for passing Lotame DMP data */ + video: { + skippable: true, /* optional */ + playback_method: ['auto_play_sound_off'], /* optional */ + targetDiv: 'some-different-div-id-to-my-adunitcode' /* optional */ + } } }] }]; diff --git a/test/spec/modules/ozoneBidAdapter_spec.js b/test/spec/modules/ozoneBidAdapter_spec.js index a910ef391b0..b0f252d4469 100644 --- a/test/spec/modules/ozoneBidAdapter_spec.js +++ b/test/spec/modules/ozoneBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { spec } from 'modules/ozoneBidAdapter'; import { config } from 'src/config'; +import {Renderer} from '../../../src/Renderer'; const OZONEURI = 'https://elb.the-ozone-project.com/openrtb2/auction'; const BIDDER_CODE = 'ozone'; /* @@ -63,7 +64,7 @@ var validBidRequestsWithBannerMediaType = [ transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } ]; -var validBidRequestsWithNonBannerMediaTypes = [ +var validBidRequestsWithNonBannerMediaTypesAndValidOutstreamVideo = [ { adUnitCode: 'div-gpt-ad-1460505748561-0', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', @@ -72,8 +73,8 @@ var validBidRequestsWithNonBannerMediaTypes = [ bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: {'gender': 'bart', 'age': 'low'}, ozoneData: {'networkID': '3048', 'dfpSiteID': 'd.thesun', 'sectionID': 'homepage', 'path': '/', 'sec_id': 'null', 'sec': 'sec', 'topics': 'null', 'kw': 'null', 'aid': 'null', 'search': 'null', 'article_type': 'null', 'hide_ads': '', 'article_slug': 'null'}, lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}], 'ThirdPartyAudience': [{'id': '123', 'name': 'Automobiles'}, {'id': '456', 'name': 'Ages: 30-39'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, - mediaTypes: {video: {info: 'dummy data'}, native: {info: 'dummy data'}}, + params: { publisherId: '9876abcd12-3', customData: {'gender': 'bart', 'age': 'low'}, ozoneData: {'networkID': '3048', 'dfpSiteID': 'd.thesun', 'sectionID': 'homepage', 'path': '/', 'sec_id': 'null', 'sec': 'sec', 'topics': 'null', 'kw': 'null', 'aid': 'null', 'search': 'null', 'article_type': 'null', 'hide_ads': '', 'article_slug': 'null'}, lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}], 'ThirdPartyAudience': [{'id': '123', 'name': 'Automobiles'}, {'id': '456', 'name': 'Ages: 30-39'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, video: {skippable: true, playback_method: ['auto_play_sound_off'], targetDiv: 'some-different-div-id-to-my-adunitcode'} } ] }, + mediaTypes: {video: {mimes: ['video/mp4'], 'context': 'outstream'}, native: {info: 'dummy data'}}, transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } ]; @@ -154,6 +155,67 @@ var validResponse = { } }, 'headers': {} +}; +var validOutstreamResponse = { + 'body': { + 'id': 'd6198807-7a53-4141-b2db-d2cb754d68ba', + 'seatbid': [ + { + 'bid': [ + { + 'id': '677903815252395017', + 'impid': '2899ec066a91ff8', + 'price': 0.5, + 'adm': '', + 'adid': '98493581', + 'adomain': [ + 'http://prebid.org' + ], + 'iurl': 'https://fra1-ib.adnxs.com/cr?id=98493581', + 'cid': '9325', + 'crid': '98493581', + 'cat': [ + 'IAB3-1' + ], + 'w': 300, + 'h': 600, + 'ext': { + 'prebid': { + 'type': 'video' + }, + 'bidder': { + 'unruly': { + 'renderer': { + 'config': { + 'targetingUUID': 'aafd3388-afaf-41f4-b271-0ac8e0325a7f', + 'siteId': 1052815, + 'featureOverrides': {} + }, + 'url': 'https://video.unrulymedia.com/native/native-loader.js#supplyMode=prebid?cb=6284685353877994', + 'id': 'unruly_inarticle' + }, + 'vast_url': 'data:text/xml;base64,PD94bWwgdmVyc2lvbj0i' + } + } + } + } + ], + 'seat': 'unruly' + } + ], + 'ext': { + 'responsetimemillis': { + 'appnexus': 47, + 'openx': 30 + } + }, + 'timing': { + 'start': 1536848078.089177, + 'end': 1536848078.142203, + 'TimeTaken': 0.05302619934082031 + } + }, + 'headers': {} } describe('ozone Adapter', function () { @@ -463,6 +525,43 @@ describe('ozone Adapter', function () { it('should not validate lotameData being sent', function () { expect(spec.isBidRequestValid(xBadLotame)).to.equal(false); }); + + var xBadVideoContext = { + bidder: BIDDER_CODE, + params: { + 'placementId': '1234567890', + 'publisherId': '9876abcd12-3', + 'lotameData': 'this should be an object', + siteId: '1234567890' + }, + mediaTypes: { + video: { + mimes: ['video/mp4'], + 'context': 'instream'}, + } + }; + + it('should not validate video instream being sent', function () { + expect(spec.isBidRequestValid(xBadVideoContext)).to.equal(false); + }); + + let validVideoBidReq = { + bidder: BIDDER_CODE, + params: { + placementId: '1310000099', + publisherId: '9876abcd12-3', + siteId: '1234567890' + }, + mediaTypes: { + video: { + mimes: ['video/mp4'], + 'context': 'outstream'}, + } + }; + + it('should not validate video instream being sent', function () { + expect(spec.isBidRequestValid(validVideoBidReq)).to.equal(true); + }); }); describe('buildRequests', function () { @@ -509,8 +608,8 @@ describe('ozone Adapter', function () { expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); }); - it('handles missing banner mediaType element correctly', function () { - const request = spec.buildRequests(validBidRequestsWithNonBannerMediaTypes, validBidderRequest); + it('handles video mediaType element correctly, with outstream video', function () { + const request = spec.buildRequests(validBidRequestsWithNonBannerMediaTypesAndValidOutstreamVideo, validBidderRequest); expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); }); @@ -584,6 +683,13 @@ describe('ozone Adapter', function () { expect(result).to.be.an('array'); expect(result).to.be.empty; }); + + it('should have video renderer', function () { + const request = spec.buildRequests(validBidRequestsWithNonBannerMediaTypesAndValidOutstreamVideo, validBidderRequest); + const result = spec.interpretResponse(validOutstreamResponse, request); + const bid = result[0]; + expect(bid.renderer).to.be.an.instanceOf(Renderer); + }); }); describe('userSyncs', function () { From c6c20a2d5a2245bd883d20b5e0df4c650ccab0d3 Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Wed, 22 May 2019 14:52:26 -0400 Subject: [PATCH 078/146] fix how native sizes are passed in AppNexus adapter (#3832) --- modules/appnexusBidAdapter.js | 24 +++------ test/spec/modules/appnexusBidAdapter_spec.js | 55 ++------------------ 2 files changed, 11 insertions(+), 68 deletions(-) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 2dafd8fb293..e0ae19187b2 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -19,13 +19,11 @@ const NATIVE_MAPPING = { cta: 'ctatext', image: { serverName: 'main_image', - requiredParams: { required: true }, - minimumParams: { sizes: [{}] }, + requiredParams: { required: true } }, icon: { serverName: 'icon', - requiredParams: { required: true }, - minimumParams: { sizes: [{}] }, + requiredParams: { required: true } }, sponsoredBy: 'sponsored_by', privacyLink: 'privacy_link', @@ -680,18 +678,12 @@ function buildNativeRequest(params) { const requiredParams = NATIVE_MAPPING[key] && NATIVE_MAPPING[key].requiredParams; request[requestKey] = Object.assign({}, requiredParams, params[key]); - // minimum params are passed if no non-required params given on adunit - const minimumParams = NATIVE_MAPPING[key] && NATIVE_MAPPING[key].minimumParams; - - if (requiredParams && minimumParams) { - // subtract required keys from adunit keys - const adunitKeys = Object.keys(params[key]); - const requiredKeys = Object.keys(requiredParams); - const remaining = adunitKeys.filter(key => !includes(requiredKeys, key)); - - // if none are left over, the minimum params needs to be sent - if (remaining.length === 0) { - request[requestKey] = Object.assign({}, request[requestKey], minimumParams); + // convert the sizes of image/icon assets to proper format (if needed) + const isImageAsset = !!(requestKey === NATIVE_MAPPING.image.serverName || requestKey === NATIVE_MAPPING.icon.serverName); + if (isImageAsset && request[requestKey].sizes) { + let sizes = request[requestKey].sizes; + if (utils.isArrayOfNums(sizes) || (utils.isArray(sizes) && sizes.length > 0 && sizes.every(sz => utils.isArrayOfNums(sz)))) { + request[requestKey].sizes = transformSizes(request[requestKey].sizes); } } }); diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index c912122b25d..9e37f6cbffb 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -396,7 +396,8 @@ describe('AppNexusAdapter', function () { title: {required: true}, body: {required: true}, body2: {required: true}, - image: {required: true, sizes: [{ width: 100, height: 100 }]}, + image: {required: true, sizes: [100, 100]}, + icon: {required: true}, cta: {required: false}, rating: {required: true}, sponsoredBy: {required: true}, @@ -420,6 +421,7 @@ describe('AppNexusAdapter', function () { description: {required: true}, desc2: {required: true}, main_image: {required: true, sizes: [{ width: 100, height: 100 }]}, + icon: {required: true}, ctatext: {required: false}, rating: {required: true}, sponsored_by: {required: true}, @@ -434,57 +436,6 @@ describe('AppNexusAdapter', function () { }); }); - it('sets minimum native asset params when not provided on adunit', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - mediaType: 'native', - nativeParams: { - image: {required: true}, - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.tags[0].native.layouts[0]).to.deep.equal({ - main_image: {required: true, sizes: [{}]}, - }); - }); - - it('does not overwrite native ad unit params with mimimum params', function () { - let bidRequest = Object.assign({}, - bidRequests[0], - { - mediaType: 'native', - nativeParams: { - image: { - aspect_ratios: [{ - min_width: 100, - ratio_width: 2, - ratio_height: 3, - }] - } - } - } - ); - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.tags[0].native.layouts[0]).to.deep.equal({ - main_image: { - required: true, - aspect_ratios: [{ - min_width: 100, - ratio_width: 2, - ratio_height: 3, - }] - }, - }); - }); - it('should always populated tags[].sizes with 1,1 for native if otherwise not defined', function () { let bidRequest = Object.assign({}, bidRequests[0], From 612cd3d99aef856166e3df3a79585467b0126b0a Mon Sep 17 00:00:00 2001 From: Jaimin Panchal Date: Wed, 22 May 2019 16:36:56 -0400 Subject: [PATCH 079/146] Prebid 2.16.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6bb0b49fab0..4f5085a7dc5 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.16.0-pre", + "version": "2.16.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From c006a6a6fafc29ef009a0a76404895c6996cd9ac Mon Sep 17 00:00:00 2001 From: Jaimin Panchal Date: Wed, 22 May 2019 16:48:13 -0400 Subject: [PATCH 080/146] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4f5085a7dc5..ab415a03ea0 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.16.0", + "version": "2.17.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From a7953e442b16ba46a2b457de8249d03e54aa608d Mon Sep 17 00:00:00 2001 From: Salomon Rada Date: Mon, 27 May 2019 19:17:56 +0300 Subject: [PATCH 081/146] Refactor bid response - remove unnecessary properties (#3807) --- modules/cleanmedianetBidAdapter.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/cleanmedianetBidAdapter.js b/modules/cleanmedianetBidAdapter.js index 00fb5aa5325..15871e1a6ae 100644 --- a/modules/cleanmedianetBidAdapter.js +++ b/modules/cleanmedianetBidAdapter.js @@ -175,16 +175,14 @@ export const spec = { bids.forEach(bid => { const outBid = { - adId: bidRequest.bidRequest.adUnitCode, requestId: bidRequest.bidRequest.bidId, cpm: bid.price, width: bid.w, height: bid.h, ttl: 60 * 10, - creativeId: bid.crid, + creativeId: bid.crid || bid.adid, netRevenue: true, currency: bid.cur || response.cur, - adUnitCode: bidRequest.bidRequest.adUnitCode, mediaType: helper.getMediaType(bid) }; From 3f5a5981c714f30acf0fbb3435d86efdd90dbb5b Mon Sep 17 00:00:00 2001 From: Michele Nasti Date: Mon, 27 May 2019 18:29:03 +0200 Subject: [PATCH 082/146] change in Aardvark adapter to handle additional data (#3821) * added property ex to rawBid * removed utils.getTopWindowUrl() --- modules/aardvarkBidAdapter.js | 6 +++- test/spec/modules/aardvarkBidAdapter_spec.js | 35 ++++++++++++++++---- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/modules/aardvarkBidAdapter.js b/modules/aardvarkBidAdapter.js index 49095b1cfc1..108e56ac06a 100644 --- a/modules/aardvarkBidAdapter.js +++ b/modules/aardvarkBidAdapter.js @@ -25,7 +25,7 @@ export const spec = { var auctionCodes = []; var requests = []; var requestsMap = {}; - var referer = utils.getTopWindowUrl(); + var referer = bidderRequest.refererInfo.referer; var pageCategories = []; // This reference to window.top can cause issues when loaded in an iframe if not protected with a try/catch. @@ -124,6 +124,10 @@ export const spec = { bidResponse.dealId = rawBid.dealId } + if (rawBid.hasOwnProperty('ex')) { + bidResponse.ex = rawBid.ex; + } + switch (rawBid.media) { case 'banner': bidResponse.ad = rawBid.adm + utils.createTrackPixelHtml(decodeURIComponent(rawBid.nurl)); diff --git a/test/spec/modules/aardvarkBidAdapter_spec.js b/test/spec/modules/aardvarkBidAdapter_spec.js index 628f19f8fd2..8ce4aa85561 100644 --- a/test/spec/modules/aardvarkBidAdapter_spec.js +++ b/test/spec/modules/aardvarkBidAdapter_spec.js @@ -54,21 +54,27 @@ describe('aardvarkAdapterTest', function () { auctionId: 'e97cafd0-ebfc-4f5c-b7c9-baa0fd335a4a' }]; + const bidderRequest = { + refererInfo: { + referer: 'http://example.com' + } + }; + it('should use HTTP GET method', function () { - const requests = spec.buildRequests(bidRequests); + const requests = spec.buildRequests(bidRequests, bidderRequest); requests.forEach(function(requestItem) { expect(requestItem.method).to.equal('GET'); }); }); it('should call the correct bidRequest url', function () { - const requests = spec.buildRequests(bidRequests); + const requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests.length).to.equal(1); expect(requests[0].url).to.match(new RegExp('^\/\/adzone.pub.com/xiby/TdAx_RAZd/aardvark\?')); }); it('should have correct data', function () { - const requests = spec.buildRequests(bidRequests); + const requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests.length).to.equal(1); expect(requests[0].data.version).to.equal(1); expect(requests[0].data.jsonp).to.equal(false); @@ -108,22 +114,28 @@ describe('aardvarkAdapterTest', function () { auctionId: 'e97cafd0-ebfc-4f5c-b7c9-baa0fd335a4a' }]; + const bidderRequest = { + refererInfo: { + referer: 'http://example.com' + } + }; + it('should use HTTP GET method', function () { - const requests = spec.buildRequests(bidRequests); + const requests = spec.buildRequests(bidRequests, bidderRequest); requests.forEach(function(requestItem) { expect(requestItem.method).to.equal('GET'); }); }); it('should call the correct bidRequest urls for each auction', function () { - const requests = spec.buildRequests(bidRequests); + const requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests[0].url).to.match(new RegExp('^\/\/bidder.rtk.io/Toby/TdAx/aardvark\?')); expect(requests[0].data.categories.length).to.equal(2); expect(requests[1].url).to.match(new RegExp('^\/\/adzone.pub.com/xiby/RAZd/aardvark\?')); }); it('should have correct data', function () { - const requests = spec.buildRequests(bidRequests); + const requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests.length).to.equal(2); expect(requests[0].data.version).to.equal(1); expect(requests[0].data.jsonp).to.equal(false); @@ -157,6 +169,9 @@ describe('aardvarkAdapterTest', function () { gdprConsent: { consentString: 'awefasdfwefasdfasd', gdprApplies: true + }, + refererInfo: { + referer: 'http://example.com' } }; @@ -184,7 +199,10 @@ describe('aardvarkAdapterTest', function () { }]; const bidderRequest = { - gdprConsent: undefined + gdprConsent: undefined, + refererInfo: { + referer: 'http://example.com' + } }; it('should transmit correct data', function () { @@ -219,6 +237,7 @@ describe('aardvarkAdapterTest', function () { cid: '1abgs362e0x48a8', adm: '', ttl: 200, + ex: 'extraproperty' } ], headers: {} @@ -234,6 +253,7 @@ describe('aardvarkAdapterTest', function () { expect(result[0].currency).to.equal('USD'); expect(result[0].ttl).to.equal(200); expect(result[0].dealId).to.equal('dealing'); + expect(result[0].ex).to.be.undefined; expect(result[0].ad).to.not.be.undefined; expect(result[1].requestId).to.equal('1abgs362e0x48a8'); @@ -243,6 +263,7 @@ describe('aardvarkAdapterTest', function () { expect(result[1].currency).to.equal('USD'); expect(result[1].ttl).to.equal(200); expect(result[1].ad).to.not.be.undefined; + expect(result[1].ex).to.equal('extraproperty'); }); it('should handle nobid responses', function () { From 96aae26eb2e31b65cd43e5533c15f00f4696220c Mon Sep 17 00:00:00 2001 From: Telaria Engineering <36203956+telariaEng@users.noreply.github.com> Date: Mon, 27 May 2019 09:35:29 -0700 Subject: [PATCH 083/146] Updated the bidder code in the test ad unit. (#3844) * - Updated the test ad unit to use 'telaria' as the bidder code. - Added an example URL. * using the bidder code constant --- modules/telariaBidAdapter.js | 2 +- modules/telariaBidAdapter.md | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/telariaBidAdapter.js b/modules/telariaBidAdapter.js index 12698f0ce56..0dd2e5e6edb 100644 --- a/modules/telariaBidAdapter.js +++ b/modules/telariaBidAdapter.js @@ -84,7 +84,7 @@ export const spec = { utils.logError(errorMessage); } else if (bidResult.seatbid && bidResult.seatbid.length > 0) { bidResult.seatbid[0].bid.forEach(tag => { - bids.push(createBid(STATUS.GOOD, bidderRequest, tag, width, height, bidResult.seatbid[0].seat.toLowerCase())); + bids.push(createBid(STATUS.GOOD, bidderRequest, tag, width, height, BIDDER_CODE)); }); } diff --git a/modules/telariaBidAdapter.md b/modules/telariaBidAdapter.md index 6a34a14a6b2..6a5e24e9a5e 100644 --- a/modules/telariaBidAdapter.md +++ b/modules/telariaBidAdapter.md @@ -23,7 +23,7 @@ Telaria bid adapter supports insteream Video. } }, bids: [{ - bidder: 'tremor', + bidder: 'telaria', params: { supplyCode: 'ssp-demo-rm6rh', adCode: 'ssp-!demo!-lufip', @@ -33,3 +33,5 @@ Telaria bid adapter supports insteream Video. } ``` +# Example: +https://console.telaria.com/examples/hb/headerbidding.jsp From d7af6dbaa666c69ac7fca3e196fb495d8dac94c8 Mon Sep 17 00:00:00 2001 From: Kamoris Date: Mon, 27 May 2019 18:46:10 +0200 Subject: [PATCH 084/146] Update rtbhouseBidAdapter.md (#3857) Explicit native.image.sizes in doc --- modules/rtbhouseBidAdapter.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/rtbhouseBidAdapter.md b/modules/rtbhouseBidAdapter.md index b8410fe8bb6..233ed34053a 100644 --- a/modules/rtbhouseBidAdapter.md +++ b/modules/rtbhouseBidAdapter.md @@ -39,7 +39,8 @@ Please reach out to pmp@rtbhouse.com to receive your own len: 25 }, image: { - required: true + required: true, + sizes: [300, 250] }, body: { required: true, From 3f1b73908884260dc5c71b86a85e853f1cca3ff5 Mon Sep 17 00:00:00 2001 From: dpapworth-qc <50959025+dpapworth-qc@users.noreply.github.com> Date: Mon, 27 May 2019 17:52:01 +0100 Subject: [PATCH 085/146] Added optional dealId parameter to bid response. (#3858) --- modules/quantcastBidAdapter.js | 6 +++++- test/spec/modules/quantcastBidAdapter_spec.js | 20 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/modules/quantcastBidAdapter.js b/modules/quantcastBidAdapter.js index 022122b5a2e..64cec7e231a 100644 --- a/modules/quantcastBidAdapter.js +++ b/modules/quantcastBidAdapter.js @@ -182,7 +182,7 @@ export const spec = { } const bidResponsesList = response.bids.map(bid => { - const { ad, cpm, width, height, creativeId, currency, videoUrl } = bid; + const { ad, cpm, width, height, creativeId, currency, videoUrl, dealId } = bid; const result = { requestId: response.requestId, @@ -201,6 +201,10 @@ export const spec = { result['mediaType'] = 'video'; } + if (dealId !== undefined && dealId) { + result['dealId'] = dealId; + } + return result; }); diff --git a/test/spec/modules/quantcastBidAdapter_spec.js b/test/spec/modules/quantcastBidAdapter_spec.js index 5a1a905f843..662641de17b 100644 --- a/test/spec/modules/quantcastBidAdapter_spec.js +++ b/test/spec/modules/quantcastBidAdapter_spec.js @@ -305,6 +305,26 @@ describe('Quantcast adapter', function () { expect(interpretedResponse[0]).to.deep.equal(expectedResponse); }); + it('should include dealId in bid response', function () { + response.body.bids[0].dealId = 'test-dealid'; + const expectedResponse = { + requestId: 'erlangcluster@qa-rtb002.us-ec.adtech.com-11417780270886458', + cpm: 4.5, + width: 300, + height: 250, + ad: + '
    Quantcast
    ', + ttl: QUANTCAST_TTL, + creativeId: 1001, + netRevenue: QUANTCAST_NET_REVENUE, + currency: 'USD', + dealId: 'test-dealid' + }; + const interpretedResponse = qcSpec.interpretResponse(response); + + expect(interpretedResponse[0]).to.deep.equal(expectedResponse); + }); + it('should get correct bid response for instream video', function() { const expectedResponse = { requestId: 'erlangcluster@qa-rtb002.us-ec.adtech.com-11417780270886458', From 0ee3cc638327e6f1926499bb3116a3b041283193 Mon Sep 17 00:00:00 2001 From: Dan Bogdan <43830380+EMXDigital@users.noreply.github.com> Date: Tue, 28 May 2019 11:58:42 -0400 Subject: [PATCH 086/146] adding Outstream mediaType to EMX Digital (#3840) * addressed feedback from #3731 ticket * removed commented code from emx test spec * logging removed from spec * flip h & w values from playerSize for video requests * adding Outstream mediaType to EMX Digital --- modules/emx_digitalBidAdapter.js | 62 +++++++++++++++---- modules/emx_digitalBidAdapter.md | 4 +- .../modules/emx_digitalBidAdapter_spec.js | 48 ++++++++++++-- 3 files changed, 96 insertions(+), 18 deletions(-) diff --git a/modules/emx_digitalBidAdapter.js b/modules/emx_digitalBidAdapter.js index 4bffd8e1e00..75ce47aae0a 100644 --- a/modules/emx_digitalBidAdapter.js +++ b/modules/emx_digitalBidAdapter.js @@ -1,11 +1,13 @@ -import * as utils from '../src/utils'; -import { registerBidder } from '../src/adapters/bidderFactory'; -import { BANNER, VIDEO } from '../src/mediaTypes'; -import { config } from '../src/config'; +import * as utils from 'src/utils'; +import { registerBidder } from 'src/adapters/bidderFactory'; +import { BANNER, VIDEO } from 'src/mediaTypes'; +import { config } from 'src/config'; +import { Renderer } from 'src/Renderer'; import includes from 'core-js/library/fn/array/includes'; const BIDDER_CODE = 'emx_digital'; const ENDPOINT = 'hb.emxdgt.com'; +const RENDERER_URL = '//js.brealtime.com/outstream/1.30.0/bundle.js'; export const emxAdapter = { validateSizes: (sizes) => { @@ -16,7 +18,7 @@ export const emxAdapter = { return sizes.every(size => utils.isArray(size) && size.length === 2); }, checkVideoContext: (bid) => { - return (bid && bid.mediaTypes && bid.mediaTypes.video && bid.mediaTypes.video.context && bid.mediaTypes.video.context === 'instream'); + return ((bid && bid.mediaTypes && bid.mediaTypes.video && bid.mediaTypes.video.context) && ((bid.mediaTypes.video.context === 'instream') || (bid.mediaTypes.video.context === 'outstream'))); }, buildBanner: (bid) => { let sizes = []; @@ -38,20 +40,56 @@ export const emxAdapter = { }, formatVideoResponse: (bidResponse, emxBid) => { bidResponse.vastXml = emxBid.adm; + if (!emxBid.renderer && (!emxBid.mediaTypes || !emxBid.mediaTypes.video || !emxBid.mediaTypes.video.context || emxBid.mediaTypes.video.context === 'outstream')) { + bidResponse.renderer = emxAdapter.createRenderer(bidResponse, { + id: emxBid.bidId, + url: RENDERER_URL + }); + } return bidResponse; }, - buildVideo: (bid) => { - bid.params.video.h = bid.mediaTypes.video.playerSize[0][1]; - bid.params.video.w = bid.mediaTypes.video.playerSize[0][0]; - return emxAdapter.cleanProtocols(bid.params.video); - }, cleanProtocols: (video) => { if (video.protocols && includes(video.protocols, 7)) { + // not supporting VAST protocol 7 (VAST 4.0); utils.logWarn(BIDDER_CODE + ': VAST 4.0 is currently not supported. This protocol has been filtered out of the request.'); video.protocols = video.protocols.filter(protocol => protocol !== 7); } return video; }, + outstreamRender: (bid) => { + bid.renderer.push(function () { + let params = (bid && bid.params && bid.params[0] && bid.params[0].video) ? bid.params[0].video : {}; + window.emxVideoQueue = window.emxVideoQueue || []; + window.queueEmxVideo({ + id: bid.adUnitCode, + adsResponses: bid.vastXml, + options: params + }); + if (window.emxVideoReady && window.videojs) { + window.emxVideoReady(); + } + }); + }, + createRenderer: (bid, rendererParams) => { + const renderer = Renderer.install({ + id: rendererParams.id, + url: RENDERER_URL, + loaded: false + }); + try { + renderer.setRender(emxAdapter.outstreamRender); + } catch (err) { + utils.logWarn('Prebid Error calling setRender on renderer', err); + } + + return renderer; + }, + buildVideo: (bid) => { + bid.params.video = bid.params.video || {}; + bid.params.video.h = bid.mediaTypes.video.playerSize[0][0]; + bid.params.video.w = bid.mediaTypes.video.playerSize[0][1]; + return emxAdapter.cleanProtocols(bid.params.video); + }, getGdpr: (bidRequests, emxData) => { if (bidRequests.gdprConsent) { emxData.regs = { @@ -100,7 +138,7 @@ export const spec = { } } else if (bid.mediaTypes && bid.mediaTypes.video) { if (!emxAdapter.checkVideoContext(bid)) { - utils.logWarn(BIDDER_CODE + ': Missing video context: instream'); + utils.logWarn(BIDDER_CODE + ': Missing video context: instream or outstream'); return false; } @@ -146,7 +184,7 @@ export const spec = { domain: window.top.document.location.host, page: page }, - version: '1.21.1' + version: '1.30.0' }; emxData = emxAdapter.getGdpr(bidderRequest, Object.assign({}, emxData)); diff --git a/modules/emx_digitalBidAdapter.md b/modules/emx_digitalBidAdapter.md index b0acdbcada8..03ba554c5ad 100644 --- a/modules/emx_digitalBidAdapter.md +++ b/modules/emx_digitalBidAdapter.md @@ -8,7 +8,7 @@ Maintainer: git@emxdigital.com # Description -The EMX Digital adapter provides publishers with access to the EMX Marketplace. The adapter is GDPR compliant. Please note that the adapter supports Banner and Video (Instream) media types only. +The EMX Digital adapter provides publishers with access to the EMX Marketplace. The adapter is GDPR compliant. Please note that the adapter supports Banner and Video (Instream & Outstream) media types. Note: The EMX Digital adapter requires approval and implementation guidelines from the EMX team, including existing publishers that work with EMX Digital. Please reach out to your account manager or prebid@emxdigital.com for more information. @@ -43,7 +43,7 @@ var adUnits = [{ code: 'video-div', mediaTypes: { video: { - context: 'instream', + context: 'instream', // also applicable for 'outstream' playerSize: [640, 480] } }, diff --git a/test/spec/modules/emx_digitalBidAdapter_spec.js b/test/spec/modules/emx_digitalBidAdapter_spec.js index dfb11ad82cd..170e5676f43 100644 --- a/test/spec/modules/emx_digitalBidAdapter_spec.js +++ b/test/spec/modules/emx_digitalBidAdapter_spec.js @@ -138,9 +138,32 @@ describe('emx_digital Adapter', function () { 'auctionId': '1d1a01234a475' }; + let outstreamBid = { + 'bidder': 'emx_digital', + 'params': { + 'tagid': '25251', + 'video': {} + }, + 'mediaTypes': { + 'video': { + 'context': 'outstream', + 'playerSize': [640, 480] + } + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250], + [300, 600] + ], + 'bidId': '30b31c2501de1e', + 'bidderRequestId': '22edbae3120bf6', + 'auctionId': '1d1a01234a475' + }; + it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); expect(spec.isBidRequestValid(noInstreamBid)).to.equal(false); + expect(spec.isBidRequestValid(outstreamBid)).to.equal(true); }); it('should contain tagid param', function () { @@ -283,8 +306,24 @@ describe('emx_digital Adapter', function () { let request = spec.buildRequests(bidRequestWithVideo, bidderRequest); const data = JSON.parse(request.data); expect(data.imp[0].video).to.exist.and.to.be.a('object'); - expect(data.imp[0].video.h).to.equal(bidRequestWithVideo[0].mediaTypes.video.playerSize[0][1]); - expect(data.imp[0].video.w).to.equal(bidRequestWithVideo[0].mediaTypes.video.playerSize[0][0]); + expect(data.imp[0].video.h).to.equal(bidRequestWithVideo[0].mediaTypes.video.playerSize[0][0]); + expect(data.imp[0].video.w).to.equal(bidRequestWithVideo[0].mediaTypes.video.playerSize[0][1]); + }); + + it('builds correctly formatted request video object for outstream', function () { + let bidRequestWithOutstreamVideo = utils.deepClone(bidderRequest.bids); + bidRequestWithOutstreamVideo[0].mediaTypes = { + video: { + context: 'outstream', + playerSize: [640, 480] + }, + }; + bidRequestWithOutstreamVideo[0].params.video = {}; + let request = spec.buildRequests(bidRequestWithOutstreamVideo, bidderRequest); + const data = JSON.parse(request.data); + expect(data.imp[0].video).to.exist.and.to.be.a('object'); + expect(data.imp[0].video.h).to.equal(bidRequestWithOutstreamVideo[0].mediaTypes.video.playerSize[0][0]); + expect(data.imp[0].video.w).to.equal(bidRequestWithOutstreamVideo[0].mediaTypes.video.playerSize[0][1]); }); it('shouldn\'t contain a user obj without GDPR information', function () { @@ -452,6 +491,7 @@ describe('emx_digital Adapter', function () { expect(ad1.vastXml).to.equal(serverResponse.seatbid[1].bid[0].adm); expect(ad1.ad).to.exist.and.to.be.a('string'); }); + it('handles nobid responses', function () { let serverResponse = { 'bids': [] @@ -464,10 +504,10 @@ describe('emx_digital Adapter', function () { }); }); - describe('getUserSyncs', function() { + describe('getUserSyncs', function () { let syncOptionsIframe = { iframeEnabled: true }; let syncOptionsPixel = { pixelEnabled: true }; - it('Should push the correct sync type depending on the config', function() { + it('Should push the correct sync type depending on the config', function () { let iframeSync = spec.getUserSyncs(syncOptionsIframe); expect(iframeSync.length).to.equal(1); expect(iframeSync[0].type).to.equal('iframe'); From 445df80dabbfcef9aff811ae7be9e3fa061d5cac Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Tue, 28 May 2019 13:01:18 -0700 Subject: [PATCH 087/146] Adding bidfloor to video imp req (#3863) --- modules/rubiconBidAdapter.js | 1 + test/spec/modules/rubiconBidAdapter_spec.js | 28 +++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index aeb6418eea8..dd1c815150e 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -134,6 +134,7 @@ export const spec = { }, tmax: config.getConfig('TTL') || 1000, imp: [{ + bidfloor: utils.deepAccess(bidRequest, 'params.floor') ? parseFloat(bidRequest.params.floor) : 0.0, exp: 300, id: bidRequest.adUnitCode, secure: isSecure() || bidRequest.params.secure ? 1 : 0, diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 33e5d04466a..0f29afe0069 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1143,6 +1143,34 @@ describe('the rubicon adapter', function () { expect(request.data.imp[0].video.pos).to.equal(1); }); + it('should send correct bidfloor to PBS', function() { + createVideoBidderRequest(); + + bidderRequest.bids[0].params.floor = 0.1; + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.imp[0].bidfloor).to.equal(0.1); + + bidderRequest.bids[0].params.floor = 5.5; + [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.imp[0].bidfloor).to.equal(5.5); + + bidderRequest.bids[0].params.floor = '1.7'; + [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.imp[0].bidfloor).to.equal(1.7); + + bidderRequest.bids[0].params.floor = 0; + [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.imp[0].bidfloor).to.equal(0.0); + + bidderRequest.bids[0].params.floor = undefined; + [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.imp[0].bidfloor).to.equal(0.0); + + bidderRequest.bids[0].params.floor = null; + [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + expect(request.data.imp[0].bidfloor).to.equal(0.0); + }); + it('should send request with proper ad position when mediaTypes.video.pos is not defined', function () { createVideoBidderRequest(); let positionBidderRequest = clone(bidderRequest); From c1f6ce41cb078c45c93d0bc9a2d276bbc3295abd Mon Sep 17 00:00:00 2001 From: rhythmonebhaines <49991465+rhythmonebhaines@users.noreply.github.com> Date: Tue, 28 May 2019 13:32:53 -0700 Subject: [PATCH 088/146] Rhythmone Adapter - Multiple ad size support, rewrite tests, update docs. (#3854) --- modules/rhythmoneBidAdapter.js | 60 +- modules/rhythmoneBidAdapter.md | 40 +- test/spec/modules/rhythmoneBidAdapter_spec.js | 759 +++++++++++++----- 3 files changed, 629 insertions(+), 230 deletions(-) diff --git a/modules/rhythmoneBidAdapter.js b/modules/rhythmoneBidAdapter.js index 29572c228fe..6e7a935c71c 100644 --- a/modules/rhythmoneBidAdapter.js +++ b/modules/rhythmoneBidAdapter.js @@ -15,12 +15,12 @@ function RhythmOneBidAdapter() { let SUPPORTED_VIDEO_API = [1, 2, 5]; let slotsToBids = {}; let that = this; - let version = '1.0.2.1'; + let version = '2.0.0.0'; let loadStart = Date.now(); var win = typeof window !== 'undefined' ? window : {}; this.isBidRequestValid = function (bid) { - return true; + return !!(bid.params && bid.params.placementId); }; this.getUserSyncs = function (syncOptions, responses, gdprConsent) { @@ -90,6 +90,7 @@ function RhythmOneBidAdapter() { impObj.id = BRs[i].adUnitCode; impObj.bidfloor = parseFloat(utils.deepAccess(BRs[i], 'params.floor')) || 0; impObj.secure = win.location.protocol === 'https:' ? 1 : 0; + if (utils.deepAccess(BRs[i], 'mediaTypes.banner') || utils.deepAccess(BRs[i], 'mediaType') === 'banner') { impObj.banner = frameBanner(BRs[i]); } @@ -139,21 +140,58 @@ function RhythmOneBidAdapter() { } } - function frameBanner(bid) { - var sizes = utils.parseSizesInput(bid.sizes).map(size => size.split('x')); - return { - w: parseInt(sizes[0][0]), - h: parseInt(sizes[0][1]) + function getValidSizeSet(dimensionList) { + let w = parseInt(dimensionList[0]); + let h = parseInt(dimensionList[1]); + // clever check for NaN + if (! (w !== w || h !== h)) { // eslint-disable-line + return [w, h]; + } + return false; + } + + function frameBanner(adUnit) { + // adUnit.sizes is scheduled to be deprecated, continue its support but prefer adUnit.mediaTypes.banner + var sizeList = adUnit.sizes; + if (adUnit.mediaTypes && adUnit.mediaTypes.banner) { + sizeList = adUnit.mediaTypes.banner.sizes; + } + var sizeStringList = utils.parseSizesInput(sizeList); + if (!Array.isArray(sizeStringList)) { + return {}; } + + var format = []; + sizeStringList.forEach(function(size) { + if (!size) { + return; + } + var dimensionList = getValidSizeSet(size.split('x')); + if (dimensionList) { + format.push({ + 'w': dimensionList[0], + 'h': dimensionList[1], + }); + } + }); + if (format.length) { + return { + 'format': format + }; + } + return {}; } function frameVideo(bid) { var size = []; if (utils.deepAccess(bid, 'mediaTypes.video.playerSize')) { + var dimensionSet = bid.mediaTypes.video.playerSize; if (utils.isArray(bid.mediaTypes.video.playerSize[0])) { - size = bid.mediaTypes.video.playerSize[0]; - } else if (utils.isNumber(bid.mediaTypes.video.playerSize[0])) { - size = bid.mediaTypes.video.playerSize; + dimensionSet = bid.mediaTypes.video.playerSize[0]; + } + var validSize = getValidSizeSet(dimensionSet) + if (validSize) { + size = validSize; } } return { @@ -172,7 +210,7 @@ function RhythmOneBidAdapter() { function frameExt(bid) { return { bidder: { - placementId: (bid.params && bid.params['placementId']) ? bid.params['placementId'] : '', + placementId: bid.params['placementId'], zone: (bid.params && bid.params['zone']) ? bid.params['zone'] : '1r', path: (bid.params && bid.params['path']) ? bid.params['path'] : 'mvo' } diff --git a/modules/rhythmoneBidAdapter.md b/modules/rhythmoneBidAdapter.md index ec3ee4852e5..df657ed2651 100644 --- a/modules/rhythmoneBidAdapter.md +++ b/modules/rhythmoneBidAdapter.md @@ -14,15 +14,41 @@ This module relays Prebid bids from Rhythm Exchange, RhythmOne's ad exchange. ```js const adUnits = [{ - code: 'uuddlrlrbass', - sizes: [ - [300, 250] - ], + code: 'adSlot-1', + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [300, 600] + ] + } + }, bids: [ { bidder: 'rhythmone', - params: - { + params: + { + placementId: '80184', // REQUIRED + zone: '1r', // OPTIONAL + path: 'mvo', // OPTIONAL + endpoint: "//tag.1rx.io/rmp/80184/0/mvo?z=1r" // OPTIONAL, only required for testing. this api guarantees no 204 responses + } + } + ] +}, +{ + code: 'adSlot-2', + mediaTypes: { + video: { + context: "instream", + playerSize: [640, 480] + } + }, + bids: [ + { + bidder: 'rhythmone', + params: + { placementId: '80184', // REQUIRED zone: '1r', // OPTIONAL path: 'mvo', // OPTIONAL @@ -31,4 +57,4 @@ const adUnits = [{ } ] }]; -``` \ No newline at end of file +``` diff --git a/test/spec/modules/rhythmoneBidAdapter_spec.js b/test/spec/modules/rhythmoneBidAdapter_spec.js index 98a7731c324..e909827b2df 100644 --- a/test/spec/modules/rhythmoneBidAdapter_spec.js +++ b/test/spec/modules/rhythmoneBidAdapter_spec.js @@ -1,64 +1,73 @@ import {spec} from '../../../modules/rhythmoneBidAdapter'; -var assert = require('assert'); +import * as utils from '../../../src/utils'; +import * as sinon from 'sinon'; + +var r1adapter = spec; describe('rhythmone adapter tests', function () { - describe('rhythmoneResponse', function () { - var z = spec; + beforeEach(function() { + this.defaultBidderRequest = { + 'refererInfo': { + 'referer': 'Reference Page' + } + }; + }); - var rmpBannerRequest = z.buildRequests( - [ + describe('Verify 1.0 POST Banner Bid Request', function () { + it('buildRequests works', function () { + var bidRequestList = [ { 'bidder': 'rhythmone', 'params': { - 'placementId': 'abc', - 'keywords': '', - 'categories': [], - 'trace': true, - 'zone': '2345', - 'path': 'mvo', - 'method': 'POST' + 'placementId': 'myplacement', + 'zone': 'myzone', + 'path': 'mypath' }, 'mediaType': 'banner', 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'sizes': [[300, 250]] + 'sizes': [[300, 250]], + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' } - ], { 'refererInfo': { 'referer': 'Reference Page' } } - ); - - it('Verify POST Banner Bid Request', function () { - expect(rmpBannerRequest.url).to.have.string('//tag.1rx.io/rmp/abc/0/mvo?z=2345&hbv='); - expect(rmpBannerRequest.method).to.equal('POST'); - const bidRequest = JSON.parse(rmpBannerRequest.data); - expect(bidRequest.site).to.not.equal(null); - expect(bidRequest.site.ref).to.equal('Reference Page'); - expect(bidRequest.device).to.not.equal(null); - expect(bidRequest.device.ua).to.equal(navigator.userAgent); - expect(bidRequest.device).to.have.property('dnt'); - expect(bidRequest.imp[0].banner).to.not.equal(null); - expect(bidRequest.imp[0].banner.w).to.equal(300); - expect(bidRequest.imp[0].banner.h).to.equal(250); - expect(bidRequest.imp[0].ext.bidder.zone).to.equal('2345'); - expect(bidRequest.imp[0].ext.bidder.path).to.equal('mvo'); - }); + ]; - var bannerBids = z.interpretResponse({ - body: [ - { - 'impid': 'div-gpt-ad-1438287399331-0', - 'w': 300, - 'h': 250, - 'adm': '
    My ad4 with cpm of a4ab3485f434f74f
    ', - 'price': 1, - 'crid': 'cr-cfy24' - } - ] - }); + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - it('should register one bid', function() { - assert.equal(bannerBids.length, 1); + expect(bidRequest.url).to.have.string('//tag.1rx.io/rmp/myplacement/0/mypath?z=myzone&hbv='); + expect(bidRequest.method).to.equal('POST'); + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.site).to.not.equal(null); + expect(openrtbRequest.site.ref).to.equal('Reference Page'); + expect(openrtbRequest.device).to.not.equal(null); + expect(openrtbRequest.device.ua).to.equal(navigator.userAgent); + expect(openrtbRequest.device.dnt).to.equal(0); + expect(openrtbRequest.imp[0].banner).to.not.equal(null); + expect(openrtbRequest.imp[0].banner.format[0].w).to.equal(300); + expect(openrtbRequest.imp[0].banner.format[0].h).to.equal(250); + expect(openrtbRequest.imp[0].ext.bidder.zone).to.equal('myzone'); + expect(openrtbRequest.imp[0].ext.bidder.path).to.equal('mypath'); }); - it('Verify parse banner response', function() { + it('interpretResponse works', function() { + var bidList = { + 'body': [ + { + 'impid': 'div-gpt-ad-1438287399331-0', + 'w': 300, + 'h': 250, + 'adm': '
    My Compelling Ad
    ', + 'price': 1, + 'crid': 'cr-cfy24' + } + ] + }; + + var bannerBids = r1adapter.interpretResponse(bidList); + + expect(bannerBids.length).to.equal(1); const bid = bannerBids[0]; expect(bid.width).to.equal(300); expect(bid.height).to.equal(250); @@ -68,70 +77,80 @@ describe('rhythmone adapter tests', function () { expect(bid.cpm).to.equal(1.0); expect(bid.ttl).to.equal(350); }); + }); - var rmpVideoRequest = z.buildRequests( - [ + describe('Verify POST Video Bid Request', function() { + it('buildRequests works', function () { + var bidRequestList = [ { 'bidder': 'rhythmone', 'params': { - 'placementId': 'xyz', - 'keywords': '', - 'categories': [], - 'trace': true, - 'method': 'POST' + 'placementId': 'myplacement', + 'zone': 'myzone', + 'path': 'mypath' }, 'mediaTypes': { 'video': { - 'playerSize': [[640, 480]], + 'playerSize': [ + [640, 480] + ], 'context': 'instream' } }, - 'placementCode': 'div-gpt-ad-1438287399331-1', - 'sizes': [[300, 250]] + 'adUnitCode': 'div-gpt-ad-1438287399331-1', + 'sizes': [ + [300, 250] + ], + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' } - ], { 'refererInfo': { 'referer': 'Reference Page' } } - ); - - it('Verify POST Video Bid Request', function () { - expect(rmpVideoRequest.url).to.have.string('//tag.1rx.io/rmp/xyz/0/mvo?z=1r&hbv='); - expect(rmpVideoRequest.method).to.equal('POST'); - const bidRequest = JSON.parse(rmpVideoRequest.data); - expect(bidRequest.site).to.not.equal(null); - expect(bidRequest.device).to.not.equal(null); - expect(bidRequest.device.ua).to.equal(navigator.userAgent); - expect(bidRequest.device).to.have.property('dnt'); - expect(bidRequest.imp[0].video).to.not.equal(null); - expect(bidRequest.imp[0].video.w).to.equal(640); - expect(bidRequest.imp[0].video.h).to.equal(480); - expect(bidRequest.imp[0].video.mimes[0]).to.equal('video/mp4'); - expect(bidRequest.imp[0].video.protocols).to.eql([2, 3, 5, 6]); - expect(bidRequest.imp[0].video.startdelay).to.equal(0); - expect(bidRequest.imp[0].video.skip).to.equal(0); - expect(bidRequest.imp[0].video.playbackmethod).to.eql([1, 2, 3, 4]); - expect(bidRequest.imp[0].video.delivery[0]).to.equal(1); - expect(bidRequest.imp[0].video.api).to.eql([1, 2, 5]); - }); + ]; - var videoBids = z.interpretResponse({ - body: [ - { - 'impid': 'div-gpt-ad-1438287399331-1', - 'price': 1, - 'nurl': 'http://testdomain/rmp/placementid/0/path?reqId=1636037', - 'adomain': ['test.com'], - 'cid': '467415', - 'crid': 'cr-vid', - 'w': 800, - 'h': 600 - } - ] - }); + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - it('should register one bid', function() { - assert.equal(videoBids.length, 1); + expect(bidRequest.url).to.have.string('//tag.1rx.io/rmp/myplacement/0/mypath?z=myzone&hbv='); + expect(bidRequest.method).to.equal('POST'); + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.site).to.not.equal(null); + expect(openrtbRequest.device).to.not.equal(null); + expect(openrtbRequest.device.ua).to.equal(navigator.userAgent); + expect(openrtbRequest.device).to.have.property('dnt'); + expect(openrtbRequest.imp[0].video).to.not.equal(null); + expect(openrtbRequest.imp[0].video.w).to.equal(640); + expect(openrtbRequest.imp[0].video.h).to.equal(480); + expect(openrtbRequest.imp[0].video.mimes[0]).to.equal('video/mp4'); + expect(openrtbRequest.imp[0].video.protocols).to.eql([2, 3, 5, 6]); + expect(openrtbRequest.imp[0].video.startdelay).to.equal(0); + expect(openrtbRequest.imp[0].video.skip).to.equal(0); + expect(openrtbRequest.imp[0].video.playbackmethod).to.eql([1, 2, 3, 4]); + expect(openrtbRequest.imp[0].video.delivery[0]).to.equal(1); + expect(openrtbRequest.imp[0].video.api).to.eql([1, 2, 5]); }); - it('Verify parse video response', function() { + it('interpretResponse works', function() { + var bidList = { + 'body': [ + { + 'impid': 'div-gpt-ad-1438287399331-1', + 'price': 1, + 'nurl': 'http://testdomain/rmp/placementid/0/path?reqId=1636037', + 'adomain': [ + 'test.com' + ], + 'cid': '467415', + 'crid': 'cr-vid', + 'w': 800, + 'h': 600 + } + ] + }; + + var videoBids = r1adapter.interpretResponse(bidList); + + expect(videoBids.length).to.equal(1); const bid = videoBids[0]; expect(bid.width).to.equal(800); expect(bid.height).to.equal(600); @@ -143,47 +162,23 @@ describe('rhythmone adapter tests', function () { expect(bid.cpm).to.equal(1.0); expect(bid.ttl).to.equal(600); }); + }); - it('should send GDPR Consent data to RhythmOne tag', function () { - let _consentString = 'testConsentString'; - var request = z.buildRequests( - [ - { - 'bidder': 'rhythmone', - 'params': { - 'placementId': 'xyz', - 'keywords': '', - 'categories': [], - 'trace': true, - 'method': 'POST' - }, - 'adUnitCode': 'div-gpt-ad-1438287399331-3', - 'sizes': [[300, 250]] - } - ], {'gdprConsent': {'gdprApplies': true, 'consentString': _consentString}, 'refererInfo': { 'referer': 'Reference Page' }} - ); - const bidRequest = JSON.parse(request.data); - expect(bidRequest.user.ext.consent).to.equal(_consentString); - expect(bidRequest.regs.ext.gdpr).to.equal(true); - }); - - var rmpMultiFormatRequest = z.buildRequests( - [ + describe('Verify Multi-Format ads and Multiple Size Bid Request', function() { + it('buildRequests works', function () { + var bidRequestList = [ { 'bidder': 'rhythmone', 'params': { - 'placementId': 'xyz', - 'keywords': '', - 'categories': [], - 'trace': true, - 'zone': '2345', - 'path': 'mvo', - 'method': 'POST' + 'placementId': 'myplacement', + 'zone': 'myzone', + 'path': 'mypath', }, 'mediaTypes': { 'banner': { 'sizes': [ - [300, 250] + [300, 250], + [300, 600] ] }, 'video': { @@ -192,58 +187,69 @@ describe('rhythmone adapter tests', function () { } }, 'adUnitCode': 'div-gpt-ad-1438287399331-5', - 'sizes': [[300, 250]] + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' } - ], { 'refererInfo': { 'referer': 'Reference Page' } } - ); - - it('Verify Multi-Format ads Bid Request', function () { - const bidRequest = JSON.parse(rmpMultiFormatRequest.data); - expect(bidRequest.site).to.not.equal(null); - expect(bidRequest.site.ref).to.equal('Reference Page'); - expect(bidRequest.device).to.not.equal(null); - expect(bidRequest.device.ua).to.equal(navigator.userAgent); - expect(bidRequest.device).to.have.property('dnt'); - expect(bidRequest.imp[0].video).to.not.equal(null); - expect(bidRequest.imp[0].video.w).to.equal(640); - expect(bidRequest.imp[0].video.h).to.equal(480); - expect(bidRequest.imp[0].video.mimes[0]).to.equal('video/mp4'); - expect(bidRequest.imp[0].video.protocols).to.eql([2, 3, 5, 6]); - expect(bidRequest.imp[0].video.startdelay).to.equal(0); - expect(bidRequest.imp[0].video.skip).to.equal(0); - expect(bidRequest.imp[0].video.playbackmethod).to.eql([1, 2, 3, 4]); - expect(bidRequest.imp[0].video.delivery[0]).to.equal(1); - expect(bidRequest.imp[0].video.api).to.eql([1, 2, 5]); - expect(bidRequest.imp[0].banner).to.not.equal(null); - expect(bidRequest.imp[0].banner.w).to.equal(300); - expect(bidRequest.imp[0].banner.h).to.equal(250); - expect(bidRequest.imp[0].ext.bidder.zone).to.equal('2345'); - expect(bidRequest.imp[0].ext.bidder.path).to.equal('mvo'); - }); + ]; - var forRMPMultiFormatResponse = z.interpretResponse({ - body: { - 'id': '1e810245dd1779', - 'seatbid': [ { - 'bid': [ { - 'impid': 'div-gpt-ad-1438287399331-5', - 'price': 1, - 'nurl': 'http://testdomain/rmp/placementid/0/path?reqId=1636037', - 'adomain': ['test.com'], - 'cid': '467415', - 'crid': 'cr-vid', - 'w': 800, - 'h': 600 - } ] - } ] - } - }); + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); - it('should register one bid', function() { - assert.equal(forRMPMultiFormatResponse.length, 1); + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.site).to.not.equal(null); + expect(openrtbRequest.site.ref).to.equal('Reference Page'); + expect(openrtbRequest.device).to.not.equal(null); + expect(openrtbRequest.device.ua).to.equal(navigator.userAgent); + expect(openrtbRequest.device).to.have.property('dnt'); + expect(openrtbRequest.imp[0].video).to.not.equal(null); + expect(openrtbRequest.imp[0].video.w).to.equal(640); + expect(openrtbRequest.imp[0].video.h).to.equal(480); + expect(openrtbRequest.imp[0].video.mimes[0]).to.equal('video/mp4'); + expect(openrtbRequest.imp[0].video.protocols).to.eql([2, 3, 5, 6]); + expect(openrtbRequest.imp[0].video.startdelay).to.equal(0); + expect(openrtbRequest.imp[0].video.skip).to.equal(0); + expect(openrtbRequest.imp[0].video.playbackmethod).to.eql([1, 2, 3, 4]); + expect(openrtbRequest.imp[0].video.delivery[0]).to.equal(1); + expect(openrtbRequest.imp[0].video.api).to.eql([1, 2, 5]); + expect(openrtbRequest.imp[0].banner).to.not.equal(null); + expect(openrtbRequest.imp[0].banner.format[0].w).to.equal(300); + expect(openrtbRequest.imp[0].banner.format[0].h).to.equal(250); + expect(openrtbRequest.imp[0].banner.format[1].w).to.equal(300); + expect(openrtbRequest.imp[0].banner.format[1].h).to.equal(600); + expect(openrtbRequest.imp[0].ext.bidder.zone).to.equal('myzone'); + expect(openrtbRequest.imp[0].ext.bidder.path).to.equal('mypath'); }); - it('Verify parse for multi format ad response', function() { + it('interpretResponse works', function() { + var bidList = { + 'body': { + 'id': '1e810245dd1779', + 'seatbid': [ + { + 'bid': [ + { + 'impid': 'div-gpt-ad-1438287399331-5', + 'price': 1, + 'nurl': 'http://testdomain/rmp/placementid/0/path?reqId=1636037', + 'adomain': [ + 'test.com' + ], + 'cid': '467415', + 'crid': 'cr-vid', + 'w': 800, + 'h': 600 + } + ] + } + ] + } + }; + + var forRMPMultiFormatResponse = r1adapter.interpretResponse(bidList); + + expect(forRMPMultiFormatResponse.length).to.equal(1); const bid = forRMPMultiFormatResponse[0]; expect(bid.width).to.equal(800); expect(bid.height).to.equal(600); @@ -255,65 +261,394 @@ describe('rhythmone adapter tests', function () { expect(bid.cpm).to.equal(1.0); expect(bid.ttl).to.equal(600); }); + }); + + describe('misc buildRequests', function() { + it('should send GDPR Consent data to RhythmOne tag', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + 'zone': 'myzone', + 'path': 'mypath' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-3', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var consentString = 'testConsentString'; + var gdprBidderRequest = this.defaultBidderRequest; + gdprBidderRequest.gdprConsent = { + 'gdprApplies': true, + 'consentString': consentString + }; + + var bidRequest = r1adapter.buildRequests(bidRequestList, gdprBidderRequest); + + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.user.ext.consent).to.equal(consentString); + expect(openrtbRequest.regs.ext.gdpr).to.equal(true); + }); + + it('prefer 2.0 sizes', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + 'zone': 'myzone', + 'path': 'mypath' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 600]] + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'sizes': [[300, 250]], + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; - var noBidResponse = z.interpretResponse({ - body: '' + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.imp[0].banner.format[0].w).to.equal(300); + expect(openrtbRequest.imp[0].banner.format[0].h).to.equal(600); }); - it('No bid response', function() { - assert.equal(noBidResponse.length, 0); + it('survives size misconfiguration', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + 'zone': 'myzone', + 'path': 'mypath' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300]] + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.imp[0].banner.format).to.be.undefined; + }); + + it('dnt is correctly set to 1', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 600]] + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var dntStub = sinon.stub(utils, 'getDNT').returns(1); + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + dntStub.restore(); + + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.device.dnt).to.equal(1); }); - describe('isRequiredParamPresent', function () { - var rmpBannerRequest = z.buildRequests( - [ - { - 'bidder': 'rhythmone', - 'params': { - 'keywords': '', - 'categories': [], - 'trace': true, - 'zone': '2345', - 'path': 'mvo', - 'method': 'POST' - }, - 'mediaType': 'banner', - 'adUnitCode': 'div-gpt-ad-1438287399331-0', - 'sizes': [[300, 250]] - } - ], { 'refererInfo': { 'referer': 'Reference Page' } } - ); - it('should return empty when required params not found', function () { - expect(rmpBannerRequest).to.be.empty; + it('sets floor', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + 'floor': 100.0 + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 600]] + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.imp[0].bidfloor).to.equal(100.0); + }); + + it('support for correct video size definition', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + }, + 'mediaTypes': { + 'video': { + 'playerSize': [640, 480], + 'context': 'instream' + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-1', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.imp[0].video.w).to.equal(640); + expect(openrtbRequest.imp[0].video.h).to.equal(480); + }); + + it('supports string video sizes', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + }, + 'mediaTypes': { + 'video': { + 'context': 'instream', + 'playerSize': ['600', '300'] + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-1', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.imp[0].video.w).to.equal(600); + expect(openrtbRequest.imp[0].video.h).to.equal(300); + }); + + it('rejects bad video sizes', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + }, + 'mediaTypes': { + 'video': { + 'context': 'instream', + 'playerSize': ['badWidth', 'badHeight'] + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-1', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.imp[0].video.w).to.be.undefined; + expect(openrtbRequest.imp[0].video.h).to.be.undefined; + }); + + it('supports missing video size', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement', + }, + 'mediaTypes': { + 'video': { + 'context': 'instream' + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-1', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.imp[0].video.w).to.be.undefined; + expect(openrtbRequest.imp[0].video.h).to.be.undefined; + }); + + it('uses default zone and path', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'placementId': 'myplacement' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [300, 600] + ] + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + const openrtbRequest = JSON.parse(bidRequest.data); + expect(openrtbRequest.imp[0].ext.bidder.zone).to.equal('1r'); + expect(openrtbRequest.imp[0].ext.bidder.path).to.equal('mvo'); + }); + + it('should return empty when required params not found', function () { + var bidRequestList = [ + { + 'bidder': 'rhythmone', + 'params': { + 'zone': 'myzone', + 'path': 'mypath' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [300, 250] + ] + } + }, + 'adUnitCode': 'div-gpt-ad-1438287399331-3', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1, + 'bidId': '51ef8751f9aead' + } + ]; + + var bidRequest = r1adapter.buildRequests(bidRequestList, this.defaultBidderRequest); + + expect(bidRequest).to.be.empty; + }); + }); + + describe('misc interpretResponse', function () { + it('No bid response', function() { + var noBidResponse = r1adapter.interpretResponse({ + 'body': '' }); + expect(noBidResponse.length).to.equal(0); }); }); - describe('auditBeacon', function() { - var z = spec; - var beaconURL = z.getUserSyncs({pixelEnabled: true})[0]; + describe('auditBeacon', function() { it('should contain the correct path', function() { - var u = '//hbevents.1rx.io/audit?'; - assert.equal(beaconURL.url.substring(0, u.length), u); + var syncList = r1adapter.getUserSyncs({pixelEnabled: true}); + expect(syncList.length).to.equal(1); + var syncData = syncList[0]; + var expectedURL = '//hbevents.1rx.io/audit?'; + assert.equal(syncData.url.substring(0, expectedURL.length), expectedURL); }); - beaconURL = z.getUserSyncs({pixelEnabled: true}, null, {'gdprApplies': true, 'consentString': 'testConsentString'})[0]; it('should send GDPR Consent data to Sync pixel', function () { - expect(beaconURL.url).to.have.string('&gdpr=true&gdpr_consent=testConsentString'); + var syncList = r1adapter.getUserSyncs({pixelEnabled: true}, null, {'gdprApplies': true, 'consentString': 'testConsentString'}); + expect(syncList.length).to.equal(1); + var syncData = syncList[0]; + expect(syncData.url).to.have.string('&gdpr=true&gdpr_consent=testConsentString'); + }); + + it('should not return anything when pixelEnabled is false', function () { + var syncList = r1adapter.getUserSyncs({pixelEnabled: false}, null, {'gdprApplies': true, 'consentString': 'testConsentString'}); + expect(syncList).to.be.undefined; }); }); + describe('isBidRequestValid', function () { - let bid = { + var bid = { 'bidder': 'rhythmone', 'params': { - 'placementId': '469127' + 'placementId': 'myplacement', + 'path': 'mypath', + 'zone': 'myzone' }, - 'adUnitCode': 'bannerDiv', - 'sizes': [[300, 250]] + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'adUnitCode': 'bannerDiv' }; it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); + expect(r1adapter.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when placementId missing', function () { + delete bid.params.placementId; + expect(r1adapter.isBidRequestValid(bid)).to.equal(false); }); }); }); From dc3134ce1cec7d0461fb95a2e44647226a768a7e Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Tue, 28 May 2019 17:17:31 -0400 Subject: [PATCH 089/146] minor updates to consentManagement tests (#3849) --- integrationExamples/gpt/gdpr_hello_world.html | 10 ++++-- modules/consentManagement.js | 4 +-- test/spec/modules/consentManagement_spec.js | 32 +++++++++---------- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/integrationExamples/gpt/gdpr_hello_world.html b/integrationExamples/gpt/gdpr_hello_world.html index 084310b57d2..84efb5b7596 100644 --- a/integrationExamples/gpt/gdpr_hello_world.html +++ b/integrationExamples/gpt/gdpr_hello_world.html @@ -82,13 +82,17 @@ var adUnits = [{ code: 'div-gpt-ad-1460505748561-0', - sizes: [[300, 250], [300,600]], + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]] + } + }, // Replace this object to test a new Adapter! bids: [{ - bidder: 'appnexusAst', + bidder: 'appnexus', params: { - placementId: '10433394' + placementId: 13144370 } }] diff --git a/modules/consentManagement.js b/modules/consentManagement.js index 94a3b13e383..1e2a6648145 100644 --- a/modules/consentManagement.js +++ b/modules/consentManagement.js @@ -342,7 +342,7 @@ export function resetConsentData() { * A configuration function that initializes some module variables, as well as add a hook into the requestBids function * @param {object} config required; consentManagement module config settings; cmp (string), timeout (int), allowAuctionWithoutConsent (boolean) */ -export function setConfig(config) { +export function setConsentConfig(config) { if (utils.isStr(config.cmpApi)) { userCMP = config.cmpApi; } else { @@ -379,4 +379,4 @@ export function setConfig(config) { } addedConsentHook = true; } -config.getConfig('consentManagement', config => setConfig(config.consentManagement)); +config.getConfig('consentManagement', config => setConsentConfig(config.consentManagement)); diff --git a/test/spec/modules/consentManagement_spec.js b/test/spec/modules/consentManagement_spec.js index 40c96c38eb0..6be96427750 100644 --- a/test/spec/modules/consentManagement_spec.js +++ b/test/spec/modules/consentManagement_spec.js @@ -1,4 +1,4 @@ -import {setConfig, requestBidsHook, resetConsentData, userCMP, consentTimeout, allowAuction, staticConsentData} from 'modules/consentManagement'; +import {setConsentConfig, requestBidsHook, resetConsentData, userCMP, consentTimeout, allowAuction, staticConsentData} from 'modules/consentManagement'; import {gdprDataHandler} from 'src/adapterManager'; import * as utils from 'src/utils'; import { config } from 'src/config'; @@ -7,8 +7,8 @@ let assert = require('chai').assert; let expect = require('chai').expect; describe('consentManagement', function () { - describe('setConfig tests:', function () { - describe('empty setConfig value', function () { + describe('setConsentConfig tests:', function () { + describe('empty setConsentConfig value', function () { beforeEach(function () { sinon.stub(utils, 'logInfo'); }); @@ -19,7 +19,7 @@ describe('consentManagement', function () { }); it('should use system default values', function () { - setConfig({}); + setConsentConfig({}); expect(userCMP).to.be.equal('iab'); expect(consentTimeout).to.be.equal(10000); expect(allowAuction).to.be.true; @@ -27,7 +27,7 @@ describe('consentManagement', function () { }); }); - describe('valid setConfig value', function () { + describe('valid setConsentConfig value', function () { afterEach(function () { config.resetConfig(); $$PREBID_GLOBAL$$.requestBids.removeAll(); @@ -39,14 +39,14 @@ describe('consentManagement', function () { allowAuctionWithoutConsent: false }; - setConfig(allConfig); + setConsentConfig(allConfig); expect(userCMP).to.be.equal('iab'); expect(consentTimeout).to.be.equal(7500); expect(allowAuction).to.be.false; }); }); - describe('static consent string setConfig value', () => { + describe('static consent string setConsentConfig value', () => { afterEach(() => { config.resetConfig(); $$PREBID_GLOBAL$$.requestBids.removeAll(); @@ -447,7 +447,7 @@ describe('consentManagement', function () { } }; - setConfig(staticConfig); + setConsentConfig(staticConfig); expect(userCMP).to.be.equal('static'); expect(consentTimeout).to.be.equal(0); // should always return without a timeout when config is used expect(allowAuction).to.be.false; @@ -495,7 +495,7 @@ describe('consentManagement', function () { let badCMPConfig = { cmpApi: 'bad' }; - setConfig(badCMPConfig); + setConsentConfig(badCMPConfig); expect(userCMP).to.be.equal(badCMPConfig.cmpApi); requestBidsHook(() => { @@ -508,7 +508,7 @@ describe('consentManagement', function () { }); it('should throw proper errors when CMP is not found', function () { - setConfig(goodConfigWithCancelAuction); + setConsentConfig(goodConfigWithCancelAuction); requestBidsHook(() => { didHookReturn = true; @@ -546,7 +546,7 @@ describe('consentManagement', function () { cmpStub = sinon.stub(window, '__cmp').callsFake((...args) => { args[2](testConsentData); }); - setConfig(goodConfigWithAllowAuction); + setConsentConfig(goodConfigWithAllowAuction); requestBidsHook(() => {}, {}); cmpStub.restore(); @@ -610,7 +610,7 @@ describe('consentManagement', function () { args[2](testConsentData.data.msgName, testConsentData.data); }); - setConfig(goodConfigWithAllowAuction); + setConsentConfig(goodConfigWithAllowAuction); requestBidsHook(() => { didHookReturn = true; }, {adUnits: [{ sizes: [[300, 250]] }]}); @@ -684,7 +684,7 @@ describe('consentManagement', function () { function testIFramedPage(testName, messageFormatString) { it(`should return the consent string from a postmessage + addEventListener response - ${testName}`, (done) => { stringifyResponse = messageFormatString; - setConfig(goodConfigWithAllowAuction); + setConsentConfig(goodConfigWithAllowAuction); requestBidsHook(() => { let consent = gdprDataHandler.getConsentData(); sinon.assert.notCalled(utils.logWarn); @@ -726,7 +726,7 @@ describe('consentManagement', function () { args[2](testConsentData); }); - setConfig(goodConfigWithAllowAuction); + setConsentConfig(goodConfigWithAllowAuction); requestBidsHook(() => { didHookReturn = true; @@ -748,7 +748,7 @@ describe('consentManagement', function () { args[2](testConsentData); }); - setConfig(goodConfigWithCancelAuction); + setConsentConfig(goodConfigWithCancelAuction); requestBidsHook(() => { didHookReturn = true; @@ -768,7 +768,7 @@ describe('consentManagement', function () { args[2](testConsentData); }); - setConfig(goodConfigWithAllowAuction); + setConsentConfig(goodConfigWithAllowAuction); requestBidsHook(() => { didHookReturn = true; From e88dec19d31f2fe0b63823f3a68118718f01adec Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Tue, 28 May 2019 19:33:55 -0400 Subject: [PATCH 090/146] auction key limiter feature (#3825) * auction key limiter feature - initial commit * Updated config name --- src/targeting.js | 91 ++++++++++++++- test/spec/unit/core/targeting_spec.js | 160 +++++++++++++++++++++++++- 2 files changed, 248 insertions(+), 3 deletions(-) diff --git a/src/targeting.js b/src/targeting.js index 4ac993cf94a..a8c989bdf37 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -1,4 +1,4 @@ -import { uniques, isGptPubadsDefined, getHighestCpm, getOldestHighestCpmBid, groupBy, isAdUnitCodeMatchingSlot, timestamp, deepAccess } from './utils'; +import { uniques, isGptPubadsDefined, getHighestCpm, getOldestHighestCpmBid, groupBy, isAdUnitCodeMatchingSlot, timestamp, deepAccess, deepClone, logError, logWarn, logInfo } from './utils'; import { config } from './config'; import { NATIVE_TARGETING_KEYS } from './native'; import { auctionManager } from './auctionManager'; @@ -43,6 +43,40 @@ export function getHighestCpmBidsFromBidPool(bidsReceived, highestCpmCallback) { return bids; } +/** +* A descending sort function that will sort the list of objects based on the following two dimensions: +* - bids with a deal are sorted before bids w/o a deal +* - then sort bids in each grouping based on the hb_pb value +* eg: the following list of bids would be sorted like: +* [{ +* "hb_adid": "vwx", +* "hb_pb": "28", +* "hb_deal": "7747" +* }, { +* "hb_adid": "jkl", +* "hb_pb": "10", +* "hb_deal": "9234" +* }, { +* "hb_adid": "stu", +* "hb_pb": "50" +* }, { +* "hb_adid": "def", +* "hb_pb": "2" +* }] +*/ +export function sortByDealAndPriceBucket(a, b) { + if (a.adUnitTargeting.hb_deal !== undefined && b.adUnitTargeting.hb_deal === undefined) { + return -1; + } + + if ((a.adUnitTargeting.hb_deal === undefined && b.adUnitTargeting.hb_deal !== undefined)) { + return 1; + } + + // assuming both values either have a deal or don't have a deal - sort by the hb_pb param + return b.adUnitTargeting.hb_pb - a.adUnitTargeting.hb_pb; +} + /** * @typedef {Object.} targeting * @property {string} targeting_key @@ -122,8 +156,13 @@ export function newTargeting(auctionManager) { targeting = flattenTargeting(targeting); - // make sure at least there is a entry per adUnit code in the targetingSet so receivers of SET_TARGETING call's can know what ad units are being invoked + const auctionKeysThreshold = config.getConfig('targetingControls.auctionKeyMaxChars'); + if (auctionKeysThreshold) { + logInfo(`Detected 'targetingControls.auctionKeyMaxChars' was active for this auction; set with a limit of ${auctionKeysThreshold} characters. Running checks on auction keys...`); + targeting = filterTargetingKeys(targeting, auctionKeysThreshold); + } + // make sure at least there is a entry per adUnit code in the targetingSet so receivers of SET_TARGETING call's can know what ad units are being invoked adUnitCodes.forEach(code => { if (!targeting[code]) { targeting[code] = {}; @@ -133,6 +172,54 @@ export function newTargeting(auctionManager) { return targeting; }; + // create an encoded string variant based on the keypairs of the provided object + // - note this will encode the characters between the keys (ie = and &) + function convertKeysToQueryForm(keyMap) { + return Object.keys(keyMap).reduce(function (queryString, key) { + let encodedKeyPair = `${key}%3d${encodeURIComponent(keyMap[key])}%26`; + return queryString += encodedKeyPair; + }, ''); + } + + function filterTargetingKeys(targeting, auctionKeysThreshold) { + // read each targeting.adUnit object and sort the adUnits into a list of adUnitCodes based on priorization setting (eg CPM) + let targetingCopy = deepClone(targeting); + + let targetingMap = Object.keys(targetingCopy).map(adUnitCode => { + return { + adUnitCode, + adUnitTargeting: targetingCopy[adUnitCode] + }; + }).sort(sortByDealAndPriceBucket); + + // iterate through the targeting based on above list and transform the keys into the query-equivalent and count characters + return targetingMap.reduce(function (accMap, currMap, index, arr) { + let adUnitQueryString = convertKeysToQueryForm(currMap.adUnitTargeting); + + // for the last adUnit - trim last encoded ampersand from the converted query string + if ((index + 1) === arr.length) { + adUnitQueryString = adUnitQueryString.slice(0, -3); + } + + // if under running threshold add to result + let code = currMap.adUnitCode; + let querySize = adUnitQueryString.length; + if (querySize <= auctionKeysThreshold) { + auctionKeysThreshold -= querySize; + logInfo(`AdUnit '${code}' auction keys comprised of ${querySize} characters. Deducted from running threshold; new limit is ${auctionKeysThreshold}`, targetingCopy[code]); + + accMap[code] = targetingCopy[code]; + } else { + logWarn(`The following keys for adUnitCode '${code}' exceeded the current limit of the 'auctionKeyMaxChars' setting.\nThe key-set size was ${querySize}, the current allotted amount was ${auctionKeysThreshold}.\n`, targetingCopy[code]); + } + + if ((index + 1) === arr.length && Object.keys(accMap).length === 0) { + logError('No auction targeting keys were permitted due to the setting in setConfig(targetingControls.auctionKeyMaxChars). Please review setup and consider adjusting.'); + } + return accMap; + }, {}); + } + /** * Converts targeting array and flattens to make it easily iteratable * e.g: Sample input to this function diff --git a/test/spec/unit/core/targeting_spec.js b/test/spec/unit/core/targeting_spec.js index 727c790c991..bc5958f0495 100644 --- a/test/spec/unit/core/targeting_spec.js +++ b/test/spec/unit/core/targeting_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { targeting as targetingInstance, filters } from 'src/targeting'; +import { targeting as targetingInstance, filters, sortByDealAndPriceBucket } from 'src/targeting'; import { config } from 'src/config'; import { getAdUnits, createBidReceived } from 'test/fixtures/fixtures'; import CONSTANTS from 'src/constants.json'; @@ -128,6 +128,8 @@ describe('targeting tests', function () { let amBidsReceivedStub; let amGetAdUnitsStub; let bidExpiryStub; + let logWarnStub; + let logErrorStub; let bidsReceived; beforeEach(function () { @@ -140,6 +142,14 @@ describe('targeting tests', function () { return ['/123456/header-bid-tag-0']; }); bidExpiryStub = sandbox.stub(filters, 'isBidNotExpired').returns(true); + logWarnStub = sinon.stub(utils, 'logWarn'); + logErrorStub = sinon.stub(utils, 'logError'); + }); + + afterEach(function() { + config.resetConfig(); + logWarnStub.restore(); + logErrorStub.restore(); }); describe('when hb_deal is present in bid.adserverTargeting', function () { @@ -165,6 +175,34 @@ describe('targeting tests', function () { }); }); + it('will enforce a limit on the number of auction keys when auctionKeyMaxChars setting is active', function () { + config.setConfig({ + targetingControls: { + auctionKeyMaxChars: 150 + } + }); + + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0', '/123456/header-bid-tag-1']); + expect(targeting['/123456/header-bid-tag-1']).to.deep.equal({}); + expect(targeting['/123456/header-bid-tag-0']).to.contain.keys('hb_pb', 'hb_adid', 'hb_bidder', 'hb_deal'); + expect(targeting['/123456/header-bid-tag-0']['hb_adid']).to.equal(bid1.adId); + expect(logWarnStub.calledOnce).to.be.true; + }); + + it('will return an error when auctionKeyMaxChars setting is set too low for any auction keys to be allowed', function () { + config.setConfig({ + targetingControls: { + auctionKeyMaxChars: 50 + } + }); + + const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0', '/123456/header-bid-tag-1']); + expect(targeting['/123456/header-bid-tag-1']).to.deep.equal({}); + expect(targeting['/123456/header-bid-tag-0']).to.deep.equal({}); + expect(logWarnStub.calledTwice).to.be.true; + expect(logErrorStub.calledOnce).to.be.true; + }); + it('selects the top bid when enableSendAllBids true', function () { enableSendAllBids = true; let targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); @@ -327,6 +365,126 @@ describe('targeting tests', function () { }); }); + describe('sortByDealAndPriceBucket', function() { + it('will properly sort bids when some bids have deals and some do not', function () { + let bids = [{ + adUnitTargeting: { + hb_adid: 'abc', + hb_pb: '1.00', + hb_deal: '1234' + } + }, { + adUnitTargeting: { + hb_adid: 'def', + hb_pb: '0.50', + } + }, { + adUnitTargeting: { + hb_adid: 'ghi', + hb_pb: '20.00', + hb_deal: '4532' + } + }, { + adUnitTargeting: { + hb_adid: 'jkl', + hb_pb: '9.00', + hb_deal: '9864' + } + }, { + adUnitTargeting: { + hb_adid: 'mno', + hb_pb: '50.00', + } + }, { + adUnitTargeting: { + hb_adid: 'pqr', + hb_pb: '100.00', + } + }]; + bids.sort(sortByDealAndPriceBucket); + expect(bids[0].adUnitTargeting.hb_adid).to.equal('ghi'); + expect(bids[1].adUnitTargeting.hb_adid).to.equal('jkl'); + expect(bids[2].adUnitTargeting.hb_adid).to.equal('abc'); + expect(bids[3].adUnitTargeting.hb_adid).to.equal('pqr'); + expect(bids[4].adUnitTargeting.hb_adid).to.equal('mno'); + expect(bids[5].adUnitTargeting.hb_adid).to.equal('def'); + }); + + it('will properly sort bids when all bids have deals', function () { + let bids = [{ + adUnitTargeting: { + hb_adid: 'abc', + hb_pb: '1.00', + hb_deal: '1234' + } + }, { + adUnitTargeting: { + hb_adid: 'def', + hb_pb: '0.50', + hb_deal: '4321' + } + }, { + adUnitTargeting: { + hb_adid: 'ghi', + hb_pb: '2.50', + hb_deal: '4532' + } + }, { + adUnitTargeting: { + hb_adid: 'jkl', + hb_pb: '2.00', + hb_deal: '9864' + } + }]; + bids.sort(sortByDealAndPriceBucket); + expect(bids[0].adUnitTargeting.hb_adid).to.equal('ghi'); + expect(bids[1].adUnitTargeting.hb_adid).to.equal('jkl'); + expect(bids[2].adUnitTargeting.hb_adid).to.equal('abc'); + expect(bids[3].adUnitTargeting.hb_adid).to.equal('def'); + }); + + it('will properly sort bids when no bids have deals', function () { + let bids = [{ + adUnitTargeting: { + hb_adid: 'abc', + hb_pb: '1.00' + } + }, { + adUnitTargeting: { + hb_adid: 'def', + hb_pb: '0.10' + } + }, { + adUnitTargeting: { + hb_adid: 'ghi', + hb_pb: '10.00' + } + }, { + adUnitTargeting: { + hb_adid: 'jkl', + hb_pb: '10.01' + } + }, { + adUnitTargeting: { + hb_adid: 'mno', + hb_pb: '1.00' + } + }, { + adUnitTargeting: { + hb_adid: 'pqr', + hb_pb: '100.00' + } + }]; + bids.sort(sortByDealAndPriceBucket); + expect(bids[0].adUnitTargeting.hb_adid).to.equal('pqr'); + expect(bids[1].adUnitTargeting.hb_adid).to.equal('jkl'); + expect(bids[2].adUnitTargeting.hb_adid).to.equal('ghi'); + expect(bids[3].adUnitTargeting.hb_adid).to.equal('abc'); + expect(bids[4].adUnitTargeting.hb_adid).to.equal('mno'); + expect(bids[5].adUnitTargeting.hb_adid).to.equal('def'); + }); + }); + describe('setTargetingForAst', function () { let sandbox, apnTagStub; From a2f8500f14989beeffa2ecfd8405e5275d74dc75 Mon Sep 17 00:00:00 2001 From: Aleksa Trajkovic Date: Wed, 29 May 2019 05:02:00 +0200 Subject: [PATCH 091/146] aardvark tdid support (#3860) * aardvark tdid support * increase aardvark test coverage --- modules/aardvarkBidAdapter.js | 9 +++ test/spec/modules/aardvarkBidAdapter_spec.js | 70 ++++++++++++++++++-- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/modules/aardvarkBidAdapter.js b/modules/aardvarkBidAdapter.js index 108e56ac06a..239800ce7fa 100644 --- a/modules/aardvarkBidAdapter.js +++ b/modules/aardvarkBidAdapter.js @@ -27,6 +27,7 @@ export const spec = { var requestsMap = {}; var referer = bidderRequest.refererInfo.referer; var pageCategories = []; + var tdId = ''; // This reference to window.top can cause issues when loaded in an iframe if not protected with a try/catch. try { @@ -35,6 +36,10 @@ export const spec = { } } catch (e) {} + if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.tdid'))) { + tdId = validBidRequests[0].userId.tdid; + } + utils._each(validBidRequests, function(b) { var rMap = requestsMap[b.params.ai]; if (!rMap) { @@ -48,6 +53,10 @@ export const spec = { endpoint: DEFAULT_ENDPOINT }; + if (tdId) { + rMap.payload.tdid = tdId; + } + if (pageCategories && pageCategories.length) { rMap.payload.categories = pageCategories.slice(0); } diff --git a/test/spec/modules/aardvarkBidAdapter_spec.js b/test/spec/modules/aardvarkBidAdapter_spec.js index 8ce4aa85561..727527acf29 100644 --- a/test/spec/modules/aardvarkBidAdapter_spec.js +++ b/test/spec/modules/aardvarkBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec } from 'modules/aardvarkBidAdapter'; +import { spec, resetUserSync } from 'modules/aardvarkBidAdapter'; describe('aardvarkAdapterTest', function () { describe('forming valid bidRequests', function () { @@ -37,7 +37,8 @@ describe('aardvarkAdapterTest', function () { sizes: [300, 250], bidId: '1abgs362e0x48a8', bidderRequestId: '70deaff71c281d', - auctionId: '5c66da22-426a-4bac-b153-77360bef5337' + auctionId: '5c66da22-426a-4bac-b153-77360bef5337', + userId: { tdid: 'eff98622-b5fd-44fa-9a49-6e846922d532' } }, { bidder: 'aardvark', @@ -62,7 +63,7 @@ describe('aardvarkAdapterTest', function () { it('should use HTTP GET method', function () { const requests = spec.buildRequests(bidRequests, bidderRequest); - requests.forEach(function(requestItem) { + requests.forEach(function (requestItem) { expect(requestItem.method).to.equal('GET'); }); }); @@ -82,6 +83,13 @@ describe('aardvarkAdapterTest', function () { expect(requests[0].data.rtkreferer).to.not.be.undefined; expect(requests[0].data.RAZd).to.equal('22aidtbx5eabd9'); }); + + it('should have tdid, it is available in bidRequest', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + requests.forEach(function (requestItem) { + expect(requestItem.data.tdid).to.equal('eff98622-b5fd-44fa-9a49-6e846922d532'); + }); + }); }); describe('splitting multi-auction ad units into own requests', function () { @@ -122,7 +130,7 @@ describe('aardvarkAdapterTest', function () { it('should use HTTP GET method', function () { const requests = spec.buildRequests(bidRequests, bidderRequest); - requests.forEach(function(requestItem) { + requests.forEach(function (requestItem) { expect(requestItem.method).to.equal('GET'); }); }); @@ -148,6 +156,13 @@ describe('aardvarkAdapterTest', function () { expect(requests[1].data.rtkreferer).to.not.be.undefined; expect(requests[1].data.RAZd).to.equal('22aidtbx5eabd9'); }); + + it('should have no tdid, it is not available in bidRequest', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + requests.forEach(function (requestItem) { + expect(requestItem.data.tdid).to.be.undefined; + }); + }); }); describe('GDPR conformity', function () { @@ -281,4 +296,51 @@ describe('aardvarkAdapterTest', function () { expect(result.length).to.equal(0); }); }); + + describe('getUserSyncs', function () { + const syncOptions = { + iframeEnabled: true + }; + + it('should produce sync url', function () { + const syncs = spec.getUserSyncs(syncOptions); + expect(syncs.length).to.equal(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.equal('//sync.rtk.io/cs'); + }); + + it('should return empty, as we sync only once', function () { + const syncs = spec.getUserSyncs(syncOptions); + expect(syncs.length).to.equal(0); + }); + + it('should reset hasSynced flag, allowing another sync', function () { + resetUserSync(); + + const syncs = spec.getUserSyncs(syncOptions); + expect(syncs.length).to.equal(1); + }); + + it('should return empty when iframe disallowed', function () { + resetUserSync(); + + const noIframeOptions = { iframeEnabled: false }; + const syncs = spec.getUserSyncs(noIframeOptions); + expect(syncs.length).to.equal(0); + }); + + it('should produce sync url with gdpr params', function () { + const gdprConsent = { + gdprApplies: true, + consentString: 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA' + }; + + resetUserSync(); + + const syncs = spec.getUserSyncs(syncOptions, null, gdprConsent); + expect(syncs.length).to.equal(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.equal('//sync.rtk.io/cs?g=1&c=BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA'); + }); + }); }); From bec741de7174f6b3371635f9fc3a9a66c0eed220 Mon Sep 17 00:00:00 2001 From: Robert Ray Martinez III Date: Wed, 29 May 2019 08:53:50 -0700 Subject: [PATCH 092/146] We want to remove bidfloor if not set by pb (#3866) --- modules/rubiconBidAdapter.js | 5 ++++- test/spec/modules/rubiconBidAdapter_spec.js | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index dd1c815150e..48ce6ae9648 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -134,7 +134,6 @@ export const spec = { }, tmax: config.getConfig('TTL') || 1000, imp: [{ - bidfloor: utils.deepAccess(bidRequest, 'params.floor') ? parseFloat(bidRequest.params.floor) : 0.0, exp: 300, id: bidRequest.adUnitCode, secure: isSecure() || bidRequest.params.secure ? 1 : 0, @@ -159,6 +158,10 @@ export const spec = { } } } + const bidFloor = parseFloat(utils.deepAccess(bidRequest, 'params.floor')); + if (!isNaN(bidFloor)) { + data.imp[0].bidfloor = bidFloor; + } // if value is set, will overwrite with same value data.imp[0].ext.rubicon.video.size_id = determineRubiconVideoSizeId(bidRequest) diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 0f29afe0069..67a92d4a26e 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1160,15 +1160,15 @@ describe('the rubicon adapter', function () { bidderRequest.bids[0].params.floor = 0; [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - expect(request.data.imp[0].bidfloor).to.equal(0.0); + expect(request.data.imp[0].bidfloor).to.equal(0); bidderRequest.bids[0].params.floor = undefined; [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - expect(request.data.imp[0].bidfloor).to.equal(0.0); + expect(request.data.imp[0]).to.not.haveOwnProperty('bidfloor'); bidderRequest.bids[0].params.floor = null; [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - expect(request.data.imp[0].bidfloor).to.equal(0.0); + expect(request.data.imp[0]).to.not.haveOwnProperty('bidfloor'); }); it('should send request with proper ad position when mediaTypes.video.pos is not defined', function () { From efd5ed614f0c554ec3261cbaf4a701670ab06d37 Mon Sep 17 00:00:00 2001 From: Gaudeamus Date: Wed, 29 May 2019 19:08:34 +0300 Subject: [PATCH 093/146] mgid adapter: add support of currency.adServerCurrency (#3850) * native support & minor changes * native support & minor changes * increase test coverage * fix win price value * fix win price value tests * fix alias, fix bidfloor * remove alias * use currency.adServerCurrency * update version --- modules/mgidBidAdapter.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/mgidBidAdapter.js b/modules/mgidBidAdapter.js index b7759ea1e0a..e1b15ef4b51 100644 --- a/modules/mgidBidAdapter.js +++ b/modules/mgidBidAdapter.js @@ -2,6 +2,7 @@ import {registerBidder} from 'src/adapters/bidderFactory'; import * as utils from '../src/utils'; import * as urlUtils from '../src/url'; import {BANNER, NATIVE} from 'src/mediaTypes'; +import {config} from '../src/config'; const DEFAULT_CUR = 'USD'; const BIDDER_CODE = 'mgid'; const ENDPOINT_URL = 'https://prebid.mgid.com/prebid/'; @@ -59,7 +60,7 @@ utils._each(NATIVE_ASSETS, anAsset => { _NATIVE_ASSET_ID_TO_KEY_MAP[anAsset.ID] utils._each(NATIVE_ASSETS, anAsset => { _NATIVE_ASSET_KEY_TO_ASSET_MAP[anAsset.KEY] = anAsset }); export const spec = { - VERSION: '1.1', + VERSION: '1.2', code: BIDDER_CODE, supportedMediaTypes: [BANNER, NATIVE], reId: /^[0-9]+$/, @@ -126,6 +127,7 @@ export const spec = { if (utils.isStr(muid) && muid.length > 0) { url += '?muid=' + muid; } + const cur = [config.getConfig('currency.adServerCurrency') || setOnAny(validBidRequests, 'params.currency') || setOnAny(validBidRequests, 'params.cur') || DEFAULT_CUR]; const page = utils.deepAccess(bidderRequest, 'refererInfo.canonicalUrl') || referer; const secure = window.location.protocol === 'https:' ? 1 : 0; let imp = []; @@ -177,7 +179,7 @@ export const spec = { let request = { id: utils.deepAccess(bidderRequest, 'bidderRequestId'), site: { domain, page }, - cur: [setOnAny(validBidRequests, 'params.currency') || setOnAny(validBidRequests, 'params.cur') || DEFAULT_CUR], + cur: cur, device: { ua: navigator.userAgent, js: 1, From 7094e082cf40aec76fba2c1dd7deda2a401aa906 Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Wed, 29 May 2019 11:44:55 -0700 Subject: [PATCH 094/146] Prebid 2.17.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ab415a03ea0..cda4e7d6b0a 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.17.0-pre", + "version": "2.17.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From dc8f6d50acf1d8af796b49ad2e423d5e02d447fd Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Wed, 29 May 2019 12:05:34 -0700 Subject: [PATCH 095/146] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cda4e7d6b0a..e84e1698434 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.17.0", + "version": "2.18.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 582ecdf89b21cc4b78d9059151a344f89ebf8e78 Mon Sep 17 00:00:00 2001 From: adxcgcom <31470944+adxcgcom@users.noreply.github.com> Date: Thu, 30 May 2019 17:11:07 +0000 Subject: [PATCH 096/146] adxcgBidAdapter - added pubcid (#3824) * adxcgBidAdapter - added pubcid * mini fix * Update adxcgBidAdapter Added minimal change to restart CI process * added passing tdid, updated pubcid * unit test for userid support - pubcid, tdid * Update adxcgBidAdapter.js just to restart CircleCI * Update adxcgBidAdapter.js just to restart CircleCI --- modules/adxcgBidAdapter.js | 9 ++++ test/spec/modules/adxcgBidAdapter_spec.js | 50 +++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/modules/adxcgBidAdapter.js b/modules/adxcgBidAdapter.js index 23808fa3be7..34b5ea25fb0 100644 --- a/modules/adxcgBidAdapter.js +++ b/modules/adxcgBidAdapter.js @@ -11,6 +11,7 @@ import includes from 'core-js/library/fn/array/includes' * updated for gdpr compliance on 2018.05.22 -requires gdpr compliance module * updated to pass aditional auction and impression level parameters. added pass for video targeting parameters * updated to fix native support for image width/height and icon 2019.03.17 + * updated support for userid - pubcid,ttid 2019.05.28 */ const BIDDER_CODE = 'adxcg' @@ -159,6 +160,14 @@ export const spec = { beaconParams.prebidBidIds = prebidBidIds.join(',') beaconParams.bidfloors = bidfloors.join(',') + if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.pubcid'))) { + beaconParams.pubcid = validBidRequests[0].userId.pubcid; + } + + if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.tdid'))) { + beaconParams.tdid = validBidRequests[0].userId.tdid; + } + let adxcgRequestUrl = url.format({ protocol: secure ? 'https' : 'http', hostname: secure ? 'hbps.adxcg.net' : 'hbp.adxcg.net', diff --git a/test/spec/modules/adxcgBidAdapter_spec.js b/test/spec/modules/adxcgBidAdapter_spec.js index 0277e0ab964..5bac9523b18 100644 --- a/test/spec/modules/adxcgBidAdapter_spec.js +++ b/test/spec/modules/adxcgBidAdapter_spec.js @@ -133,6 +133,56 @@ describe('AdxcgAdapter', function () { }) }) + describe('userid pubcid should be passed to querystring', function () { + let bid = [{ + 'bidder': 'adxcg', + 'params': { + 'adzoneid': '1' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [640, 360], [1, 1]], + 'bidId': '84ab500420319d', + 'bidderRequestId': '7101db09af0db2', + 'auctionId': '1d1a030790a475', + }] + + let bidderRequests = {}; + + bid[0].userId = {'pubcid': 'pubcidabcd'}; + + it('should send pubcid if available', function () { + let request = spec.buildRequests(bid, bidderRequests) + let parsedRequestUrl = url.parse(request.url) + let query = parsedRequestUrl.search + expect(query.pubcid).to.equal('pubcidabcd') + }) + }) + + describe('userid tdid should be passed to querystring', function () { + let bid = [{ + 'bidder': 'adxcg', + 'params': { + 'adzoneid': '1' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [640, 360], [1, 1]], + 'bidId': '84ab500420319d', + 'bidderRequestId': '7101db09af0db2', + 'auctionId': '1d1a030790a475', + }] + + let bidderRequests = {}; + + bid[0].userId = {'tdid': 'tdidabcd'}; + + it('should send pubcid if available', function () { + let request = spec.buildRequests(bid, bidderRequests) + let parsedRequestUrl = url.parse(request.url) + let query = parsedRequestUrl.search + expect(query.tdid).to.equal('tdidabcd'); + }) + }) + describe('response handler', function () { let BIDDER_REQUEST = { 'bidder': 'adxcg', From 7aa0e0d4a0bd36351d5d095c5d7556c4dc0b346b Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Mon, 3 Jun 2019 08:29:00 -0700 Subject: [PATCH 097/146] getCpmInNewCurrency to use current value of bid.cpm and bid.currency (#3845) * getCpmInNewCurrency to use current value of bid.cpm and bid.currency * added a test case for boosted bid, this test fails with old code --- modules/currency.js | 5 +---- test/spec/modules/currency_spec.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/modules/currency.js b/modules/currency.js index 17c38b17a98..a3e2c223072 100644 --- a/modules/currency.js +++ b/modules/currency.js @@ -180,12 +180,9 @@ export function addBidResponseHook(fn, adUnitCode, bid) { bid.currency = 'USD'; } - let fromCurrency = bid.currency; - let cpm = bid.cpm; - // used for analytics bid.getCpmInNewCurrency = function(toCurrency) { - return (parseFloat(cpm) * getCurrencyConversion(fromCurrency, toCurrency)).toFixed(3); + return (parseFloat(this.cpm) * getCurrencyConversion(this.currency, toCurrency)).toFixed(3); }; // execute immediately if the bid is already in the desired currency diff --git a/test/spec/modules/currency_spec.js b/test/spec/modules/currency_spec.js index 9fb32102749..98f51b72251 100644 --- a/test/spec/modules/currency_spec.js +++ b/test/spec/modules/currency_spec.js @@ -191,6 +191,34 @@ describe('currency', function () { expect(innerBid.getCpmInNewCurrency('JPY')).to.equal('100.000'); }); + it('uses rates specified in json when provided and consider boosted bid', function () { + setConfig({ + adServerCurrency: 'USD', + rates: { + USD: { + JPY: 100 + } + } + }); + + var bid = { cpm: 100, currency: 'JPY', bidder: 'rubicon' }; + var innerBid; + + addBidResponseHook(function(adCodeId, bid) { + innerBid = bid; + }, 'elementId', bid); + + expect(innerBid.cpm).to.equal('1.0000'); + expect(typeof innerBid.getCpmInNewCurrency).to.equal('function'); + expect(innerBid.getCpmInNewCurrency('JPY')).to.equal('100.000'); + + // Boosting the bid now + innerBid.cpm *= 10; + expect(innerBid.cpm).to.equal(10.0000); + expect(typeof innerBid.getCpmInNewCurrency).to.equal('function'); + expect(innerBid.getCpmInNewCurrency('JPY')).to.equal('1000.000'); + }); + it('uses default rates when currency file fails to load', function () { setConfig({}); From ac658124afeba062b41cb07b81921f6ac1878d50 Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Mon, 3 Jun 2019 08:36:21 -0700 Subject: [PATCH 098/146] always adding originalCpm and originalCurrency to bid object (#3856) --- modules/currency.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/currency.js b/modules/currency.js index a3e2c223072..ae2f9ac1f1b 100644 --- a/modules/currency.js +++ b/modules/currency.js @@ -185,6 +185,9 @@ export function addBidResponseHook(fn, adUnitCode, bid) { return (parseFloat(this.cpm) * getCurrencyConversion(this.currency, toCurrency)).toFixed(3); }; + bid.originalCpm = bid.cpm; + bid.originalCurrency = bid.currency; + // execute immediately if the bid is already in the desired currency if (bid.currency === adServerCurrency) { return fn.call(this, adUnitCode, bid); @@ -209,8 +212,6 @@ function wrapFunction(fn, context, params) { let fromCurrency = bid.currency; try { let conversion = getCurrencyConversion(fromCurrency); - bid.originalCpm = bid.cpm; - bid.originalCurrency = bid.currency; if (conversion !== 1) { bid.cpm = (parseFloat(bid.cpm) * conversion).toFixed(4); bid.currency = adServerCurrency; From db167c0ff28f9a9b07ba94783161a61366df2aac Mon Sep 17 00:00:00 2001 From: "Antoine Jacquemin (Rubicon)" Date: Tue, 4 Jun 2019 05:13:13 +0800 Subject: [PATCH 099/146] new size Rubicon (#3877) add size 288 (640x380) --- modules/rubiconBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 48ce6ae9648..a0e9c89a221 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -81,7 +81,8 @@ var sizeMap = { 229: '320x180', 232: '580x400', 257: '400x600', - 265: '1920x1080' + 265: '1920x1080', + 288: '640x380' }; utils._each(sizeMap, (item, key) => sizeMap[item] = key); From 2f208f86d620b3a83bdf75e5943c41075e2d58bd Mon Sep 17 00:00:00 2001 From: AdmixerTech <35560933+AdmixerTech@users.noreply.github.com> Date: Tue, 4 Jun 2019 17:38:35 +0300 Subject: [PATCH 100/146] BIDDER_CODE check removed (#3862) --- modules/admixerBidAdapter.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/admixerBidAdapter.js b/modules/admixerBidAdapter.js index ee224dc6a5c..c6d6dd34a11 100644 --- a/modules/admixerBidAdapter.js +++ b/modules/admixerBidAdapter.js @@ -29,9 +29,7 @@ export const spec = { referrer: encodeURIComponent(utils.getTopWindowUrl()), }; bidderRequest.forEach((bid) => { - if (bid.bidder === BIDDER_CODE || ALIASES.indexOf(bid.bidder) > -1) { - payload.imps.push(bid); - } + payload.imps.push(bid); }); const payloadString = JSON.stringify(payload); return { From 2a10388dc44f5cc792bed72a2d3e3cfae07cd769 Mon Sep 17 00:00:00 2001 From: Arne Schulz Date: Tue, 4 Jun 2019 17:16:02 +0200 Subject: [PATCH 101/146] Bugfix add bid parameters if not present (#3808) * initial orbidder version in personal github repo * use adUnits from orbidder_example.html * replace obsolete functions * forgot to commit the test * check if bidderRequest object is available * try to fix weird safari/ie issue * ebayK: add more params * update orbidderBidAdapter.md * use spec. instead of this. for consistency reasons * add bidfloor parameter to params object * fix gdpr object handling * default to consentRequired: false when not explicitly given * wip - use onSetTargeting callback * add tests for onSetTargeting callback * fix params and respective tests --- modules/orbidderBidAdapter.js | 5 ++--- test/spec/modules/orbidderBidAdapter_spec.js | 14 +++++++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/modules/orbidderBidAdapter.js b/modules/orbidderBidAdapter.js index 1123cc5d50e..e085a14c6b8 100644 --- a/modules/orbidderBidAdapter.js +++ b/modules/orbidderBidAdapter.js @@ -82,10 +82,9 @@ export const spec = { const getRefererInfo = detectReferer(window); bid.pageUrl = getRefererInfo().referer; - if (spec.bidParams[bid.adId]) { - bid.params = spec.bidParams[bid.adId]; + if (spec.bidParams[bid.requestId] && (typeof bid.params === 'undefined')) { + bid.params = [spec.bidParams[bid.requestId]]; } - spec.ajaxCall(`${spec.orbidderHost}${route}`, JSON.stringify(bid)); }, diff --git a/test/spec/modules/orbidderBidAdapter_spec.js b/test/spec/modules/orbidderBidAdapter_spec.js index 3818f502901..55f5e2cae4c 100644 --- a/test/spec/modules/orbidderBidAdapter_spec.js +++ b/test/spec/modules/orbidderBidAdapter_spec.js @@ -2,6 +2,7 @@ import {expect} from 'chai'; import {spec} from 'modules/orbidderBidAdapter'; import {newBidder} from 'src/adapters/bidderFactory'; import openxAdapter from '../../../modules/openxAnalyticsAdapter'; +import {detectReferer} from 'src/refererDetection'; describe('orbidderBidAdapter', () => { const adapter = newBidder(spec); @@ -153,9 +154,16 @@ describe('orbidderBidAdapter', () => { adId: 'testId', test: 1, pageUrl: 'www.someurl.de', - referrer: 'www.somereferrer.de' + referrer: 'www.somereferrer.de', + requestId: '123req456' }; + spec.bidParams['123req456'] = {'accountId': '123acc456'}; + + let bidObjClone = deepClone(bidObj); + bidObjClone.pageUrl = detectReferer(window)().referer; + bidObjClone.params = [{'accountId': '123acc456'}]; + beforeEach(() => { ajaxStub = sinon.stub(spec, 'ajaxCall'); }); @@ -169,13 +177,13 @@ describe('orbidderBidAdapter', () => { expect(ajaxStub.calledOnce).to.equal(true); expect(ajaxStub.firstCall.args[0].indexOf('https://')).to.equal(0); expect(ajaxStub.firstCall.args[0]).to.equal(`${spec.orbidderHost}/win`); - expect(ajaxStub.firstCall.args[1]).to.equal(JSON.stringify(bidObj)); + expect(ajaxStub.firstCall.args[1]).to.equal(JSON.stringify(bidObjClone)); spec.onSetTargeting(bidObj); expect(ajaxStub.calledTwice).to.equal(true); expect(ajaxStub.secondCall.args[0].indexOf('https://')).to.equal(0); expect(ajaxStub.secondCall.args[0]).to.equal(`${spec.orbidderHost}/targeting`); - expect(ajaxStub.secondCall.args[1]).to.equal(JSON.stringify(bidObj)); + expect(ajaxStub.secondCall.args[1]).to.equal(JSON.stringify(bidObjClone)); }); }); From 1f9937e1f4503f414f192466d43789409f83e4db Mon Sep 17 00:00:00 2001 From: guiann Date: Wed, 5 Jun 2019 16:23:02 +0200 Subject: [PATCH 102/146] Remove useless bidderCode in bid response (#3864) --- modules/adyoulikeBidAdapter.js | 1 - test/spec/modules/adyoulikeBidAdapter_spec.js | 5 ----- 2 files changed, 6 deletions(-) diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 4624bdba8b5..fd7a1697bac 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -187,7 +187,6 @@ function createBid(response) { return { requestId: response.BidID, - bidderCode: spec.code, width: response.Width, height: response.Height, ad: response.Ad, diff --git a/test/spec/modules/adyoulikeBidAdapter_spec.js b/test/spec/modules/adyoulikeBidAdapter_spec.js index 3f28acaaf97..7edb9416b03 100644 --- a/test/spec/modules/adyoulikeBidAdapter_spec.js +++ b/test/spec/modules/adyoulikeBidAdapter_spec.js @@ -7,7 +7,6 @@ import { newBidder } from 'src/adapters/bidderFactory'; describe('Adyoulike Adapter', function () { const canonicalUrl = 'http://canonical.url/?t=%26'; const defaultDC = 'hb-api'; - const bidderCode = 'adyoulike'; const bidRequestWithEmptyPlacement = [ { 'bidId': 'bid_id_0', @@ -18,7 +17,6 @@ describe('Adyoulike Adapter', function () { } ]; const bidRequestWithEmptySizes = { - 'bidderCode': 'adyoulike', 'bids': [ { 'bidId': 'bid_id_0', @@ -193,7 +191,6 @@ describe('Adyoulike Adapter', function () { it('should add gdpr consent information to the request', function () { let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; let bidderRequest = { - 'bidderCode': 'adyoulike', 'auctionId': '1d1a030790a475', 'bidderRequestId': '22edbae2733bf6', 'timeout': 3000, @@ -306,13 +303,11 @@ describe('Adyoulike Adapter', function () { expect(result.length).to.equal(2); - expect(result[0].bidderCode).to.equal(bidderCode); expect(result[0].cpm).to.equal(0.5); expect(result[0].ad).to.equal('placement_0'); expect(result[0].width).to.equal(300); expect(result[0].height).to.equal(300); - expect(result[1].bidderCode).to.equal(bidderCode); expect(result[1].cpm).to.equal(0.6); expect(result[1].ad).to.equal('placement_1'); expect(result[1].width).to.equal(300); From 3ac37f8c7bd3677105bebb947454abffad591bbf Mon Sep 17 00:00:00 2001 From: Michael Rooke Date: Wed, 5 Jun 2019 10:38:40 -0400 Subject: [PATCH 103/146] Use actual global object name in log message (#3874) - The global Prebid.js object name can be something other than 'pbjs'. Update log messages to reference the specified prebid global object name. --- modules/dfpAdServerVideo.js | 2 +- src/video.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dfpAdServerVideo.js b/modules/dfpAdServerVideo.js index d8cd6e099ee..79c11c5c886 100644 --- a/modules/dfpAdServerVideo.js +++ b/modules/dfpAdServerVideo.js @@ -58,7 +58,7 @@ const defaultParamConstants = { */ export default function buildDfpVideoUrl(options) { if (!options.params && !options.url) { - logError(`A params object or a url is required to use pbjs.adServers.dfp.buildVideoUrl`); + logError(`A params object or a url is required to use $$PREBID_GLOBAL$$.adServers.dfp.buildVideoUrl`); return; } diff --git a/src/video.js b/src/video.js index 9cf25016d46..f59ff78a32a 100644 --- a/src/video.js +++ b/src/video.js @@ -48,7 +48,7 @@ export const checkVideoBidSetup = hook('sync', function(bid, bidRequest, videoMe if (!config.getConfig('cache.url') && bid.vastXml && !bid.vastUrl) { logError(` This bid contains only vastXml and will not work when a prebid cache url is not specified. - Try enabling prebid cache with pbjs.setConfig({ cache: {url: "..."} }); + Try enabling prebid cache with $$PREBID_GLOBAL$$.setConfig({ cache: {url: "..."} }); `); return false; } From 2cf64989037c837a89cd4832b41a6c08981990d1 Mon Sep 17 00:00:00 2001 From: Chris Cole Date: Wed, 5 Jun 2019 08:59:12 -0700 Subject: [PATCH 104/146] Digitrust submodule (#3867) * Initial checkin with submodule support for DigiTrust. * Addition of simple example file * DigiTrust submodule now functioning with new userId system. * Addition of Full example and confirming to work with or without DigiTrust library. * Update based upon code review requests. * Revert "Initial checkin with submodule support for DigiTrust." This reverts commit c1fc37a888958150ce9ebd528ad910cc217ff6aa. # Conflicts: # modules/digiTrustIdSystem.js # modules/digiTrustIdSystem.md --- integrationExamples/gpt/digitrust_Full.html | 222 +++++++++++++ integrationExamples/gpt/digitrust_Simple.html | 220 +++++++++++++ modules/digiTrustIdSystem.js | 307 ++++++++++++++++++ modules/digiTrustIdSystem.md | 154 +++++++++ 4 files changed, 903 insertions(+) create mode 100644 integrationExamples/gpt/digitrust_Full.html create mode 100644 integrationExamples/gpt/digitrust_Simple.html create mode 100644 modules/digiTrustIdSystem.js create mode 100644 modules/digiTrustIdSystem.md diff --git a/integrationExamples/gpt/digitrust_Full.html b/integrationExamples/gpt/digitrust_Full.html new file mode 100644 index 00000000000..7ec268a619a --- /dev/null +++ b/integrationExamples/gpt/digitrust_Full.html @@ -0,0 +1,222 @@ + + + Full DigiTrust Prebid Sample + + + + + + + + + + + + + +

    DigiTrust Prebid Full Sample

    + + +

    + This sample shows the simplest integration path for using DigiTrust ID with Prebid. + You can use DigiTrust ID without integrating the entire DigiTrust suite. +

    + +
    + +
    + +
    + + + + diff --git a/integrationExamples/gpt/digitrust_Simple.html b/integrationExamples/gpt/digitrust_Simple.html new file mode 100644 index 00000000000..c9a8c1d2ad6 --- /dev/null +++ b/integrationExamples/gpt/digitrust_Simple.html @@ -0,0 +1,220 @@ + + + Simple DigiTrust Prebid - No Framework + + + + + + + + + + + + + + +

    DigiTrust Prebid Sample - No Framework

    + +

    + This sample shows the simplest integration path for using DigiTrust ID with Prebid. + You can use DigiTrust ID without integrating the entire DigiTrust suite. +

    +
    + +
    + +
    + + diff --git a/modules/digiTrustIdSystem.js b/modules/digiTrustIdSystem.js new file mode 100644 index 00000000000..b587913e1ed --- /dev/null +++ b/modules/digiTrustIdSystem.js @@ -0,0 +1,307 @@ +/** + * This module adds DigiTrust ID support to the User ID module + * The {@link module:modules/userId} module is required + * If the full DigiTrust Id library is included the standard functions + * will be invoked to obtain the user's DigiTrust Id. + * When the full library is not included this will fall back to the + * DigiTrust Identity API and generate a mock DigiTrust object. + * @module modules/digiTrustIdSystem + * @requires module:modules/userId + */ + +// import { config } from 'src/config'; +import * as utils from '../src/utils' +import { ajax } from 'src/ajax'; +import { attachIdSystem } from '../modules/userId'; +// import { getGlobal } from 'src/prebidGlobal'; + +/** + * Checks to see if the DigiTrust framework is initialized. + * @function + */ +function isInitialized() { + if (window.DigiTrust == null) { + return false; + } + return DigiTrust.isClient; // this is set to true after init +} + +/** + * Tests for presence of the DigiTrust object + * */ +function isPresent() { + return (window.DigiTrust != null); +} + +var noop = function () { +}; + +const MAX_RETRIES = 2; +const DT_ID_SVC = 'https://prebid.digitru.st/id/v1'; + +var isFunc = function (fn) { + return typeof (fn) === 'function'; +} + +function callApi(options) { + ajax( + DT_ID_SVC, + { + success: options.success, + error: options.fail + }, + null, + { + method: 'GET' + } + ); +} + +/** + * Encode the Id per DigiTrust lib + * @param {any} id + */ +function encId(id) { + try { + if (typeof (id) !== 'string') { + id = JSON.stringify(id); + } + return encodeURIComponent(btoa(id)); + } catch (ex) { + return id; + } +} + +/** + * Writes the Identity into the expected DigiTrust cookie + * @param {any} id + */ +function writeDigiId(id) { + var key = 'DigiTrust.v1.identity'; + var date = new Date(); + date.setTime(date.getTime() + 604800000); + var exp = 'expires=' + date.toUTCString(); + document.cookie = key + '=' + encId(id) + '; ' + exp + '; path=/;'; +} + +/** + * Set up a DigiTrust fascade object to mimic the API + * + */ +function initDigitrustFascade(config) { + var _savedId = null; // closure variable for storing Id to avoid additional requests + var fascade = { + isClient: true, + isMock: true, + _internals: { + callCount: 0, + initCallback: null + }, + getUser: function (obj, callback) { + var cb = callback || noop; + var inter = fascade._internals; + inter.callCount++; + + // wrap the initializer callback, if present + var checkCallInitializeCb = function (idResponse) { + if (inter.callCount <= 1 && isFunc(inter.initCallback)) { + try { + inter.initCallback(idResponse); + } catch (ex) { + utils.logError('Exception in passed DigiTrust init callback'); + } + } + } + + if (_savedId != null) { + checkCallInitializeCb(_savedId); + cb(_savedId); + return; + } + + var opts = { + success: function (respText, result) { + var idResult = { + success: true + } + try { + writeDigiId(respText); + idResult.identity = JSON.parse(respText); + _savedId = idResult; + } catch (ex) { + idResult.success = false; + } + checkCallInitializeCb(idResult); + cb(idResult); + }, + fail: function (statusErr, result) { + utils.logError('DigiTrustId API error: ' + statusErr); + } + } + + callApi(opts); + } + } + + if (window && window.DigiTrust == null) { + window.DigiTrust = fascade; + } +} + +/** + * Encapsulation of needed info for the callback return. + * + * @param {any} opts + */ +var ResultWrapper = function (opts) { + var me = this; + this.idObj = null; + + var idSystemFn = null; + + /** + * Callback method that is passed back to the userId module. + * + * @param {function} callback + */ + this.userIdCallback = function (callback) { + idSystemFn = callback; + if (me.idObj != null && isFunc(callback)) { + callback(wrapIdResult()); + } + } + + /** + * Return a wrapped result formatted for userId system + */ + function wrapIdResult() { + if (me.idObj == null) { + return null; + } + + var cp = me.configParams; + var exp = (cp && cp.storage && cp.storage.expires) || 60; + + var rslt = { + data: null, + expires: exp + }; + if (me.idObj && me.idObj.success && me.idObj.identity) { + rslt.data = me.idObj.identity; + } else { + rslt.err = 'Failure getting id'; + } + + return rslt; + } + + this.retries = 0; + this.retryId = 0; + + this.executeIdRequest = function (configParams) { + DigiTrust.getUser({ member: 'prebid' }, function (idResult) { + me.idObj = idResult; + var cb = function () { + if (isFunc(idSystemFn)) { + idSystemFn(wrapIdResult()); + } + } + + cb(); + if (configParams && configParams.callback && isFunc(configParams.callback)) { + try { + configParams.callback(idResult); + } catch (ex) { + utils.logError('Failure in DigiTrust executeIdRequest', ex); + } + } + }); + } +} + +// An instance of the result wrapper object. +var resultHandler = new ResultWrapper(); + +/* + * Internal implementation to get the Id and trigger callback + */ +function getDigiTrustId(configParams) { + if (resultHandler.configParams == null) { + resultHandler.configParams = configParams; + } + + // First see if we should initialize DigiTrust framework + if (isPresent() && !isInitialized()) { + initializeDigiTrust(configParams); + resultHandler.retryId = setTimeout(function () { + getDigiTrustId(configParams); + }, 100 * (1 + resultHandler.retries++)); + return resultHandler.userIdCallback; + } else if (!isInitialized()) { // Second see if we should build a fascade object + if (resultHandler.retries >= MAX_RETRIES) { + initDigitrustFascade(configParams); // initialize a fascade object that relies on the AJAX call + resultHandler.executeIdRequest(configParams); + } else { + // use expanding envelope + if (resultHandler.retryId != 0) { + clearTimeout(resultHandler.retryId); + } + resultHandler.retryId = setTimeout(function () { + getDigiTrustId(configParams); + }, 100 * (1 + resultHandler.retries++)); + } + return resultHandler.userIdCallback; + } else { // Third get the ID + resultHandler.executeIdRequest(configParams); + return resultHandler.userIdCallback; + } +} + +function initializeDigiTrust(config) { + utils.logInfo('Digitrust Init'); + var dt = window.DigiTrust; + if (dt && !dt.isClient && config != null) { + dt.initialize(config.init, config.callback); + } else if (dt == null) { + // Assume we are already on a delay and DigiTrust is not on page + initDigitrustFascade(config); + } +} + +var testHook = {}; + +/** + * Exposes the test hook object by attaching to the digitrustIdModule. + * This method is called in the unit tests to surface internals. + */ +function surfaceTestHook() { + digitrustIdModule['_testHook'] = testHook; +} + +testHook.initDigitrustFascade = initDigitrustFascade; + +/** @type {Submodule} */ +export const digiTrustIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: 'digitrust', + /** + * decode the stored id value for passing to bid requests + * @function + * @param {string} value + * @returns {{pubcid:string}} + */ + decode: function (idData) { + try { + return { 'digitrustid': idData }; + } catch (e) { + utils.logError('DigiTrust ID submodule decode error'); + } + }, + getId: getDigiTrustId, + _testInit: surfaceTestHook +}; + +attachIdSystem(digiTrustIdSubmodule); diff --git a/modules/digiTrustIdSystem.md b/modules/digiTrustIdSystem.md new file mode 100644 index 00000000000..8fe3c6652d9 --- /dev/null +++ b/modules/digiTrustIdSystem.md @@ -0,0 +1,154 @@ +## DigiTrust Universal Id Integration + +Setup +----- +The DigiTrust Id integration for Prebid may be used with or without the full +DigiTrust library. This is an optional module that must be used in conjunction +with the userId module. + +See the [Prebid Integration Guide for DigiTrust](https://github.com/digi-trust/dt-cdn/wiki/Prebid-Integration-for-DigiTrust-Id) +and the [DigiTrust wiki](https://github.com/digi-trust/dt-cdn/wiki) +for further instructions. + + +## Example Prebid Configuration for Digitrust Id +``` + pbjs.que.push(function() { + pbjs.setConfig({ + usersync: { + userIds: [{ + name: "digitrust", + params: { + init: { + member: 'example_member_id', + site: 'example_site_id' + }, + callback: function (digiTrustResult) { + // This callback method is optional + if (digiTrustResult.success) { + // Success in Digitrust init; + // 'DigiTrust Id (encrypted): ' + digiTrustResult.identity.id; + } + else { + // Digitrust init failed + } + } + }, + storage: { + type: "html5", + name: "pbjsdigitrust", + expires: 60 + } + }] + } + }); + pbjs.addAdUnits(adUnits); + pbjs.requestBids({ + bidsBackHandler: sendAdserverRequest + }); + }); + +``` + + +## Building Prebid with DigiTrust Support +Your Prebid build must include the modules for both **userId** and **digitrustIdLoader**. Follow the build instructions for Prebid as +explained in the top level README.md file of the Prebid source tree. + +ex: $ gulp build --modules=userId,digitrustIdLoader + +### Step by step Prebid build instructions for DigiTrust + +1. Download the Prebid source from [Prebid Git Repo](https://github.com/prebid/Prebid.js) +2. Set up your environment as outlined in the [Readme File](https://github.com/prebid/Prebid.js/blob/master/README.md#Build) +3. Execute the build command either with all modules or with the `userId` and `digitrustIdLoader` modules. + ``` + $ gulp build --modules=userId,digitrustIdLoader + ``` +4. (Optional) Concatenate the DigiTrust source code to the end of your `prebid.js` file for a single source distribution. +5. Upload the resulting source file to your CDN. + + +## Deploying Prebid with DigiTrust ID support +**Precondition:** You must be a DigiTrust member and have registered through the [DigiTrust Signup Process](http://www.digitru.st/signup/). +Your assigned publisher ID will be required in the configuration settings for all deployment scenarios. + +There are three supported approaches to deploying the Prebid-integrated DigiTrust package: + +* "Bare bones" deployment using only the integrated DigiTrust module code. +* Full DigiTrust with CDN referenced DigiTrust.js library. +* Full DigiTrust packaged with Prebid or site js. + +### Bare Bones Deployment + +This deployment results in the smallest Javascript package and is the simplest deployment. +It is appropriate for testing or deployments where simplicity is key. This approach +utilizes the REST API for ID generation. While there is less Javascript in use, +the user may experience more network requests than the scenarios that include the full +DigiTrust library. + +1. Build your Prebid package as above, skipping step 4. +2. Add the DigiTrust initializer section to your Prebid initialization object as below, + using your Member ID and Site ID. +3. Add a reference to your Prebid package and the initialization code on all pages you wish + to utilize Prebid with integrated DigiTrust ID. + + + + +### Full DigiTrust with CDN referenced DigiTrust library + +Both "Full DigiTrust" deployments will result in a larger initial Javascript payload. +The end user may experience fewer overall network requests as the encrypted and anonymous +DigiTrust ID can often be generated fully in client-side code. Utilizing the CDN reference +to the official DigiTrust distribution insures you will be running the latest version of the library. + +The Full DigiTrust deployment is designed to work with both new DigiTrust with Prebid deployments, and with +Prebid deployments by existing DigiTrust members. This allows you to migrate your code more slowly +without losing DigiTrust support in the process. + +1. Deploy your built copy of `prebid.js` to your CDN. +2. On each page reference both your `prebid.js` and a copy of the **DigiTrust** library. + This may either be a copy downloaded from the [DigiTrust CDN](https://cdn.digitru.st/prod/1/digitrust.min.js) to your CDN, + or directly referenced from the URL https://cdn.digitru.st/prod/1/digitrust.min.js. These may be added to the page in any order. +3. Add a configuration section for Prebid that includes the `usersync` settings and the `digitrust` settings. + +### Full DigiTrust packaged with Prebid + + +1. Deploy your built copy of `prebid.js` to your CDN. Be sure to perform *Step 4* of the build to concatenate or + integrate the full DigiTrust library code with your Prebid package. +2. On each page reference your `prebid.js` +3. Add a configuration section for Prebid that includes the `usersync` settings and the `digitrust` settings. + This code may also be appended to your Prebid package or placed in other initialization methods. + + + +## Parameter Descriptions for the `usersync` Configuration Section +The below parameters apply only to the DigiTrust ID integration. + +{: .table .table-bordered .table-striped } +| Param under usersync.userIds[] | Scope | Type | Description | Example | +| --- | --- | --- | --- | --- | +| name | Required | String | ID value for the DigiTrust module - `"digitrust"` | `"digitrust"` | +| params | Required | Object | Details for DigiTrust initialization. | | +| params.init | Required | Object | Initialization parameters, including the DigiTrust Publisher ID and Site ID. | | +| params.init.member | Required | String | DigiTrust Publisher Id | "A897dTzB" | +| params.init.site | Required | String | DigiTrust Site Id | "MM2123" | +| params.callback | Optional | Function | Callback method to fire after initialization of the DigiTrust framework. The argument indicates failure and success and the identity object upon success. | | +| storage | Required | Object | The publisher must specify the local storage in which to store the results of the call to get the user ID. This can be either cookie or HTML5 storage. | | +| storage.type | Required | String | This is where the results of the user ID will be stored. The recommended method is `localStorage` by specifying `html5`. | `"html5"` | +| storage.name | Required | String | The name of the cookie or html5 local storage where the user ID will be stored. | `"pbjsdigitrust"` | +| storage.expires | Optional | Integer | How long (in days) the user ID information will be stored. Default is 30 for UnifiedId and 1825 for PubCommonID | `365` | +| value | Optional | Object | Used only if the page has a separate mechanism for storing the Unified ID. The value is an object containing the values to be sent to the adapters. In this scenario, no URL is called and nothing is added to local storage | `{"tdid": "D6885E90-2A7A-4E0F-87CB-7734ED1B99A3"}` | + + + +## Further Reading + ++ [DigiTrust Home Page](http://digitru.st) + ++ [DigiTrust integration guide](https://github.com/digi-trust/dt-cdn/wiki/Integration-Guide) + ++ [DigiTrust ID Encryption](https://github.com/digi-trust/dt-cdn/wiki/ID-encryption) + From 45e5be75e8db071c107118d1e9feccbb43c901f4 Mon Sep 17 00:00:00 2001 From: Malkov Mikhail Date: Wed, 5 Jun 2019 22:25:15 +0300 Subject: [PATCH 105/146] changed name company (#3875) * changed name company * changed name company in test --- ...leniumBidAdapter.js => nextMillenniumBidAdapter.js} | 2 +- ...leniumBidAdapter.md => nextMillenniumBidAdapter.md} | 6 +++--- ...dapter_spec.js => nextMillenniumBidAdapter_spec.js} | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) rename modules/{nextMilleniumBidAdapter.js => nextMillenniumBidAdapter.js} (98%) rename modules/{nextMilleniumBidAdapter.md => nextMillenniumBidAdapter.md} (76%) rename test/spec/modules/{nextMilleniumBidAdapter_spec.js => nextMillenniumBidAdapter_spec.js} (91%) diff --git a/modules/nextMilleniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js similarity index 98% rename from modules/nextMilleniumBidAdapter.js rename to modules/nextMillenniumBidAdapter.js index 46f5a42b3c0..8093f6e8f7c 100644 --- a/modules/nextMilleniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -2,7 +2,7 @@ import * as utils from '../src/utils'; import { registerBidder } from '../src/adapters/bidderFactory'; import { BANNER } from '../src/mediaTypes'; -const BIDDER_CODE = 'nextMillenium'; +const BIDDER_CODE = 'nextMillennium'; const HOST = 'https://brainlyads.com'; const CURRENCY = 'USD'; const TIME_TO_LIVE = 360; diff --git a/modules/nextMilleniumBidAdapter.md b/modules/nextMillenniumBidAdapter.md similarity index 76% rename from modules/nextMilleniumBidAdapter.md rename to modules/nextMillenniumBidAdapter.md index a89e7e30822..c583969b4af 100644 --- a/modules/nextMilleniumBidAdapter.md +++ b/modules/nextMillenniumBidAdapter.md @@ -1,12 +1,12 @@ # Overview ``` -Module Name: NextMillenium Bid Adapter +Module Name: NextMillennium Bid Adapter Module Type: Bidder Adapter Maintainer: mikhail.ivanchenko@iageengineering.net ``` # Description -Module that connects to NextMillenium's server for bids. +Module that connects to NextMillennium's server for bids. Currently module supports only banner mediaType. # Test Parameters @@ -19,7 +19,7 @@ Currently module supports only banner mediaType. } }, bids: [{ - bidder: 'nextMillenium', + bidder: 'nextMillennium', params: { placement_id: -1 } diff --git a/test/spec/modules/nextMilleniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js similarity index 91% rename from test/spec/modules/nextMilleniumBidAdapter_spec.js rename to test/spec/modules/nextMillenniumBidAdapter_spec.js index 74c8ff5dfd9..087f06d7e8e 100644 --- a/test/spec/modules/nextMilleniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -1,12 +1,12 @@ import { expect } from 'chai'; -import { spec } from 'modules/nextMilleniumBidAdapter'; +import { spec } from 'modules/nextMillenniumBidAdapter'; -describe('nextMilleniumBidAdapterTests', function() { +describe('nextMillenniumBidAdapterTests', function() { let bidRequestData = { bids: [ { bidId: 'transaction_1234', - bidder: 'nextMillenium', + bidder: 'nextMillennium', params: { placement_id: 12345 }, @@ -19,7 +19,7 @@ describe('nextMilleniumBidAdapterTests', function() { it('validate_pub_params', function() { expect( spec.isBidRequestValid({ - bidder: 'nextMillenium', + bidder: 'nextMillennium', params: { placement_id: 12345 } @@ -31,7 +31,7 @@ describe('nextMilleniumBidAdapterTests', function() { let bidRequestData = [ { bidId: 'bid1234', - bidder: 'nextMillenium', + bidder: 'nextMillennium', params: { placement_id: -1 }, sizes: [[300, 250]] } From 81932cd9e3d5c9c51518b658d0dc5cf540094883 Mon Sep 17 00:00:00 2001 From: Matt Quirion Date: Wed, 5 Jun 2019 16:06:43 -0400 Subject: [PATCH 106/146] New STAQ analytics adapter (#3772) * initial dev * fix staq adapter name * fix hello world staq call * get hello world working again * add user agent collection * fix some unite tests * Add STAQ Analytics Adapter doc * clean up hello world * fix tests to play nice with browserstack * fix around issues with browserstack and deep equals of objects * dump variable env testing since we can't mod user agent stuff in browserstack * Update STAQ adapter to stop using deprecated utils for referrer * remove package-lock.json changes via master rebase * improve call frequency for ref util * change ajax content type * adjust ajax request to not expect whitelisting * remove superflous commented-out code --- modules/staqAnalyticsAdapter.js | 429 ++++++++++++++++++ modules/staqAnalyticsAdapter.md | 23 + .../spec/modules/staqAnalyticsAdapter_spec.js | 301 ++++++++++++ 3 files changed, 753 insertions(+) create mode 100644 modules/staqAnalyticsAdapter.js create mode 100644 modules/staqAnalyticsAdapter.md create mode 100644 test/spec/modules/staqAnalyticsAdapter_spec.js diff --git a/modules/staqAnalyticsAdapter.js b/modules/staqAnalyticsAdapter.js new file mode 100644 index 00000000000..4d8b81b7be2 --- /dev/null +++ b/modules/staqAnalyticsAdapter.js @@ -0,0 +1,429 @@ +import adapter from '../src/AnalyticsAdapter'; +import CONSTANTS from '../src/constants.json'; +import adapterManager from '../src/adapterManager'; +import {getRefererInfo} from '../src/refererDetection'; +import {parse} from '../src/url'; +import * as utils from '../src/utils'; +import {ajax} from '../src/ajax'; + +const ANALYTICS_VERSION = '1.0.0'; +const DEFAULT_QUEUE_TIMEOUT = 4000; +const DEFAULT_HOST = 'tag.staq.com'; + +let staqAdapterRefWin; + +const STAQ_EVENTS = { + AUCTION_INIT: 'auctionInit', + BID_REQUEST: 'bidRequested', + BID_RESPONSE: 'bidResponse', + BID_WON: 'bidWon', + AUCTION_END: 'auctionEnd', + TIMEOUT: 'adapterTimedOut' +} + +function buildRequestTemplate(connId) { + const url = staqAdapterRefWin.referer; + const ref = staqAdapterRefWin.referer; + const topLocation = staqAdapterRefWin.referer; + + return { + ver: ANALYTICS_VERSION, + domain: topLocation.hostname, + path: topLocation.pathname, + userAgent: navigator.userAgent, + connId: connId, + env: { + screen: { + w: window.screen.width, + h: window.screen.height + }, + lang: navigator.language + }, + src: getUmtSource(url, ref) + } +} + +let analyticsAdapter = Object.assign(adapter({analyticsType: 'endpoint'}), + { + track({ eventType, args }) { + if (!analyticsAdapter.context) { + return; + } + let handler = null; + switch (eventType) { + case CONSTANTS.EVENTS.AUCTION_INIT: + if (analyticsAdapter.context.queue) { + analyticsAdapter.context.queue.init(); + } + handler = trackAuctionInit; + break; + case CONSTANTS.EVENTS.BID_REQUESTED: + handler = trackBidRequest; + break; + case CONSTANTS.EVENTS.BID_RESPONSE: + handler = trackBidResponse; + break; + case CONSTANTS.EVENTS.BID_WON: + handler = trackBidWon; + break; + case CONSTANTS.EVENTS.BID_TIMEOUT: + handler = trackBidTimeout; + break; + case CONSTANTS.EVENTS.AUCTION_END: + handler = trackAuctionEnd; + break; + } + if (handler) { + let events = handler(args); + if (analyticsAdapter.context.queue) { + analyticsAdapter.context.queue.push(events); + if (eventType === CONSTANTS.EVENTS.BID_WON) { + analyticsAdapter.context.queue.updateWithWins(events); + } + } + if (eventType === CONSTANTS.EVENTS.AUCTION_END) { + sendAll(); + } + } + } + }); + +analyticsAdapter.context = {}; + +analyticsAdapter.originEnableAnalytics = analyticsAdapter.enableAnalytics; + +analyticsAdapter.enableAnalytics = (config) => { + utils.logInfo('Enabling STAQ Adapter'); + staqAdapterRefWin = getRefererInfo(window); + if (!config.options.connId) { + utils.logError('ConnId is not defined. STAQ Analytics won\'t work'); + return; + } + if (!config.options.url) { + utils.logError('URL is not defined. STAQ Analytics won\'t work'); + return; + } + analyticsAdapter.context = { + host: config.options.host || DEFAULT_HOST, + url: config.options.url, + connectionId: config.options.connId, + requestTemplate: buildRequestTemplate(config.options.connId), + queue: new ExpiringQueue(sendAll, config.options.queueTimeout || DEFAULT_QUEUE_TIMEOUT) + }; + analyticsAdapter.originEnableAnalytics(config); +}; + +adapterManager.registerAnalyticsAdapter({ + adapter: analyticsAdapter, + code: 'staq' +}); + +export default analyticsAdapter; + +function sendAll() { + let events = analyticsAdapter.context.queue.popAll(); + if (events.length !== 0) { + let req = events.map(event => { + return Object.assign({}, event, analyticsAdapter.context.requestTemplate) + }); + analyticsAdapter.ajaxCall(JSON.stringify(req)); + } +} + +analyticsAdapter.ajaxCall = function ajaxCall(data) { + utils.logInfo('SENDING DATA: ' + data); + ajax(`//${analyticsAdapter.context.url}/prebid/${analyticsAdapter.context.connectionId}`, () => { + }, data, {contentType: 'text/plain'}); +}; + +function trackAuctionInit(args) { + analyticsAdapter.context.auctionTimeStart = Date.now(); + analyticsAdapter.context.auctionId = args.auctionId; + const event = createHbEvent(args.auctionId, undefined, STAQ_EVENTS.AUCTION_INIT); + return [event]; +} + +function trackBidRequest(args) { + return args.bids.map(bid => + createHbEvent(args.auctionId, args.bidderCode, STAQ_EVENTS.BID_REQUEST, bid.adUnitCode)); +} + +function trackBidResponse(args) { + const event = createHbEvent(args.auctionId, args.bidderCode, STAQ_EVENTS.BID_RESPONSE, + args.adUnitCode, args.cpm, args.timeToRespond / 1000, false, args); + return [event]; +} + +function trackBidWon(args) { + const event = createHbEvent(args.auctionId, args.bidderCode, STAQ_EVENTS.BID_WON, args.adUnitCode, args.cpm, undefined, true, args); + return [event]; +} + +function trackAuctionEnd(args) { + const event = createHbEvent(args.auctionId, undefined, STAQ_EVENTS.AUCTION_END, undefined, + undefined, (Date.now() - analyticsAdapter.context.auctionTimeStart) / 1000); + return [event]; +} + +function trackBidTimeout(args) { + return args.map(arg => + createHbEvent(arg.auctionId, arg.bidderCode, STAQ_EVENTS.TIMEOUT) + ); +} + +function createHbEvent(auctionId, adapter, event, adUnitCode = undefined, value = 0, time = 0, bidWon = undefined, eventArgs) { + let ev = { event: event }; + if (adapter) { + ev.adapter = adapter; + ev.bidderName = adapter; + } + if (adUnitCode) { + ev.adUnitCode = adUnitCode; + } + if (value) { + ev.cpm = value; + } + if (time) { + ev.timeToRespond = time; + } + if (typeof bidWon !== 'undefined') { + ev.bidWon = bidWon; + } else if (event === 'bidResponse') { + ev.bidWon = false; + } + ev.auctionId = auctionId; + + if (eventArgs) { + if (STAQ_EVENTS.BID_RESPONSE == event || STAQ_EVENTS.BID_WON == event) { + ev.width = eventArgs.width; + ev.height = eventArgs.height; + + ev.adId = eventArgs.adId; + } + } + + return ev; +} + +const UTM_TAGS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', + 'utm_c1', 'utm_c2', 'utm_c3', 'utm_c4', 'utm_c5']; +const STAQ_PREBID_KEY = 'staq_analytics'; +const DIRECT = '(direct)'; +const REFERRAL = '(referral)'; +const ORGANIC = '(organic)'; + +export let storage = { + getItem: (name) => { + return localStorage.getItem(name); + }, + setItem: (name, value) => { + localStorage.setItem(name, value); + } +}; + +export function getUmtSource(pageUrl, referrer) { + let prevUtm = getPreviousTrafficSource(); + let currUtm = getCurrentTrafficSource(pageUrl, referrer); + let [updated, actual] = chooseActualUtm(prevUtm, currUtm); + if (updated) { + storeUtm(actual); + } + return actual; + + function getPreviousTrafficSource() { + let val = storage.getItem(STAQ_PREBID_KEY); + if (!val) { + return getDirect(); + } + return JSON.parse(val); + } + + function getCurrentTrafficSource(pageUrl, referrer) { + var source = getUTM(pageUrl); + if (source) { + return source; + } + if (referrer) { + let se = getSearchEngine(referrer); + if (se) { + return asUtm(se, ORGANIC, ORGANIC); + } + let parsedUrl = parse(pageUrl); + let [refHost, refPath] = getReferrer(referrer); + if (refHost && refHost !== parsedUrl.hostname) { + return asUtm(refHost, REFERRAL, REFERRAL, '', refPath); + } + } + return getDirect(); + } + + function getSearchEngine(pageUrl) { + let engines = { + 'google': /^https?\:\/\/(?:www\.)?(?:google\.(?:com?\.)?(?:com|cat|[a-z]{2})|g.cn)\//i, + 'yandex': /^https?\:\/\/(?:www\.)?ya(?:ndex\.(?:com|net)?\.?(?:asia|mobi|org|[a-z]{2})?|\.ru)\//i, + 'bing': /^https?\:\/\/(?:www\.)?bing\.com\//i, + 'duckduckgo': /^https?\:\/\/(?:www\.)?duckduckgo\.com\//i, + 'ask': /^https?\:\/\/(?:www\.)?ask\.com\//i, + 'yahoo': /^https?\:\/\/(?:[-a-z]+\.)?(?:search\.)?yahoo\.com\//i + }; + + for (let engine in engines) { + if (engines.hasOwnProperty(engine) && engines[engine].test(pageUrl)) { + return engine; + } + } + } + + function getReferrer(referrer) { + let ref = parse(referrer); + return [ref.hostname, ref.pathname]; + } + + function getUTM(pageUrl) { + let urlParameters = parse(pageUrl).search; + if (!urlParameters['utm_campaign'] || !urlParameters['utm_source']) { + return; + } + let utmArgs = []; + utils._each(UTM_TAGS, (utmTagName) => { + let utmValue = urlParameters[utmTagName] || ''; + utmArgs.push(utmValue); + }); + return asUtm.apply(this, utmArgs); + } + + function getDirect() { + return asUtm(DIRECT, DIRECT, DIRECT); + } + + function storeUtm(utm) { + let val = JSON.stringify(utm); + storage.setItem(STAQ_PREBID_KEY, val); + } + + function asUtm(source, medium, campaign, term = '', content = '', c1 = '', c2 = '', c3 = '', c4 = '', c5 = '') { + let result = { + source: source, + medium: medium, + campaign: campaign + }; + if (term) { + result.term = term; + } + if (content) { + result.content = content; + } + if (c1) { + result.c1 = c1; + } + if (c2) { + result.c2 = c2; + } + if (c3) { + result.c3 = c3; + } + if (c4) { + result.c4 = c4; + } + if (c5) { + result.c5 = c5; + } + return result; + } + + function chooseActualUtm(prev, curr) { + if (ord(prev) < ord(curr)) { + return [true, curr]; + } if (ord(prev) > ord(curr)) { + return [false, prev]; + } else { + if (prev.campaign === REFERRAL && prev.content !== curr.content) { + return [true, curr]; + } else if (prev.campaign === ORGANIC && prev.source !== curr.source) { + return [true, curr]; + } else if (isCampaignTraffic(prev) && (prev.campaign !== curr.campaign || prev.source !== curr.source)) { + return [true, curr]; + } + } + return [false, prev]; + } + + function ord(utm) { + switch (utm.campaign) { + case DIRECT: + return 0; + case ORGANIC: + return 1; + case REFERRAL: + return 2; + default: + return 3; + } + } + + function isCampaignTraffic(utm) { + return [DIRECT, REFERRAL, ORGANIC].indexOf(utm.campaign) === -1; + } +} + +/** + * Expiring queue implementation. Fires callback on elapsed timeout since last last update or creation. + * @param callback + * @param ttl + * @constructor + */ +export function ExpiringQueue(callback, ttl) { + let queue = []; + let timeoutId; + + this.push = (event) => { + if (event instanceof Array) { + queue.push.apply(queue, event); + } else { + queue.push(event); + } + reset(); + }; + + this.updateWithWins = (winEvents) => { + winEvents.forEach(winEvent => { + queue.forEach(prevEvent => { + if (prevEvent.event === 'bidResponse' && + prevEvent.auctionId == winEvent.auctionId && + prevEvent.adUnitCode == winEvent.adUnitCode && + prevEvent.adId == winEvent.adId && + prevEvent.adapter == winEvent.adapter) { + prevEvent.bidWon = true; + } + }); + }); + } + + this.popAll = () => { + let result = queue; + queue = []; + reset(); + return result; + }; + + /** + * For test/debug purposes only + * @return {Array} + */ + this.peekAll = () => { + return queue; + }; + + this.init = reset; + + function reset() { + if (timeoutId) { + clearTimeout(timeoutId); + } + timeoutId = setTimeout(() => { + if (queue.length) { + callback(); + } + }, ttl); + } +} diff --git a/modules/staqAnalyticsAdapter.md b/modules/staqAnalyticsAdapter.md new file mode 100644 index 00000000000..c3d2e9ce3a8 --- /dev/null +++ b/modules/staqAnalyticsAdapter.md @@ -0,0 +1,23 @@ +# Overview +Module Name: STAQ Analytics Adapter + +Module Type: Analytics Adapter + +Maintainer: dev@staq.com + +# Description + +Analytics adapter for STAQ. Contact support@staq.com for information. + +# Test Parameters + +``` +{ + provider: 'staq', + options: { + host: , // HOST URL of site. Optional. Only required for whitelisting. + url: 'localhost:3000', // REQUIRED host URL for delivery of information to STAQ + connId: '5678' // REQUIRED STAQ connection ID + } +} +``` diff --git a/test/spec/modules/staqAnalyticsAdapter_spec.js b/test/spec/modules/staqAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..33c85d11431 --- /dev/null +++ b/test/spec/modules/staqAnalyticsAdapter_spec.js @@ -0,0 +1,301 @@ +import analyticsAdapter, {ExpiringQueue, getUmtSource, storage} from 'modules/staqAnalyticsAdapter'; +import {expect} from 'chai'; +import adapterManager from 'src/adapterManager'; +import CONSTANTS from 'src/constants.json'; + +const events = require('../../../src/events'); + +const DIRECT = { + source: '(direct)', + medium: '(direct)', + campaign: '(direct)' +}; +const REFERRER = { + source: 'lander.com', + medium: '(referral)', + campaign: '(referral)', + content: '/lander.html' +}; +const GOOGLE_ORGANIC = { + source: 'google', + medium: '(organic)', + campaign: '(organic)' +}; +const CAMPAIGN = { + source: 'adkernel', + medium: 'email', + campaign: 'new_campaign', + c1: '1', + c2: '2', + c3: '3', + c4: '4', + c5: '5' + +}; +describe('', function () { + let sandbox; + + before(function () { + sandbox = sinon.sandbox.create(); + }); + + after(function () { + sandbox.restore(); + analyticsAdapter.disableAnalytics(); + }); + + describe('UTM source parser', function () { + let stubSetItem; + let stubGetItem; + + before(function () { + stubSetItem = sandbox.stub(storage, 'setItem'); + stubGetItem = sandbox.stub(storage, 'getItem'); + }); + + afterEach(function () { + sandbox.reset(); + }); + + it('should parse first direct visit as (direct)', function () { + stubGetItem.withArgs('adk_dpt_analytics').returns(undefined); + stubSetItem.returns(undefined); + let source = getUmtSource('http://example.com'); + expect(source).to.be.eql(DIRECT); + }); + + it('should parse visit from google as organic', function () { + stubGetItem.withArgs('adk_dpt_analytics').returns(undefined); + stubSetItem.returns(undefined); + let source = getUmtSource('http://example.com', 'https://www.google.com/search?q=pikachu'); + expect(source).to.be.eql(GOOGLE_ORGANIC); + }); + + it('should parse referral visit', function () { + stubGetItem.withArgs('adk_dpt_analytics').returns(undefined); + stubSetItem.returns(undefined); + let source = getUmtSource('http://example.com', 'http://lander.com/lander.html'); + expect(source).to.be.eql(REFERRER); + }); + + it('should parse referral visit from same domain as direct', function () { + stubGetItem.withArgs('adk_dpt_analytics').returns(undefined); + stubSetItem.returns(undefined); + let source = getUmtSource('http://lander.com/news.html', 'http://lander.com/lander.html'); + expect(source).to.be.eql(DIRECT); + }); + + it('should parse campaign visit', function () { + stubGetItem.withArgs('adk_dpt_analytics').returns(undefined); + stubSetItem.returns(undefined); + let source = getUmtSource('http://lander.com/index.html?utm_campaign=new_campaign&utm_source=adkernel&utm_medium=email&utm_c1=1&utm_c2=2&utm_c3=3&utm_c4=4&utm_c5=5'); + expect(source).to.be.eql(CAMPAIGN); + }); + }); + + describe('ExpiringQueue', function () { + let timer; + before(function () { + timer = sandbox.useFakeTimers(0); + }); + after(function () { + timer.restore(); + }); + + it('should notify after timeout period', (done) => { + let queue = new ExpiringQueue(() => { + let elements = queue.popAll(); + expect(elements).to.be.eql([1, 2, 3, 4]); + elements = queue.popAll(); + expect(elements).to.have.lengthOf(0); + expect(Date.now()).to.be.equal(200); + done(); + }, 100); + + queue.push(1); + setTimeout(() => { + queue.push([2, 3]); + timer.tick(50); + }, 50); + setTimeout(() => { + queue.push([4]); + timer.tick(100); + }, 100); + timer.tick(50); + }); + }); + + const REQUEST = { + bidderCode: 'AppNexus', + bidderName: 'AppNexus', + auctionId: '5018eb39-f900-4370-b71e-3bb5b48d324f', + bidderRequestId: '1a6fc81528d0f6', + bids: [{ + bidder: 'AppNexus', + params: {}, + adUnitCode: 'container-1', + transactionId: 'de90df62-7fd0-4fbc-8787-92d133a7dc06', + sizes: [[300, 250]], + bidId: '208750227436c1', + bidderRequestId: '1a6fc81528d0f6', + auctionId: '5018eb39-f900-4370-b71e-3bb5b48d324f' + }], + auctionStart: 1509369418387, + timeout: 3000, + start: 1509369418389 + }; + + const RESPONSE = { + bidderCode: 'AppNexus', + width: 300, + height: 250, + statusMessage: 'Bid available', + adId: '208750227436c1', + mediaType: 'banner', + cpm: 0.015, + ad: '', + auctionId: '5018eb39-f900-4370-b71e-3bb5b48d324f', + responseTimestamp: 1509369418832, + requestTimestamp: 1509369418389, + bidder: 'AppNexus', + adUnitCode: 'container-1', + timeToRespond: 443, + size: '300x250' + }; + + const bidTimeoutArgsV1 = [{ + bidId: '2baa51527bd015', + bidderCode: 'AppNexus', + adUnitCode: 'container-1', + auctionId: '66529d4c-8998-47c2-ab3e-5b953490b98f' + }, + { + bidId: '6fe3b4c2c23092', + bidderCode: 'AppNexus', + adUnitCode: 'container-2', + auctionId: '66529d4c-8998-47c2-ab3e-5b953490b98f' + }]; + + describe('Analytics adapter', function () { + let ajaxStub; + let timer; + + before(function () { + ajaxStub = sandbox.stub(analyticsAdapter, 'ajaxCall'); + timer = sandbox.useFakeTimers(0); + }); + + beforeEach(function () { + sandbox.stub(events, 'getEvents').callsFake(() => { + return [] + }); + }); + + afterEach(function () { + events.getEvents.restore(); + }); + + it('should be configurable', function () { + adapterManager.registerAnalyticsAdapter({ + code: 'staq', + adapter: analyticsAdapter + }); + + adapterManager.enableAnalytics({ + provider: 'staq', + options: { + connId: 777, + queueTimeout: 1000, + url: 'http://localhost/prebid' + } + }); + + expect(analyticsAdapter.context).to.have.property('connectionId', 777); + }); + + it('should handle auction init event', function () { + events.emit(CONSTANTS.EVENTS.AUCTION_INIT, {config: {}, timeout: 3000}); + const ev = analyticsAdapter.context.queue.peekAll(); + expect(ev).to.have.length(1); + expect(ev[0]).to.be.eql({event: 'auctionInit', auctionId: undefined}); + }); + + it('should handle bid request event', function () { + events.emit(CONSTANTS.EVENTS.BID_REQUESTED, REQUEST); + const ev = analyticsAdapter.context.queue.peekAll(); + expect(ev).to.have.length(2); + expect(ev[1]).to.be.eql({ + adUnitCode: 'container-1', + auctionId: '5018eb39-f900-4370-b71e-3bb5b48d324f', + event: 'bidRequested', + adapter: 'AppNexus', + bidderName: 'AppNexus' + }); + }); + + it('should handle bid response event', function () { + events.emit(CONSTANTS.EVENTS.BID_RESPONSE, RESPONSE); + const ev = analyticsAdapter.context.queue.peekAll(); + expect(ev).to.have.length(3); + expect(ev[2]).to.be.eql({ + adId: '208750227436c1', + event: 'bidResponse', + adapter: 'AppNexus', + bidderName: 'AppNexus', + auctionId: '5018eb39-f900-4370-b71e-3bb5b48d324f', + adUnitCode: 'container-1', + cpm: 0.015, + timeToRespond: 0.443, + height: 250, + width: 300, + bidWon: false, + }); + }); + + it('should handle timeouts properly', function() { + events.emit(CONSTANTS.EVENTS.BID_TIMEOUT, bidTimeoutArgsV1); + + const ev = analyticsAdapter.context.queue.peekAll(); + expect(ev).to.have.length(5); // remember, we added 2 timeout events + expect(ev[3]).to.be.eql({ + adapter: 'AppNexus', + auctionId: '66529d4c-8998-47c2-ab3e-5b953490b98f', + bidderName: 'AppNexus', + event: 'adapterTimedOut' + }) + }); + + it('should handle winning bid', function () { + events.emit(CONSTANTS.EVENTS.BID_WON, RESPONSE); + const ev = analyticsAdapter.context.queue.peekAll(); + expect(ev).to.have.length(6); + expect(ev[5]).to.be.eql({ + auctionId: '5018eb39-f900-4370-b71e-3bb5b48d324f', + adId: '208750227436c1', + event: 'bidWon', + adapter: 'AppNexus', + bidderName: 'AppNexus', + adUnitCode: 'container-1', + cpm: 0.015, + height: 250, + width: 300, + bidWon: true, + }); + }); + + it('should handle auction end event', function () { + timer.tick(447); + events.emit(CONSTANTS.EVENTS.AUCTION_END, RESPONSE); + let ev = analyticsAdapter.context.queue.peekAll(); + expect(ev).to.have.length(0); + expect(ajaxStub.calledOnce).to.be.equal(true); + let firstCallArgs0 = ajaxStub.firstCall.args[0]; + ev = JSON.parse(firstCallArgs0); + // console.log('AUCTION END EVENT SHAPE ' + JSON.stringify(ev)); + const ev6 = ev[6]; + expect(ev6.connId).to.be.eql(777); + expect(ev6.auctionId).to.be.eql('5018eb39-f900-4370-b71e-3bb5b48d324f'); + expect(ev6.event).to.be.eql('auctionEnd'); + }); + }); +}); From 8b6fbd798cabf51aa6ee7636f8762ee02e42ae19 Mon Sep 17 00:00:00 2001 From: Jaimin Panchal Date: Wed, 5 Jun 2019 16:57:27 -0400 Subject: [PATCH 107/146] Prebid 2.18.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e84e1698434..299cc8f8620 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.18.0-pre", + "version": "2.18.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From a40835cbe7b382d9a84abadfdd67ad6642503d6e Mon Sep 17 00:00:00 2001 From: Jaimin Panchal Date: Wed, 5 Jun 2019 17:10:29 -0400 Subject: [PATCH 108/146] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 299cc8f8620..468b16c0ebb 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.18.0", + "version": "2.19.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 5a4b25ac80e912a41fe9c4e6ab372e434e7f9534 Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Fri, 7 Jun 2019 07:27:55 -0700 Subject: [PATCH 109/146] removed the non-working setting on table (#3890) --- modules/digiTrustIdSystem.md | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/digiTrustIdSystem.md b/modules/digiTrustIdSystem.md index 8fe3c6652d9..6ddbad4aea4 100644 --- a/modules/digiTrustIdSystem.md +++ b/modules/digiTrustIdSystem.md @@ -127,7 +127,6 @@ without losing DigiTrust support in the process. ## Parameter Descriptions for the `usersync` Configuration Section The below parameters apply only to the DigiTrust ID integration. -{: .table .table-bordered .table-striped } | Param under usersync.userIds[] | Scope | Type | Description | Example | | --- | --- | --- | --- | --- | | name | Required | String | ID value for the DigiTrust module - `"digitrust"` | `"digitrust"` | From 3cb73424b5b815ffa2d3cc43773f8397a53372fc Mon Sep 17 00:00:00 2001 From: kd-appier <45807878+kd-appier@users.noreply.github.com> Date: Tue, 11 Jun 2019 00:08:41 +0800 Subject: [PATCH 110/146] Implement Appier Analytics Adapter. (#3871) --- modules/appierAnalyticsAdapter.js | 244 ++++++ modules/appierAnalyticsAdapter.md | 23 + .../modules/appierAnalyticsAdapter_spec.js | 709 ++++++++++++++++++ 3 files changed, 976 insertions(+) create mode 100644 modules/appierAnalyticsAdapter.js create mode 100644 modules/appierAnalyticsAdapter.md create mode 100644 test/spec/modules/appierAnalyticsAdapter_spec.js diff --git a/modules/appierAnalyticsAdapter.js b/modules/appierAnalyticsAdapter.js new file mode 100644 index 00000000000..76811b598e0 --- /dev/null +++ b/modules/appierAnalyticsAdapter.js @@ -0,0 +1,244 @@ +import {ajax} from '../src/ajax'; +import adapter from '../src/AnalyticsAdapter'; +import CONSTANTS from '../src/constants.json'; +import adapterManager from '../src/adapterManager'; +import {logError, logInfo, deepClone} from '../src/utils'; + +const analyticsType = 'endpoint'; + +export const ANALYTICS_VERSION = '1.0.0'; + +const DEFAULT_SERVER = 'https://prebid-analytics.c.appier.net/v1'; + +const { + EVENTS: { + AUCTION_END, + BID_WON, + BID_TIMEOUT + } +} = CONSTANTS; + +export const BIDDER_STATUS = { + BID: 'bid', + NO_BID: 'noBid', + BID_WON: 'bidWon', + TIMEOUT: 'timeout' +}; + +export const getCpmInUsd = function (bid) { + if (bid.currency === 'USD') { + return bid.cpm; + } else { + return bid.getCpmInNewCurrency('USD'); + } +}; + +const analyticsOptions = {}; + +export const parseBidderCode = function (bid) { + let bidderCode = bid.bidderCode || bid.bidder; + return bidderCode.toLowerCase(); +}; + +export const parseAdUnitCode = function (bidResponse) { + return bidResponse.adUnitCode.toLowerCase(); +}; + +export const appierAnalyticsAdapter = Object.assign(adapter({DEFAULT_SERVER, analyticsType}), { + + cachedAuctions: {}, + + initConfig(config) { + /** + * Required option: affiliateId + * Required option: configId + * + * Optional option: server + * Optional option: sampling + * Optional option: adSampling + * Optional option: autoPick + * Optional option: predictionId + * @type {boolean} + */ + analyticsOptions.options = deepClone(config.options); + if (typeof config.options.affiliateId !== 'string' || config.options.affiliateId.length < 1) { + logError('"options.affiliateId" is required.'); + return false; + } + if (typeof config.options.configId !== 'string' || config.options.configId.length < 1) { + logError('"options.configId" is required.'); + return false; + } + + analyticsOptions.affiliateId = config.options.affiliateId; + analyticsOptions.configId = config.options.configId; + analyticsOptions.server = config.options.server || DEFAULT_SERVER; + + analyticsOptions.sampled = true; + if (typeof config.options.sampling === 'number') { + analyticsOptions.sampled = Math.random() < parseFloat(config.options.sampling); + } + analyticsOptions.adSampled = false; + if (typeof config.options.adSampling === 'number') { + analyticsOptions.adSampled = Math.random() < parseFloat(config.options.adSampling); + } + analyticsOptions.autoPick = config.options.autoPick || null; + analyticsOptions.predictionId = config.options.predictionId || null; + + return true; + }, + sendEventMessage(endPoint, data) { + logInfo(`AJAX: ${endPoint}: ` + JSON.stringify(data)); + + ajax(`${analyticsOptions.server}/${endPoint}`, null, JSON.stringify(data), { + contentType: 'application/json', + withCredentials: true + }); + }, + createCommonMessage(auctionId) { + return { + version: ANALYTICS_VERSION, + auctionId: auctionId, + affiliateId: analyticsOptions.affiliateId, + configId: analyticsOptions.configId, + referrer: window.location.href, + sampling: analyticsOptions.options.sampling, + adSampling: analyticsOptions.options.adSampling, + prebid: '$prebid.version$', + autoPick: analyticsOptions.autoPick, + predictionId: analyticsOptions.predictionId, + adUnits: {}, + }; + }, + serializeBidResponse(bid, status) { + const result = { + prebidWon: (status === BIDDER_STATUS.BID_WON), + isTimeout: (status === BIDDER_STATUS.TIMEOUT), + status: status, + }; + if (status === BIDDER_STATUS.BID || status === BIDDER_STATUS.BID_WON) { + Object.assign(result, { + time: bid.timeToRespond, + cpm: bid.cpm, + currency: bid.currency, + originalCpm: bid.originalCpm || bid.cpm, + cpmUsd: getCpmInUsd(bid), + originalCurrency: bid.originalCurrency || bid.currency, + }); + } + return result; + }, + addBidResponseToMessage(message, bid, status) { + const adUnitCode = parseAdUnitCode(bid); + message.adUnits[adUnitCode] = message.adUnits[adUnitCode] || {}; + const bidder = parseBidderCode(bid); + const bidResponse = this.serializeBidResponse(bid, status); + message.adUnits[adUnitCode][bidder] = bidResponse; + }, + createBidMessage(auctionEndArgs, winningBids, timeoutBids) { + const {auctionId, timestamp, timeout, auctionEnd, adUnitCodes, bidsReceived, noBids} = auctionEndArgs; + const message = this.createCommonMessage(auctionId); + + message.auctionElapsed = (auctionEnd - timestamp); + message.timeout = timeout; + + adUnitCodes.forEach((adUnitCode) => { + message.adUnits[adUnitCode] = {}; + }); + + // We handled noBids first because when currency conversion is enabled, a bid with a foreign currency + // will be set to NO_BID initially, and then set to BID after the currency rate json file is fully loaded. + // In this situation, the bid exists in both noBids and bids arrays. + noBids.forEach(bid => this.addBidResponseToMessage(message, bid, BIDDER_STATUS.NO_BID)); + + // This array may contain some timeout bids (responses come back after auction timeout) + bidsReceived.forEach(bid => this.addBidResponseToMessage(message, bid, BIDDER_STATUS.BID)); + + // We handle timeout after bids since it's possible that a bid has a response, but the response comes back + // after auction end. In this case, the bid exists in both bidsReceived and timeoutBids arrays. + timeoutBids.forEach(bid => this.addBidResponseToMessage(message, bid, BIDDER_STATUS.TIMEOUT)); + + // mark the winning bids with prebidWon = true + winningBids.forEach(bid => { + const adUnitCode = parseAdUnitCode(bid); + const bidder = parseBidderCode(bid); + message.adUnits[adUnitCode][bidder].prebidWon = true; + }); + return message; + }, + createImpressionMessage(bid) { + const message = this.createCommonMessage(bid.auctionId); + this.addBidResponseToMessage(message, bid, BIDDER_STATUS.BID_WON); + return message; + }, + createCreativeMessage(auctionId, bids) { + const message = this.createCommonMessage(auctionId); + bids.forEach((bid) => { + const adUnitCode = parseAdUnitCode(bid); + const bidder = parseBidderCode(bid); + message.adUnits[adUnitCode] = message.adUnits[adUnitCode] || {}; + message.adUnits[adUnitCode][bidder] = {ad: bid.ad}; + }); + return message; + }, + getCachedAuction(auctionId) { + this.cachedAuctions[auctionId] = this.cachedAuctions[auctionId] || { + timeoutBids: [], + }; + return this.cachedAuctions[auctionId]; + }, + handleAuctionEnd(auctionEndArgs) { + const cachedAuction = this.getCachedAuction(auctionEndArgs.auctionId); + const highestCpmBids = pbjs.getHighestCpmBids(); + this.sendEventMessage('bid', + this.createBidMessage(auctionEndArgs, highestCpmBids, cachedAuction.timeoutBids) + ); + if (analyticsOptions.adSampled) { + this.sendEventMessage('cr', + this.createCreativeMessage(auctionEndArgs.auctionId, auctionEndArgs.bidsReceived) + ); + } + }, + handleBidTimeout(timeoutBids) { + timeoutBids.forEach((bid) => { + const cachedAuction = this.getCachedAuction(bid.auctionId); + cachedAuction.timeoutBids.push(bid); + }); + }, + handleBidWon(bidWonArgs) { + this.sendEventMessage('imp', this.createImpressionMessage(bidWonArgs)); + }, + track({eventType, args}) { + if (analyticsOptions.sampled) { + switch (eventType) { + case BID_WON: + this.handleBidWon(args); + break; + case BID_TIMEOUT: + this.handleBidTimeout(args); + break; + case AUCTION_END: + this.handleAuctionEnd(args); + break; + } + } + }, + getAnalyticsOptions() { + return analyticsOptions; + }, +}); + +// save the base class function +appierAnalyticsAdapter.originEnableAnalytics = appierAnalyticsAdapter.enableAnalytics; + +// override enableAnalytics so we can get access to the config passed in from the page +appierAnalyticsAdapter.enableAnalytics = function (config) { + if (this.initConfig(config)) { + appierAnalyticsAdapter.originEnableAnalytics(config); // call the base class function + } +}; + +adapterManager.registerAnalyticsAdapter({ + adapter: appierAnalyticsAdapter, + code: 'appierAnalytics' +}); diff --git a/modules/appierAnalyticsAdapter.md b/modules/appierAnalyticsAdapter.md new file mode 100644 index 00000000000..09f0676d054 --- /dev/null +++ b/modules/appierAnalyticsAdapter.md @@ -0,0 +1,23 @@ +# Overview + +Module Name: Appier Analytics Adapter +Module Type: Analytics Adapter +Maintainer: apn-dev@appier.com + +# Description + +Analytics adapter for Appier + +# Test Parameters + +``` +{ + provider: 'appierAnalytics', + options: { + 'configId': 'YOUR_CONFIG_ID', + 'affiliateId': 'YOUR_AFFILIATE_ID', + } +} +``` + +PS. [Prebid currency module](http://prebid.org/dev-docs/modules/currency.html) is required, please make sure your prebid code contains currency module code. diff --git a/test/spec/modules/appierAnalyticsAdapter_spec.js b/test/spec/modules/appierAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..07b9796a4cb --- /dev/null +++ b/test/spec/modules/appierAnalyticsAdapter_spec.js @@ -0,0 +1,709 @@ +import { + appierAnalyticsAdapter, getCpmInUsd, parseBidderCode, parseAdUnitCode, + ANALYTICS_VERSION, BIDDER_STATUS +} from 'modules/appierAnalyticsAdapter'; +import {expect} from 'chai'; +const events = require('src/events'); +const constants = require('src/constants.json'); + +const affiliateId = 'WhctHaViHtI'; +const configId = 'd9cc9a9be9b240eda17cf1c9a8a4b29c'; +const serverUrl = 'https://analytics.server.url/v1'; +const autoPick = 'none'; +const predictionId = '2a91ca5de54a4a2e89950af439f7a27f'; +const auctionId = 'b0b39610-b941-4659-a87c-de9f62d3e13e'; + +describe('Appier Prebid AnalyticsAdapter Testing', function () { + describe('event tracking and message cache manager', function () { + let xhr; + + beforeEach(function () { + const configOptions = { + affiliateId: affiliateId, + configId: configId, + server: serverUrl, + autoPick: autoPick, + predictionId: predictionId, + sampling: 0, + adSampling: 1, + }; + + appierAnalyticsAdapter.enableAnalytics({ + provider: 'appierAnalytics', + options: configOptions + }); + xhr = sinon.useFakeXMLHttpRequest(); + }); + + afterEach(function () { + appierAnalyticsAdapter.disableAnalytics(); + xhr.restore(); + }); + + describe('#getCpmInUsd()', function() { + it('should get bid cpm as currency is USD', function() { + const receivedBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'appier', + bidderCode: 'APPIER', + requestId: 'a1b2c3d4', + timeToRespond: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + ad: 'fake ad1' + }, + ] + const result = getCpmInUsd(receivedBids[0]); + expect(result).to.equal(0.1); + }); + }); + + describe('#parseBidderCode()', function() { + it('should get lower case bidder code from bidderCode field value', function() { + const receivedBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'appier', + bidderCode: 'APPIER', + requestId: 'a1b2c3d4', + timeToRespond: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + ad: 'fake ad1' + }, + ]; + const result = parseBidderCode(receivedBids[0]); + expect(result).to.equal('appier'); + }); + it('should get lower case bidder code from bidder field value as bidderCode field is missing', function() { + const receivedBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'APPIER', + bidderCode: '', + requestId: 'a1b2c3d4', + timeToRespond: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + ad: 'fake ad1' + }, + ]; + const result = parseBidderCode(receivedBids[0]); + expect(result).to.equal('appier'); + }); + }); + + describe('#parseAdUnitCode()', function() { + it('should get lower case adUnit code from adUnitCode field value', function() { + const receivedBids = [ + { + auctionId: auctionId, + adUnitCode: 'ADUNIT', + bidder: 'appier', + bidderCode: 'APPIER', + requestId: 'a1b2c3d4', + timeToRespond: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + ad: 'fake ad1' + }, + ]; + const result = parseAdUnitCode(receivedBids[0]); + expect(result).to.equal('adunit'); + }); + }); + + describe('#getCachedAuction()', function() { + const existing = {timeoutBids: [{}]}; + appierAnalyticsAdapter.cachedAuctions['test_auction_id'] = existing; + + it('should get the existing cached object if it exists', function() { + const result = appierAnalyticsAdapter.getCachedAuction('test_auction_id'); + + expect(result).to.equal(existing); + }); + + it('should create a new object and store it in the cache on cache miss', function() { + const result = appierAnalyticsAdapter.getCachedAuction('no_such_id'); + + expect(result).to.deep.include({ + timeoutBids: [], + }); + }); + }); + + describe('when formatting JSON payload sent to backend', function() { + const receivedBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'appier', + bidderCode: 'appier', + requestId: 'a1b2c3d4', + timeToRespond: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + ad: 'fake ad1' + }, + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'reippa', + bidderCode: 'reippa', + requestId: 'b2c3d4e5', + timeToRespond: 100, + cpm: 0.08, + currency: 'USD', + originalCpm: 0.08, + originalCurrency: 'USD', + ad: 'fake ad2' + }, + { + auctionId: auctionId, + adUnitCode: 'adunit_2', + bidder: 'appier', + bidderCode: 'appier', + requestId: 'c3d4e5f6', + timeToRespond: 120, + cpm: 0.09, + currency: 'USD', + originalCpm: 0.09, + originalCurrency: 'USD', + ad: 'fake ad3' + }, + ]; + const highestCpmBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'appier', + bidderCode: 'appier', + // No requestId + timeToRespond: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + ad: 'fake ad1' + } + ]; + const noBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_2', + bidder: 'appier', + bidderCode: 'appier', + bidId: 'a1b2c3d4', + } + ]; + const timeoutBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_2', + bidder: 'reippa', + bidderCode: 'reippa', + bidId: '00123d4c', + } + ]; + const withoutOriginalCpmBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_2', + bidder: 'appier', + bidderCode: 'appier', + requestId: 'c3d4e5f6', + timeToRespond: 120, + cpm: 0.29, + currency: 'USD', + originalCpm: '', + originalCurrency: 'USD', + ad: 'fake ad3' + }, + ]; + const withoutOriginalCurrencyBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_2', + bidder: 'appier', + bidderCode: 'appier', + requestId: 'c3d4e5f6', + timeToRespond: 120, + cpm: 0.09, + currency: 'USD', + originalCpm: 0.09, + originalCurrency: '', + ad: 'fake ad3' + }, + ]; + function assertHavingRequiredMessageFields(message) { + expect(message).to.include({ + version: ANALYTICS_VERSION, + auctionId: auctionId, + affiliateId: affiliateId, + configId: configId, + // referrer: window.location.href, + sampling: 0, + adSampling: 1, + prebid: '$prebid.version$', + // autoPick: 'manual', + }); + } + + describe('#createCommonMessage', function() { + it('should correctly serialize some common fields', function() { + const message = appierAnalyticsAdapter.createCommonMessage(auctionId); + + assertHavingRequiredMessageFields(message); + }); + }); + + describe('#serializeBidResponse', function() { + it('should handle BID properly and serialize bid price related fields', function() { + const result = appierAnalyticsAdapter.serializeBidResponse(receivedBids[0], BIDDER_STATUS.BID); + + expect(result).to.include({ + prebidWon: false, + isTimeout: false, + status: BIDDER_STATUS.BID, + time: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + cpmUsd: 0.1, + }); + }); + + it('should handle NO_BID properly and set status to noBid', function() { + const result = appierAnalyticsAdapter.serializeBidResponse(noBids[0], BIDDER_STATUS.NO_BID); + + expect(result).to.include({ + prebidWon: false, + isTimeout: false, + status: BIDDER_STATUS.NO_BID, + }); + }); + + it('should handle BID_WON properly and serialize bid price related fields', function() { + const result = appierAnalyticsAdapter.serializeBidResponse(receivedBids[0], BIDDER_STATUS.BID_WON); + + expect(result).to.include({ + prebidWon: true, + isTimeout: false, + status: BIDDER_STATUS.BID_WON, + time: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + cpmUsd: 0.1, + }); + }); + + it('should handle TIMEOUT properly and set status to timeout and isTimeout to true', function() { + const result = appierAnalyticsAdapter.serializeBidResponse(noBids[0], BIDDER_STATUS.TIMEOUT); + + expect(result).to.include({ + prebidWon: false, + isTimeout: true, + status: BIDDER_STATUS.TIMEOUT, + }); + }); + + it('should handle BID_WON properly and fill originalCpm field with cpm in missing originalCpm case', function() { + const result = appierAnalyticsAdapter.serializeBidResponse(withoutOriginalCpmBids[0], BIDDER_STATUS.BID_WON); + + expect(result).to.include({ + prebidWon: true, + isTimeout: false, + status: BIDDER_STATUS.BID_WON, + time: 120, + cpm: 0.29, + currency: 'USD', + originalCpm: 0.29, + originalCurrency: 'USD', + cpmUsd: 0.29, + }); + }); + + it('should handle BID_WON properly and fill originalCurrency field with currency in missing originalCurrency case', function() { + const result = appierAnalyticsAdapter.serializeBidResponse(withoutOriginalCurrencyBids[0], BIDDER_STATUS.BID_WON); + expect(result).to.include({ + prebidWon: true, + isTimeout: false, + status: BIDDER_STATUS.BID_WON, + time: 120, + cpm: 0.09, + currency: 'USD', + originalCpm: 0.09, + originalCurrency: 'USD', + cpmUsd: 0.09, + }); + }); + }); + + describe('#addBidResponseToMessage()', function() { + it('should add a bid response in the output message, grouped by adunit_id and bidder', function() { + const message = { + adUnits: {} + }; + appierAnalyticsAdapter.addBidResponseToMessage(message, noBids[0], BIDDER_STATUS.NO_BID); + + expect(message.adUnits).to.deep.include({ + 'adunit_2': { + 'appier': { + prebidWon: false, + isTimeout: false, + status: BIDDER_STATUS.NO_BID, + } + } + }); + }); + }); + + describe('#createBidMessage()', function() { + it('should format auction message sent to the backend', function() { + const args = { + auctionId: auctionId, + timestamp: 1234567890, + timeout: 3000, + auctionEnd: 1234567990, + adUnitCodes: ['adunit_1', 'adunit_2'], + bidsReceived: receivedBids, + noBids: noBids + }; + + const result = appierAnalyticsAdapter.createBidMessage(args, highestCpmBids, timeoutBids); + + assertHavingRequiredMessageFields(result); + expect(result).to.deep.include({ + auctionElapsed: 100, + timeout: 3000, + adUnits: { + 'adunit_1': { + 'appier': { + prebidWon: true, + isTimeout: false, + status: BIDDER_STATUS.BID, + time: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + cpmUsd: 0.1, + }, + 'reippa': { + prebidWon: false, + isTimeout: false, + status: BIDDER_STATUS.BID, + time: 100, + cpm: 0.08, + currency: 'USD', + originalCpm: 0.08, + originalCurrency: 'USD', + cpmUsd: 0.08, + } + }, + 'adunit_2': { + // this bid result exists in both bid and noBid arrays and should be treated as bid + 'appier': { + prebidWon: false, + isTimeout: false, + time: 120, + cpm: 0.09, + currency: 'USD', + originalCpm: 0.09, + originalCurrency: 'USD', + cpmUsd: 0.09, + status: BIDDER_STATUS.BID, + }, + 'reippa': { + prebidWon: false, + isTimeout: true, + status: BIDDER_STATUS.TIMEOUT, + } + } + } + }); + }); + }); + + describe('#createImpressionMessage()', function() { + it('should format message sent to the backend with the bid result', function() { + const bid = receivedBids[0]; + const result = appierAnalyticsAdapter.createImpressionMessage(bid); + + assertHavingRequiredMessageFields(result); + expect(result.adUnits).to.deep.include({ + 'adunit_1': { + 'appier': { + prebidWon: true, + isTimeout: false, + status: BIDDER_STATUS.BID_WON, + time: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + cpmUsd: 0.1, + } + } + }); + }); + }); + + describe('#createCreativeMessage()', function() { + it('should generate message sent to the backend with ad html grouped by adunit and bidder', function() { + const result = appierAnalyticsAdapter.createCreativeMessage(auctionId, receivedBids); + + assertHavingRequiredMessageFields(result); + expect(result.adUnits).to.deep.include({ + 'adunit_1': { + 'appier': { + ad: 'fake ad1' + }, + 'reippa': { + ad: 'fake ad2' + }, + }, + 'adunit_2': { + 'appier': { + ad: 'fake ad3' + } + } + }); + }); + }); + describe('#handleBidTimeout()', function() { + it('should cached the timeout bid as BID_TIMEOUT event was triggered', function() { + appierAnalyticsAdapter.cachedAuctions['test_timeout_auction_id'] = { 'timeoutBids': [] }; + const args = [{ + auctionId: 'test_timeout_auction_id', + timestamp: 1234567890, + timeout: 3000, + auctionEnd: 1234567990, + bidsReceived: receivedBids, + noBids: noBids + }]; + + appierAnalyticsAdapter.handleBidTimeout(args); + const result = appierAnalyticsAdapter.getCachedAuction('test_timeout_auction_id'); + expect(result).to.deep.include({ + timeoutBids: [{ + auctionId: 'test_timeout_auction_id', + timestamp: 1234567890, + timeout: 3000, + auctionEnd: 1234567990, + bidsReceived: receivedBids, + noBids: noBids + }] + }); + }); + }); + describe('#handleBidWon()', function() { + it('should call createImpressionMessage once as BID_WON event was triggered', function() { + sinon.spy(appierAnalyticsAdapter, 'createImpressionMessage'); + const receivedBids = [ + { + auctionId: auctionId, + adUnitCode: 'adunit_1', + bidder: 'appier', + bidderCode: 'appier', + requestId: 'a1b2c3d4', + timeToRespond: 72, + cpm: 0.1, + currency: 'USD', + originalCpm: 0.1, + originalCurrency: 'USD', + ad: 'fake ad1' + }, + ] + + appierAnalyticsAdapter.handleBidWon(receivedBids[0]); + sinon.assert.callCount(appierAnalyticsAdapter.createImpressionMessage, 1); + appierAnalyticsAdapter.createImpressionMessage.restore(); + }); + }); + }); + }); + + describe('Appier Analytics Adapter track handler ', function () { + const configOptions = { + affiliateId: affiliateId, + configId: configId, + server: serverUrl, + autoPick: autoPick, + sampling: 1, + adSampling: 1, + }; + + beforeEach(function () { + sinon.stub(events, 'getEvents').returns([]); + appierAnalyticsAdapter.enableAnalytics({ + provider: 'appierAnalytics', + options: configOptions + }); + }); + + afterEach(function () { + appierAnalyticsAdapter.disableAnalytics(); + events.getEvents.restore(); + }); + + it('should call handleBidWon as BID_WON trigger event', function() { + sinon.spy(appierAnalyticsAdapter, 'handleBidWon'); + events.emit(constants.EVENTS.BID_WON, {}); + sinon.assert.callCount(appierAnalyticsAdapter.handleBidWon, 1); + appierAnalyticsAdapter.handleBidWon.restore(); + }); + + it('should call handleBidTimeout as BID_TIMEOUT trigger event', function() { + sinon.spy(appierAnalyticsAdapter, 'handleBidTimeout'); + events.emit(constants.EVENTS.BID_TIMEOUT, {}); + sinon.assert.callCount(appierAnalyticsAdapter.handleBidTimeout, 1); + appierAnalyticsAdapter.handleBidTimeout.restore(); + }); + + it('should call handleAuctionEnd as AUCTION_END trigger event', function() { + sinon.spy(appierAnalyticsAdapter, 'handleAuctionEnd'); + events.emit(constants.EVENTS.AUCTION_END, {}); + sinon.assert.callCount(appierAnalyticsAdapter.handleAuctionEnd, 1); + appierAnalyticsAdapter.handleAuctionEnd.restore(); + }); + + it('should call createCreativeMessage as AUCTION_END trigger event in adSampled is true', function() { + const configOptions = { + options: { + affiliateId: affiliateId, + configId: configId, + server: serverUrl, + autoPick: autoPick, + sampling: 1, + adSampling: 1, + adSampled: true, + } + }; + const args = { + auctionId: 'test_timeout_auction_id', + timestamp: 1234567890, + timeout: 3000, + auctionEnd: 1234567990, + }; + appierAnalyticsAdapter.initConfig(configOptions); + sinon.stub(appierAnalyticsAdapter, 'sendEventMessage').returns({}); + sinon.stub(appierAnalyticsAdapter, 'createBidMessage').returns({}); + sinon.spy(appierAnalyticsAdapter, 'createCreativeMessage'); + events.emit(constants.EVENTS.AUCTION_END, args); + sinon.assert.callCount(appierAnalyticsAdapter.createCreativeMessage, 1); + appierAnalyticsAdapter.sendEventMessage.restore(); + appierAnalyticsAdapter.createBidMessage.restore(); + appierAnalyticsAdapter.createCreativeMessage.restore(); + }); + }); + + describe('enableAnalytics and config parser', function () { + const configOptions = { + affiliateId: affiliateId, + configId: configId, + server: serverUrl, + autoPick: autoPick, + sampling: 0, + adSampling: 1, + }; + + beforeEach(function () { + appierAnalyticsAdapter.enableAnalytics({ + provider: 'appierAnalytics', + options: configOptions + }); + }); + + afterEach(function () { + appierAnalyticsAdapter.disableAnalytics(); + }); + + it('should parse config correctly with optional values', function () { + expect(appierAnalyticsAdapter.getAnalyticsOptions().options).to.deep.equal(configOptions); + expect(appierAnalyticsAdapter.getAnalyticsOptions().affiliateId).to.equal(configOptions.affiliateId); + expect(appierAnalyticsAdapter.getAnalyticsOptions().configId).to.equal(configOptions.configId); + expect(appierAnalyticsAdapter.getAnalyticsOptions().server).to.equal(configOptions.server); + expect(appierAnalyticsAdapter.getAnalyticsOptions().autoPick).to.equal(configOptions.autoPick); + expect(appierAnalyticsAdapter.getAnalyticsOptions().sampled).to.equal(false); + expect(appierAnalyticsAdapter.getAnalyticsOptions().adSampled).to.equal(true); + }); + + it('should not enable Analytics when affiliateId is missing', function () { + const configOptions = { + options: { + configId: configId + } + }; + const validConfig = appierAnalyticsAdapter.initConfig(configOptions); + expect(validConfig).to.equal(false); + }); + + it('should use DEFAULT_SERVER when server is missing', function () { + const configOptions = { + options: { + configId: configId, + affiliateId: affiliateId + } + }; + appierAnalyticsAdapter.initConfig(configOptions); + expect(appierAnalyticsAdapter.getAnalyticsOptions().server).to.equal('https://prebid-analytics.c.appier.net/v1'); + }); + + it('should use null when autoPick is missing', function () { + const configOptions = { + options: { + configId: configId, + affiliateId: affiliateId + } + }; + appierAnalyticsAdapter.initConfig(configOptions); + expect(appierAnalyticsAdapter.getAnalyticsOptions().autoPick).to.equal(null); + }); + + it('should not enable Analytics when configId is missing', function () { + const configOptions = { + options: { + affiliateId: affiliateId + } + }; + const validConfig = appierAnalyticsAdapter.initConfig(configOptions); + expect(validConfig).to.equal(false); + }); + + it('should fall back to default value when sampling factor is not number', function () { + const configOptions = { + options: { + affiliateId: affiliateId, + configId: configId, + sampling: 'string', + adSampling: 'string' + } + }; + appierAnalyticsAdapter.enableAnalytics({ + provider: 'appierAnalytics', + options: configOptions + }); + + expect(appierAnalyticsAdapter.getAnalyticsOptions().sampled).to.equal(false); + expect(appierAnalyticsAdapter.getAnalyticsOptions().adSampled).to.equal(true); + }); + }); +}); From cacb6e7deb7ac75b33048b3e2cc176be9f980875 Mon Sep 17 00:00:00 2001 From: naegelin Date: Mon, 10 Jun 2019 21:33:41 +0200 Subject: [PATCH 111/146] Adding aliases for adsparc and safereach to aardvark adapter (#3848) * Update aardvark adapter Adding aliases for adsparc and safereach * Update aardvarkBidAdapter.js * Removing adsparc from serverbid adapters Per request of @sanjayamolligoda at adsparc. Removing alias from serverbid. --- modules/aardvarkBidAdapter.js | 1 + modules/serverbidBidAdapter.js | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/aardvarkBidAdapter.js b/modules/aardvarkBidAdapter.js index 239800ce7fa..81d393a3859 100644 --- a/modules/aardvarkBidAdapter.js +++ b/modules/aardvarkBidAdapter.js @@ -15,6 +15,7 @@ export function resetUserSync() { export const spec = { code: BIDDER_CODE, + aliases: ['adsparc', 'safereach'], isBidRequestValid: function(bid) { return ((typeof bid.params.ai === 'string') && !!bid.params.ai.length && diff --git a/modules/serverbidBidAdapter.js b/modules/serverbidBidAdapter.js index 037721061e2..faeeafdd53f 100644 --- a/modules/serverbidBidAdapter.js +++ b/modules/serverbidBidAdapter.js @@ -16,9 +16,6 @@ const CONFIG = { 'insticator': { 'BASE_URI': 'https://e.serverbid.com/api/v2' }, - 'adsparc': { - 'BASE_URI': 'https://e.serverbid.com/api/v2' - }, 'automatad': { 'BASE_URI': 'https://e.serverbid.com/api/v2' }, @@ -38,7 +35,7 @@ let bidder = 'serverbid'; export const spec = { code: BIDDER_CODE, - aliases: ['connectad', 'onefiftytwo', 'insticator', 'adsparc', 'automatad', 'archon', 'buysellads', 'answermedia'], + aliases: ['connectad', 'onefiftytwo', 'insticator', 'automatad', 'archon', 'buysellads', 'answermedia'], /** * Determines whether or not the given bid request is valid. From 2794cd8f203b155ef6d2e7218489a92a1c2f90c5 Mon Sep 17 00:00:00 2001 From: mcamustlr <51314002+mcamustlr@users.noreply.github.com> Date: Wed, 12 Jun 2019 18:04:40 +0200 Subject: [PATCH 112/146] Add slimCut bid adapter (#3880) --- modules/slimcutBidAdapter.js | 128 ++++++++++++ modules/slimcutBidAdapter.md | 45 +++++ test/spec/modules/slimcutBidAdapter_spec.js | 212 ++++++++++++++++++++ 3 files changed, 385 insertions(+) create mode 100644 modules/slimcutBidAdapter.js create mode 100644 modules/slimcutBidAdapter.md create mode 100644 test/spec/modules/slimcutBidAdapter_spec.js diff --git a/modules/slimcutBidAdapter.js b/modules/slimcutBidAdapter.js new file mode 100644 index 00000000000..def490d6ab9 --- /dev/null +++ b/modules/slimcutBidAdapter.js @@ -0,0 +1,128 @@ +import * as utils from 'src/utils'; +import { registerBidder } from 'src/adapters/bidderFactory'; +import { ajax } from 'src/ajax'; + +const BIDDER_CODE = 'slimcut'; +const ENDPOINT_URL = '//sb.freeskreen.com/pbr'; + +export const spec = { + code: BIDDER_CODE, + aliases: ['scm'], + supportedMediaTypes: ['video', 'banner'], + + /** + * 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) { + let isValid = false; + if (typeof bid.params !== 'undefined' && !isNaN(parseInt(utils.getValue(bid.params, 'placementId'))) && parseInt(utils.getValue(bid.params, 'placementId')) > 0) { + isValid = true; + } + return isValid; + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} an array of bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests, bidderRequest) { + const bids = validBidRequests.map(buildRequestObject); + const payload = { + referrer: getReferrerInfo(bidderRequest), + data: bids, + deviceWidth: screen.width + }; + + let gdpr = bidderRequest.gdprConsent; + if (bidderRequest && gdpr) { + let isCmp = (typeof gdpr.gdprApplies === 'boolean') + let isConsentString = (typeof gdpr.consentString === 'string') + payload.gdpr_iab = { + consent: isConsentString ? gdpr.consentString : '', + status: isCmp ? gdpr.gdprApplies : -1 + }; + } + + const payloadString = JSON.stringify(payload); + return { + method: 'POST', + url: ENDPOINT_URL, + data: payloadString, + }; + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse, request) { + const bidResponses = []; + serverResponse = serverResponse.body; + + if (serverResponse.responses) { + serverResponse.responses.forEach(function (bid) { + const bidResponse = { + cpm: bid.cpm, + width: bid.width, + height: bid.height, + currency: bid.currency, + netRevenue: bid.netRevenue, + ttl: bid.ttl, + ad: bid.ad, + requestId: bid.requestId, + creativeId: bid.creativeId, + transactionId: bid.tranactionId, + winUrl: bid.winUrl + }; + bidResponses.push(bidResponse); + }); + } + return bidResponses; + }, + + getUserSyncs: function(syncOptions, serverResponses) { + if (syncOptions.iframeEnabled) { + return [{ + type: 'iframe', + url: '//sb.freeskreen.com/async_usersync.html' + }]; + } + return []; + }, + + onBidWon: function(bid) { + ajax(bid.winUrl + bid.cpm, null); + } +} + +function buildRequestObject(bid) { + const reqObj = {}; + let placementId = utils.getValue(bid.params, 'placementId'); + + reqObj.sizes = utils.parseSizesInput(bid.sizes); + reqObj.bidId = utils.getBidIdParameter('bidId', bid); + reqObj.bidderRequestId = utils.getBidIdParameter('bidderRequestId', bid); + reqObj.placementId = parseInt(placementId); + reqObj.adUnitCode = utils.getBidIdParameter('adUnitCode', bid); + reqObj.auctionId = utils.getBidIdParameter('auctionId', bid); + reqObj.transactionId = utils.getBidIdParameter('transactionId', bid); + + return reqObj; +} + +function getReferrerInfo(bidderRequest) { + let ref = window.location.href; + if (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { + ref = bidderRequest.refererInfo.referer; + } + return ref; +} + +registerBidder(spec); diff --git a/modules/slimcutBidAdapter.md b/modules/slimcutBidAdapter.md new file mode 100644 index 00000000000..1d83c8cae6f --- /dev/null +++ b/modules/slimcutBidAdapter.md @@ -0,0 +1,45 @@ +# Overview + +**Module Name**: Slimcut Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: support@slimcut.com + +# Description + +Use `slimcut` as bidder. + +`placementId` is required and must be integer. + +The Slimcut adapter requires setup and approval from the Slimcut team. +Please reach out to your account manager for more information. + +# Test Parameters + +``` + var adUnits = [ + { + code: 'test-div', + sizes: [[640, 480]], + bids: [ + { + bidder: "slimcut", + params: { + placementId: 1234 + } + } + ] + } + ]; +``` + +## UserSync example + +``` +pbjs.setConfig({ + userSync: { + iframeEnabled: true, + syncEnabled: true, + syncDelay: 1 + } +}); +``` diff --git a/test/spec/modules/slimcutBidAdapter_spec.js b/test/spec/modules/slimcutBidAdapter_spec.js new file mode 100644 index 00000000000..92649bf3556 --- /dev/null +++ b/test/spec/modules/slimcutBidAdapter_spec.js @@ -0,0 +1,212 @@ +import {expect} from 'chai'; +import {spec} from 'modules/slimcutBidAdapter'; +import {newBidder} from 'src/adapters/bidderFactory'; + +const ENDPOINT = '//sb.freeskreen.com/pbr'; +const AD_SCRIPT = '"'; + +describe('slimcutBidAdapter', function() { + const adapter = newBidder(spec); + + describe('inherited functions', function() { + it('exists and is a function', function() { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function() { + let bid = { + 'bidder': 'slimcut', + 'params': { + 'placementId': 83 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '3c871ffa8ef14c', + 'bidderRequestId': 'b41642f1aee381', + 'auctionId': '4e156668c977d7' + }; + + it('should return true when required params found', function() { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when placementId is not valid (letters)', function() { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'placementId': 'ABCD' + }; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when placementId < 0', function() { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'placementId': -1 + }; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when required params are not passed', function() { + let bid = Object.assign({}, bid); + delete bid.params; + + bid.params = {}; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function() { + let bidRequests = [ + { + 'bidder': 'teads', + 'params': { + 'placementId': 10433394 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '3c871ffa8ef14c', + 'bidderRequestId': 'b41642f1aee381', + 'auctionId': '4e156668c977d7', + 'deviceWidth': 1680 + } + ]; + + let bidderResquestDefault = { + 'auctionId': '4e156668c977d7', + 'bidderRequestId': 'b41642f1aee381', + 'timeout': 3000 + }; + + it('sends bid request to ENDPOINT via POST', function() { + const request = spec.buildRequests(bidRequests, bidderResquestDefault); + + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('POST'); + }); + + it('should send GDPR to endpoint', function() { + let consentString = 'JRJ8RKfDeBNsERRDCSAAZ+A=='; + let bidderRequest = { + 'auctionId': '4e156668c977d7', + 'bidderRequestId': 'b41642f1aee381', + 'timeout': 3000, + 'gdprConsent': { + 'consentString': consentString, + 'gdprApplies': true, + 'vendorData': { + 'hasGlobalConsent': false + } + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.gdpr_iab).to.exist; + expect(payload.gdpr_iab.consent).to.equal(consentString); + }); + + it('should add referer info to payload', function () { + const bidRequest = Object.assign({}, bidRequests[0]) + const bidderRequest = { + refererInfo: { + referer: 'http://example.com/page.html', + reachedTop: true, + numIframes: 2 + } + } + const request = spec.buildRequests([bidRequest], bidderRequest); + const payload = JSON.parse(request.data); + + expect(payload.referrer).to.exist; + expect(payload.referrer).to.deep.equal('http://example.com/page.html') + }); + }); + + describe('getUserSyncs', () => { + let bids = { + 'body': { + 'responses': [{ + 'ad': AD_SCRIPT, + 'cpm': 0.5, + 'currency': 'USD', + 'height': 250, + 'netRevenue': true, + 'requestId': '3ede2a3fa0db94', + 'ttl': 360, + 'width': 300, + 'creativeId': 'er2ee', + 'transactionId': 'deadb33f', + 'winUrl': 'https://sb.freeskreen.com/win' + }] + } + }; + + it('should get the correct number of sync urls', () => { + let urls = spec.getUserSyncs({iframeEnabled: true}, bids); + expect(urls.length).to.equal(1); + expect(urls[0].url).to.equal('//sb.freeskreen.com/async_usersync.html'); + }); + + it('should return no url if not iframe enabled', () => { + let urls = spec.getUserSyncs({iframeEnabled: false}, bids); + expect(urls.length).to.equal(0); + }); + }); + + describe('interpretResponse', function() { + let bids = { + 'body': { + 'responses': [{ + 'ad': AD_SCRIPT, + 'cpm': 0.5, + 'currency': 'USD', + 'height': 250, + 'netRevenue': true, + 'requestId': '3ede2a3fa0db94', + 'ttl': 360, + 'width': 300, + 'creativeId': 'er2ee', + 'transactionId': 'deadb33f', + 'winUrl': 'https://sb.freeskreen.com/win' + }] + } + }; + + it('should get correct bid response', function() { + let expectedResponse = [{ + 'cpm': 0.5, + 'width': 300, + 'height': 250, + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360, + 'ad': AD_SCRIPT, + 'requestId': '3ede2a3fa0db94', + 'creativeId': 'er2ee', + 'transactionId': 'deadb33f', + 'winUrl': 'https://sb.freeskreen.com/win' + }]; + + let result = spec.interpretResponse(bids); + expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + }); + + it('handles nobid responses', function() { + let bids = { + 'body': { + 'responses': [] + } + }; + + let result = spec.interpretResponse(bids); + expect(result.length).to.equal(0); + }); + }); +}); From 5715a026eae27e07ab6edc3a2593bfc97ec35204 Mon Sep 17 00:00:00 2001 From: Rafal Pastuszak Date: Wed, 12 Jun 2019 21:53:37 +0100 Subject: [PATCH 113/146] feat(unruly-bid-adapter): use bidResponse siteId when configuring the renderer (#3865) * feat(unruly-bid-adapter): use bidResponse siteId when configuring the renderer * feat(unruly-bid-adapter): bail if siteId is missing * feat(unruly-bid-adapter): log (but don't throw) when siteId is no present --- modules/unrulyBidAdapter.js | 16 ++++- test/spec/modules/unrulyBidAdapter_spec.js | 81 +++++++++++++++++++--- 2 files changed, 85 insertions(+), 12 deletions(-) diff --git a/modules/unrulyBidAdapter.js b/modules/unrulyBidAdapter.js index 5647d2cd6a3..580357a1978 100644 --- a/modules/unrulyBidAdapter.js +++ b/modules/unrulyBidAdapter.js @@ -4,9 +4,12 @@ import { registerBidder } from '../src/adapters/bidderFactory' import { VIDEO } from '../src/mediaTypes' function configureUniversalTag (exchangeRenderer) { + if (!exchangeRenderer.config) throw new Error('UnrulyBidAdapter: Missing renderer config.') + if (!exchangeRenderer.config.siteId) throw new Error('UnrulyBidAdapter: Missing renderer siteId.') + parent.window.unruly = parent.window.unruly || {}; parent.window.unruly['native'] = parent.window.unruly['native'] || {}; - parent.window.unruly['native'].siteId = parent.window.unruly['native'].siteId || exchangeRenderer.siteId; + parent.window.unruly['native'].siteId = parent.window.unruly['native'].siteId || exchangeRenderer.config.siteId; parent.window.unruly['native'].supplyMode = 'prebid'; } @@ -35,9 +38,18 @@ const serverResponseToBid = (bid, rendererInstance) => ({ const buildPrebidResponseAndInstallRenderer = bids => bids - .filter(serverBid => !!utils.deepAccess(serverBid, 'ext.renderer')) + .filter(serverBid => { + const hasConfig = !!utils.deepAccess(serverBid, 'ext.renderer.config'); + const hasSiteId = !!utils.deepAccess(serverBid, 'ext.renderer.config.siteId'); + + if (!hasConfig) utils.logError(new Error('UnrulyBidAdapter: Missing renderer config.')); + if (!hasSiteId) utils.logError(new Error('UnrulyBidAdapter: Missing renderer siteId.')); + + return hasSiteId + }) .map(serverBid => { const exchangeRenderer = utils.deepAccess(serverBid, 'ext.renderer'); + configureUniversalTag(exchangeRenderer); configureRendererQueue(); diff --git a/test/spec/modules/unrulyBidAdapter_spec.js b/test/spec/modules/unrulyBidAdapter_spec.js index e39f9a8e996..cb30fcf1a9d 100644 --- a/test/spec/modules/unrulyBidAdapter_spec.js +++ b/test/spec/modules/unrulyBidAdapter_spec.js @@ -18,7 +18,10 @@ describe('UnrulyAdapter', function () { 'statusCode': statusCode, 'renderer': { 'id': 'unruly_inarticle', - 'config': {}, + 'config': { + 'siteId': 123456, + 'targetingUUID': 'xxx-yyy-zzz' + }, 'url': 'https://video.unrulymedia.com/native/prebid-loader.js' }, 'adUnitCode': adUnitCode @@ -125,10 +128,10 @@ describe('UnrulyAdapter', function () { it('should be a function', function () { expect(typeof adapter.interpretResponse).to.equal('function'); }); - it('should return empty array when serverResponse is undefined', function () { + it('should return [] when serverResponse is undefined', function () { expect(adapter.interpretResponse()).to.deep.equal([]); }); - it('should return empty array when serverResponse has no bids', function () { + it('should return [] when serverResponse has no bids', function () { const mockServerResponse = { body: { bids: [] } }; expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([]) }); @@ -157,7 +160,13 @@ describe('UnrulyAdapter', function () { expect(fakeRenderer.setRender.called).to.be.false; const mockReturnedBid = createOutStreamExchangeBid({adUnitCode: 'video1', bidId: 'mockBidId'}); - const mockRenderer = { url: 'value: mockRendererURL' }; + const mockRenderer = { + url: 'value: mockRendererURL', + config: { + siteId: 123456, + targetingUUID: 'xxx-yyy-zzz' + } + }; mockReturnedBid.ext.renderer = mockRenderer; const mockServerResponse = createExchangeResponse(mockReturnedBid); @@ -173,6 +182,58 @@ describe('UnrulyAdapter', function () { sinon.assert.calledWithExactly(fakeRenderer.setRender, sinon.match.func) }); + it('should return [] and log if bidResponse renderer config is not available', function () { + sinon.assert.notCalled(utils.logError) + + expect(Renderer.install.called).to.be.false; + expect(fakeRenderer.setRender.called).to.be.false; + + const mockReturnedBid = createOutStreamExchangeBid({adUnitCode: 'video1', bidId: 'mockBidId'}); + const mockRenderer = { + url: 'value: mockRendererURL' + }; + mockReturnedBid.ext.renderer = mockRenderer; + const mockServerResponse = createExchangeResponse(mockReturnedBid); + + expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([]); + + const logErrorCalls = utils.logError.getCalls(); + expect(logErrorCalls.length).to.equal(2); + + const [ configErrorCall, siteIdErrorCall ] = logErrorCalls; + + expect(configErrorCall.args.length).to.equal(1); + expect(configErrorCall.args[0].message).to.equal('UnrulyBidAdapter: Missing renderer config.'); + + expect(siteIdErrorCall.args.length).to.equal(1); + expect(siteIdErrorCall.args[0].message).to.equal('UnrulyBidAdapter: Missing renderer siteId.'); + }); + + it('should return [] and log if siteId is not available', function () { + sinon.assert.notCalled(utils.logError) + + expect(Renderer.install.called).to.be.false; + expect(fakeRenderer.setRender.called).to.be.false; + + const mockReturnedBid = createOutStreamExchangeBid({adUnitCode: 'video1', bidId: 'mockBidId'}); + const mockRenderer = { + url: 'value: mockRendererURL', + config: {} + }; + mockReturnedBid.ext.renderer = mockRenderer; + const mockServerResponse = createExchangeResponse(mockReturnedBid); + + expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([]); + + const logErrorCalls = utils.logError.getCalls(); + expect(logErrorCalls.length).to.equal(1); + + const [ siteIdErrorCall ] = logErrorCalls; + + expect(siteIdErrorCall.args.length).to.equal(1); + expect(siteIdErrorCall.args[0].message).to.equal('UnrulyBidAdapter: Missing renderer siteId.'); + }); + it('bid is placed on the bid queue when render is called', function () { const exchangeBid = createOutStreamExchangeBid({ adUnitCode: 'video', vastUrl: 'value: vastUrl' }); const exchangeResponse = createExchangeResponse(exchangeBid); @@ -191,7 +252,7 @@ describe('UnrulyAdapter', function () { expect(sentRendererConfig.vastUrl).to.equal('value: vastUrl'); expect(sentRendererConfig.renderer).to.equal(fakeRenderer); expect(sentRendererConfig.adUnitCode).to.equal('video') - }) + }); it('should ensure that renderer is placed in Prebid supply mode', function () { const mockExchangeBid = createOutStreamExchangeBid({adUnitCode: 'video1', bidId: 'mockBidId'}); @@ -237,13 +298,13 @@ describe('UnrulyAdapter', function () { type: 'iframe', url: 'https://video.unrulymedia.com/iframes/third-party-iframes.html' }) - }) + }); it('should append consent params if gdpr does apply and consent is given', () => { const mockConsent = { gdprApplies: true, consentString: 'hello' - } + }; const response = {} const syncOptions = { iframeEnabled: true } const syncs = adapter.getUserSyncs(syncOptions, response, mockConsent) @@ -251,14 +312,14 @@ describe('UnrulyAdapter', function () { type: 'iframe', url: 'https://video.unrulymedia.com/iframes/third-party-iframes.html?gdpr=1&gdpr_consent=hello' }) - }) + }); it('should append consent param if gdpr applies and no consent is given', () => { const mockConsent = { gdprApplies: true, consentString: {} - } - const response = {} + }; + const response = {}; const syncOptions = { iframeEnabled: true } const syncs = adapter.getUserSyncs(syncOptions, response, mockConsent) expect(syncs[0]).to.deep.equal({ From 9598148f3f6dfb129b3193718e3cd5c2767afe5b Mon Sep 17 00:00:00 2001 From: Joshua Casingal Date: Wed, 12 Jun 2019 17:04:28 -0400 Subject: [PATCH 114/146] [BID-3479] - Add BidResponse.meta.dspid for OpenX (#3895) * [BID-3479] - Add BidResponse.meta.dspid for OpenX * Update version number to 2.1.7 --- modules/openxBidAdapter.js | 6 +++++- test/spec/modules/openxBidAdapter_spec.js | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index 9719fbb635a..8236be8c2e5 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -8,7 +8,7 @@ import {parse} from '../src/url'; const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; const BIDDER_CODE = 'openx'; const BIDDER_CONFIG = 'hb_pb'; -const BIDDER_VERSION = '2.1.6'; +const BIDDER_VERSION = '2.1.7'; let shouldSendBoPixel = true; @@ -121,6 +121,10 @@ function createBannerBidResponses(oxResponseObj, {bids, startTime}) { bidResponse.meta.brandId = adUnit.brand_id; } + if (adUnit.adv_id) { + bidResponse.meta.dspid = adUnit.adv_id; + } + bidResponses.push(bidResponse); registerBeacon(BANNER, adUnit, startTime); diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index ee8e452f707..0fda846faa1 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -1218,6 +1218,10 @@ describe('OpenxAdapter', function () { expect(bid.meta.brandId).to.equal(DEFAULT_TEST_ARJ_AD_UNIT.brand_id); }); + it('should return a brand ID', function () { + expect(bid.meta.dspid).to.equal(DEFAULT_TEST_ARJ_AD_UNIT.adv_id); + }); + it('should register a beacon', function () { resetBoPixel(); spec.interpretResponse({body: bidResponse}, bidRequest); From 96d46b159887a0f0a1f4d43e760e571ba5f9bfe7 Mon Sep 17 00:00:00 2001 From: Samuel Horwitz Date: Wed, 12 Jun 2019 17:12:08 -0400 Subject: [PATCH 115/146] kargo session id (#3897) --- modules/kargoBidAdapter.js | 8 ++++++++ test/spec/modules/kargoBidAdapter_spec.js | 16 +++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index 2c602186547..5c3d59bd241 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -23,6 +23,7 @@ export const spec = { bidSizes[bid.bidId] = bid.sizes; }); const transformedParams = Object.assign({}, { + sessionId: spec._getSessionId(), timeout: bidderRequest.timeout, currency: currency, cpmGranularity: 1, @@ -183,6 +184,13 @@ export const spec = { }; }, + _getSessionId() { + if (!spec._sessionId) { + spec._sessionId = spec._generateRandomUuid(); + } + return spec._sessionId; + }, + _generateRandomUuid() { try { // crypto.getRandomValues is supported everywhere but Opera Mini for years diff --git a/test/spec/modules/kargoBidAdapter_spec.js b/test/spec/modules/kargoBidAdapter_spec.js index 5d53a4e9c95..a6779f518a0 100644 --- a/test/spec/modules/kargoBidAdapter_spec.js +++ b/test/spec/modules/kargoBidAdapter_spec.js @@ -34,7 +34,7 @@ describe('kargo adapter tests', function () { }); describe('build request', function() { - var bids, undefinedCurrency, noAdServerCurrency, cookies = [], localStorageItems = []; + var bids, undefinedCurrency, noAdServerCurrency, cookies = [], localStorageItems = [], sessionIds = []; beforeEach(function () { undefinedCurrency = false; @@ -212,6 +212,10 @@ describe('kargo adapter tests', function () { setCookie('krg_crb', getEmptyKrgCrbOldStyle()); } + function getSessionId() { + return spec._sessionId; + } + function getExpectedKrakenParams(excludeUserIds, excludeKrux, expectedRawCRB, expectedRawCRBCookie) { var base = { timeout: 200, @@ -302,6 +306,8 @@ describe('kargo adapter tests', function () { function testBuildRequests(expected) { var request = spec.buildRequests(bids, {timeout: 200, foo: 'bar'}); + expected.sessionId = getSessionId(); + sessionIds.push(expected.sessionId); var krakenParams = JSON.parse(decodeURIComponent(request.data.slice(5))); expect(request.data.slice(0, 5)).to.equal('json='); expect(request.url).to.equal('https://krk.kargo.com/api/v2/bid'); @@ -310,6 +316,14 @@ describe('kargo adapter tests', function () { expect(request.timeout).to.equal(200); expect(request.foo).to.equal('bar'); expect(krakenParams).to.deep.equal(expected); + // Make sure session ID stays the same across requests simulating multiple auctions on one page load + for (let i in sessionIds) { + if (i == 0) { + continue; + } + let sessionId = sessionIds[i]; + expect(sessionIds[0]).to.equal(sessionId); + } } it('works when all params and localstorage and cookies are correctly set', function() { From 98db99cf7a7e4db2ad6fcd122bee1ddc0cc4eca7 Mon Sep 17 00:00:00 2001 From: Rich Snapp Date: Wed, 12 Jun 2019 15:35:26 -0600 Subject: [PATCH 116/146] allow endpoint configuration for rdn adapter (#3902) --- modules/rdnBidAdapter.js | 3 ++- test/spec/modules/rdnBidAdapter_spec.js | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/modules/rdnBidAdapter.js b/modules/rdnBidAdapter.js index f93145b5377..d85b307263d 100644 --- a/modules/rdnBidAdapter.js +++ b/modules/rdnBidAdapter.js @@ -1,6 +1,7 @@ import { registerBidder } from '../src/adapters/bidderFactory' import * as utils from '../src/utils' import { BANNER } from '../src/mediaTypes' +import { config } from '../src/config' const BIDDER_CODE = 'rdn' const ENDPOINT = 'https://s-bid.rmp.rakuten.co.jp/h' @@ -14,7 +15,7 @@ export const spec = { const params = bid.params bidRequests.push({ method: 'GET', - url: ENDPOINT, + url: config.getConfig('rdn.endpoint') || ENDPOINT, data: { bi: bid.bidId, t: params.adSpotId, diff --git a/test/spec/modules/rdnBidAdapter_spec.js b/test/spec/modules/rdnBidAdapter_spec.js index 1c5958c8065..1fef1c7bf3d 100644 --- a/test/spec/modules/rdnBidAdapter_spec.js +++ b/test/spec/modules/rdnBidAdapter_spec.js @@ -2,10 +2,20 @@ import { expect } from 'chai' import * as utils from 'src/utils' import { spec } from 'modules/rdnBidAdapter' import { newBidder } from 'src/adapters/bidderFactory' +import {config} from '../../../src/config'; describe('rdnBidAdapter', function() { const adapter = newBidder(spec); const ENDPOINT = 'https://s-bid.rmp.rakuten.co.jp/h'; + let sandbox; + + beforeEach(function() { + config.resetConfig(); + }); + + afterEach(function () { + config.resetConfig(); + }); describe('inherited functions', () => { it('exists and is a function', () => { @@ -53,6 +63,16 @@ describe('rdnBidAdapter', function() { expect(request.url).to.equal(ENDPOINT); expect(request.method).to.equal('GET') }) + + it('allows url override', () => { + config.setConfig({ + rdn: { + endpoint: '//test.rakuten.com' + } + }); + const request = spec.buildRequests(bidRequests)[0]; + expect(request.url).to.equal('//test.rakuten.com'); + }) }); describe('interpretResponse', () => { From 83aa229ea2a2747a329ca831d1ab74547d8a2eb6 Mon Sep 17 00:00:00 2001 From: Aleksa Trajkovic Date: Thu, 13 Jun 2019 18:08:45 +0200 Subject: [PATCH 117/146] aardvark adapter, add width & height params (#3892) --- modules/aardvarkBidAdapter.js | 13 +++- test/spec/modules/aardvarkBidAdapter_spec.js | 76 ++++++++++++++++++++ 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/modules/aardvarkBidAdapter.js b/modules/aardvarkBidAdapter.js index 81d393a3859..9caaaaa747c 100644 --- a/modules/aardvarkBidAdapter.js +++ b/modules/aardvarkBidAdapter.js @@ -29,12 +29,17 @@ export const spec = { var referer = bidderRequest.refererInfo.referer; var pageCategories = []; var tdId = ''; + var width = window.innerWidth; + var height = window.innerHeight; // This reference to window.top can cause issues when loaded in an iframe if not protected with a try/catch. try { - if (window.top.rtkcategories && Array.isArray(window.top.rtkcategories)) { - pageCategories = window.top.rtkcategories; + var topWin = utils.getWindowTop(); + if (topWin.rtkcategories && Array.isArray(topWin.rtkcategories)) { + pageCategories = topWin.rtkcategories; } + width = topWin.innerWidth; + height = topWin.innerHeight; } catch (e) {} if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.tdid'))) { @@ -49,7 +54,9 @@ export const spec = { payload: { version: 1, jsonp: false, - rtkreferer: referer + rtkreferer: referer, + w: width, + h: height }, endpoint: DEFAULT_ENDPOINT }; diff --git a/test/spec/modules/aardvarkBidAdapter_spec.js b/test/spec/modules/aardvarkBidAdapter_spec.js index 727527acf29..b532fa4264a 100644 --- a/test/spec/modules/aardvarkBidAdapter_spec.js +++ b/test/spec/modules/aardvarkBidAdapter_spec.js @@ -1,4 +1,5 @@ import { expect } from 'chai'; +import * as utils from 'src/utils'; import { spec, resetUserSync } from 'modules/aardvarkBidAdapter'; describe('aardvarkAdapterTest', function () { @@ -343,4 +344,79 @@ describe('aardvarkAdapterTest', function () { expect(syncs[0].url).to.equal('//sync.rtk.io/cs?g=1&c=BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA'); }); }); + + describe('reading window.top properties', function () { + const bidCategories = ['bcat1', 'bcat2', 'bcat3']; + const bidRequests = [{ + bidder: 'aardvark', + params: { + ai: 'xiby', + sc: 'TdAx', + host: 'adzone.pub.com', + categories: bidCategories + }, + adUnitCode: 'RTK_aaaa', + transactionId: '1b8389fe-615c-482d-9f1a-177fb8f7d5b0', + sizes: [300, 250], + bidId: '1abgs362e0x48a8', + bidderRequestId: '70deaff71c281d', + auctionId: '5c66da22-426a-4bac-b153-77360bef5337', + userId: { tdid: 'eff98622-b5fd-44fa-9a49-6e846922d532' } + }]; + + const bidderRequest = { + refererInfo: { + referer: 'http://example.com' + } + }; + + const topWin = { + innerWidth: 1366, + innerHeight: 768, + rtkcategories: ['cat1', 'cat2', 'cat3'] + }; + + let sandbox; + beforeEach(function () { + sandbox = sinon.createSandbox(); + }); + + afterEach(function () { + sandbox.restore(); + }); + + it('should have window.top dimensions', function () { + sandbox.stub(utils, 'getWindowTop').returns(topWin); + + const requests = spec.buildRequests(bidRequests, bidderRequest); + requests.forEach(function (requestItem) { + expect(requestItem.data.w).to.equal(topWin.innerWidth); + expect(requestItem.data.h).to.equal(topWin.innerHeight); + }); + }); + + it('should have window dimensions, as backup', function () { + sandbox.stub(utils, 'getWindowTop').returns(undefined); + + const requests = spec.buildRequests(bidRequests, bidderRequest); + requests.forEach(function (requestItem) { + expect(requestItem.data.w).to.equal(window.innerWidth); + expect(requestItem.data.h).to.equal(window.innerHeight); + }); + }); + + it('should have window.top & bid categories', function () { + sandbox.stub(utils, 'getWindowTop').returns(topWin); + + const requests = spec.buildRequests(bidRequests, bidderRequest); + requests.forEach(function (requestItem) { + utils._each(topWin.categories, function (cat) { + expect(requestItem.data.categories).to.contain(cat); + }); + utils._each(bidCategories, function (cat) { + expect(requestItem.data.categories).to.contain(cat); + }); + }); + }); + }); }); From a9dc89691993bbd91be3062d7ce47fca2d8fb9e7 Mon Sep 17 00:00:00 2001 From: nwlosinski Date: Thu, 13 Jun 2019 19:12:14 +0200 Subject: [PATCH 118/146] cache buster for user sync (#3838) --- modules/justpremiumBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/justpremiumBidAdapter.js b/modules/justpremiumBidAdapter.js index 6fbf5454b91..f8419f06faf 100644 --- a/modules/justpremiumBidAdapter.js +++ b/modules/justpremiumBidAdapter.js @@ -97,9 +97,9 @@ export const spec = { }, getUserSyncs: function getUserSyncs(syncOptions, responses, gdprConsent) { - let url = '//pre.ads.justpremium.com/v/1.0/t/sync' + let url = '//pre.ads.justpremium.com/v/1.0/t/sync' + '?_c=' + 'a' + Math.random().toString(36).substring(7) + Date.now(); if (gdprConsent && (typeof gdprConsent.gdprApplies === 'boolean')) { - url = url + '?consentString=' + encodeURIComponent(gdprConsent.consentString) + url = url + '&consentString=' + encodeURIComponent(gdprConsent.consentString) } if (syncOptions.iframeEnabled) { pixels.push({ From efe74f8a0a90d3e929d68bcc87169f6583cba676 Mon Sep 17 00:00:00 2001 From: HolzAndrew Date: Thu, 13 Jun 2019 13:14:41 -0400 Subject: [PATCH 119/146] Add bidFloor to Yieldmo Adapter (#3886) * change userid to pubcid * removes consolelogs * removes .txt file * fixes test * make the if statement useful * fix indentation * move return back to the correct place * fix indent --- modules/yieldmoBidAdapter.js | 28 +++++++++++++-------- modules/yieldmoBidAdapter.md | 3 ++- test/spec/modules/yieldmoBidAdapter_spec.js | 21 ++++++++++------ 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index b2d13e88c80..299baf49042 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -43,12 +43,13 @@ export const spec = { bidRequests.forEach((request) => { serverRequest.p.push(addPlacement(request)); - const userId = getUserId(request) - if (userId) { - const pubcid = userId.pubcid; + const pubcid = getPubcId(request) + if (pubcid) { serverRequest.pubcid = pubcid; } else if (request.crumbs) { - serverRequest.pubcid = request.crumbs.pubcid; + if (request.crumbs.pubcid) { + serverRequest.pubcid = request.crumbs.pubcid; + } } }); serverRequest.p = '[' + serverRequest.p.toString() + ']'; @@ -103,8 +104,13 @@ function addPlacement(request) { callback_id: request.bidId, sizes: request.sizes } - if (request.params && request.params.placementId) { - placementInfo.ym_placement_id = request.params.placementId + if (request.params) { + if (request.params.placementId) { + placementInfo.ym_placement_id = request.params.placementId; + } + if (request.params.bidFloor) { + placementInfo.bidFloor = request.params.bidFloor; + } } return JSON.stringify(placementInfo); } @@ -311,10 +317,10 @@ function isMraid() { return !!(window.mraid); } -function getUserId(request) { - let userId; - if (request && request.userId && typeof request.userId === 'object') { - userId = request.userId; +function getPubcId(request) { + let pubcid; + if (request && request.userId && request.userId.pubcid && typeof request.userId === 'object') { + pubcid = request.userId.pubcid; } - return userId; + return pubcid; } diff --git a/modules/yieldmoBidAdapter.md b/modules/yieldmoBidAdapter.md index 8c60202d7ea..7221f2ebdd0 100644 --- a/modules/yieldmoBidAdapter.md +++ b/modules/yieldmoBidAdapter.md @@ -23,7 +23,8 @@ var adUnits = [ bids: [{ bidder: 'yieldmo', params: { - placementId: '1779781193098233305' // string with at most 19 characters (may include numbers only) + placementId: '1779781193098233305', // string with at most 19 characters (may include numbers only) + bidFloor: .28 // optional param } }] } diff --git a/test/spec/modules/yieldmoBidAdapter_spec.js b/test/spec/modules/yieldmoBidAdapter_spec.js index 12dd87e1517..2a94dc7e5c9 100644 --- a/test/spec/modules/yieldmoBidAdapter_spec.js +++ b/test/spec/modules/yieldmoBidAdapter_spec.js @@ -9,7 +9,9 @@ describe('YieldmoAdapter', function () { let bid = { bidder: 'yieldmo', - params: {}, + params: { + bidFloor: 0.1 + }, adUnitCode: 'adunit-code', sizes: [[300, 250], [300, 600]], bidId: '30b31c1838de1e', @@ -59,11 +61,13 @@ describe('YieldmoAdapter', function () { it('should place bid information into the p parameter of data', function () { let placementInfo = spec.buildRequests(bidArray).data.p; - expect(placementInfo).to.equal('[{"placement_id":"adunit-code","callback_id":"30b31c1838de1e","sizes":[[300,250],[300,600]]}]'); + expect(placementInfo).to.equal('[{"placement_id":"adunit-code","callback_id":"30b31c1838de1e","sizes":[[300,250],[300,600]],"bidFloor":0.1}]'); bidArray.push({ bidder: 'yieldmo', - params: {}, + params: { + bidFloor: 0.2 + }, adUnitCode: 'adunit-code-1', sizes: [[300, 250], [300, 600]], bidId: '123456789', @@ -77,19 +81,19 @@ describe('YieldmoAdapter', function () { // multiple placements placementInfo = spec.buildRequests(bidArray).data.p; - expect(placementInfo).to.equal('[{"placement_id":"adunit-code","callback_id":"30b31c1838de1e","sizes":[[300,250],[300,600]]},{"placement_id":"adunit-code-1","callback_id":"123456789","sizes":[[300,250],[300,600]]}]'); + expect(placementInfo).to.equal('[{"placement_id":"adunit-code","callback_id":"30b31c1838de1e","sizes":[[300,250],[300,600]],"bidFloor":0.1},{"placement_id":"adunit-code-1","callback_id":"123456789","sizes":[[300,250],[300,600]],"bidFloor":0.2}]'); }); it('should add placement id if given', function () { bidArray[0].params.placementId = 'ym_1293871298'; let placementInfo = spec.buildRequests(bidArray).data.p; - expect(placementInfo).to.include('"ym_placement_id":"ym_1293871298"}'); - expect(placementInfo).not.to.include('"ym_placement_id":"ym_0987654321"}'); + expect(placementInfo).to.include('"ym_placement_id":"ym_1293871298"'); + expect(placementInfo).not.to.include('"ym_placement_id":"ym_0987654321"'); bidArray[1].params.placementId = 'ym_0987654321'; placementInfo = spec.buildRequests(bidArray).data.p; - expect(placementInfo).to.include('"ym_placement_id":"ym_1293871298"}'); - expect(placementInfo).to.include('"ym_placement_id":"ym_0987654321"}'); + expect(placementInfo).to.include('"ym_placement_id":"ym_1293871298"'); + expect(placementInfo).to.include('"ym_placement_id":"ym_0987654321"'); }); it('should add additional information to data parameter of request', function () { @@ -104,6 +108,7 @@ describe('YieldmoAdapter', function () { expect(data.hasOwnProperty('title')).to.be.true; expect(data.hasOwnProperty('h')).to.be.true; expect(data.hasOwnProperty('w')).to.be.true; + expect(data.hasOwnProperty('pubcid')).to.be.true; }) it('should add pubcid as parameter of request', function () { From 40f032cc3aa4944126417c3bfbb459a28eefbaa1 Mon Sep 17 00:00:00 2001 From: John Salis Date: Thu, 13 Jun 2019 13:33:32 -0400 Subject: [PATCH 120/146] Add beachfront bidder params to set outstream player settings (#3868) * Add beachfront bidder params to set outstream player settings * Add example for outstream player params * Run ci again --- modules/beachfrontBidAdapter.js | 32 +++++----- modules/beachfrontBidAdapter.md | 32 ++++++++++ .../spec/modules/beachfrontBidAdapter_spec.js | 58 +++++++++++++++++++ 3 files changed, 108 insertions(+), 14 deletions(-) diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index 552413f4878..efc7dfeda8d 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -7,7 +7,7 @@ import { VIDEO, BANNER } from '../src/mediaTypes'; import find from 'core-js/library/fn/array/find'; import includes from 'core-js/library/fn/array/includes'; -const ADAPTER_VERSION = '1.4'; +const ADAPTER_VERSION = '1.5'; const ADAPTER_NAME = 'BFIO_PREBID'; const OUTSTREAM = 'outstream'; @@ -143,21 +143,20 @@ function createRenderer(bidRequest) { loaded: false }); - renderer.setRender(outstreamRender); - - return renderer; -} - -function outstreamRender(bid) { - bid.renderer.push(() => { - window.Beachfront.Player(bid.adUnitCode, { - ad_tag_url: bid.vastUrl, - width: bid.width, - height: bid.height, - expand_in_view: false, - collapse_on_complete: true + renderer.setRender(bid => { + bid.renderer.push(() => { + window.Beachfront.Player(bid.adUnitCode, { + adTagUrl: bid.vastUrl, + width: bid.width, + height: bid.height, + expandInView: getPlayerBidParam(bidRequest, 'expandInView', false), + collapseOnComplete: getPlayerBidParam(bidRequest, 'collapseOnComplete', true), + progressColor: getPlayerBidParam(bidRequest, 'progressColor') + }); }); }); + + return renderer; } function getFirstSize(sizes) { @@ -231,6 +230,11 @@ function getBannerBidParam(bid, key) { return utils.deepAccess(bid, 'params.banner.' + key) || utils.deepAccess(bid, 'params.' + key); } +function getPlayerBidParam(bid, key, defaultValue) { + let param = utils.deepAccess(bid, 'params.player.' + key); + return param === undefined ? defaultValue : param; +} + function isVideoBidValid(bid) { return isVideoBid(bid) && getVideoBidParam(bid, 'appId') && getVideoBidParam(bid, 'bidfloor'); } diff --git a/modules/beachfrontBidAdapter.md b/modules/beachfrontBidAdapter.md index fb14db59710..3f827fe9241 100644 --- a/modules/beachfrontBidAdapter.md +++ b/modules/beachfrontBidAdapter.md @@ -86,3 +86,35 @@ Module that connects to Beachfront's demand sources } ]; ``` + +# Outstream Player Params Example +```javascript + var adUnits = [ + { + code: 'test-video-outstream', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ 640, 360 ] + } + }, + bids: [ + { + bidder: 'beachfront', + params: { + video: { + bidfloor: 0.01, + appId: '11bc5dd5-7421-4dd8-c926-40fa653bec76', + mimes: [ 'video/mp4', 'application/javascript' ] + }, + player: { + progressColor: '#50A8FA', + expandInView: false, + collapseOnComplete: true + } + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index 652dae4a74a..f5890cb6475 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -1,4 +1,5 @@ import { expect } from 'chai'; +import sinon from 'sinon'; import { spec, VIDEO_ENDPOINT, BANNER_ENDPOINT, OUTSTREAM_SRC, DEFAULT_MIMES } from 'modules/beachfrontBidAdapter'; import { parse as parseUrl } from 'src/url'; @@ -531,6 +532,63 @@ describe('BeachfrontAdapter', function () { url: OUTSTREAM_SRC }); }); + + it('should initialize a player for outstream bids', () => { + const width = 640; + const height = 480; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { + video: { + context: 'outstream', + playerSize: [ width, height ] + } + }; + const serverResponse = { + bidPrice: 5.00, + url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', + cmpId: '123abc' + }; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + window.Beachfront = { Player: sinon.spy() }; + bidResponse.adUnitCode = bidRequest.adUnitCode; + bidResponse.renderer.render(bidResponse); + sinon.assert.calledWith(window.Beachfront.Player, bidResponse.adUnitCode, sinon.match({ + adTagUrl: bidResponse.vastUrl, + width: bidResponse.width, + height: bidResponse.height, + expandInView: false, + collapseOnComplete: true + })); + delete window.Beachfront; + }); + + it('should configure outstream player settings from the bidder params', () => { + const width = 640; + const height = 480; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { + video: { + context: 'outstream', + playerSize: [ width, height ] + } + }; + bidRequest.params.player = { + expandInView: true, + collapseOnComplete: false, + progressColor: 'green' + }; + const serverResponse = { + bidPrice: 5.00, + url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', + cmpId: '123abc' + }; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + window.Beachfront = { Player: sinon.spy() }; + bidResponse.adUnitCode = bidRequest.adUnitCode; + bidResponse.renderer.render(bidResponse); + sinon.assert.calledWith(window.Beachfront.Player, bidResponse.adUnitCode, sinon.match(bidRequest.params.player)); + delete window.Beachfront; + }); }); describe('for banner bids', function () { From b617aa3e4153c0bd1c9d8db80987cd3972501560 Mon Sep 17 00:00:00 2001 From: Chris Connors Date: Thu, 13 Jun 2019 14:00:16 -0400 Subject: [PATCH 121/146] Adding Scaleable Analytics Adapter (#3846) * Adding Scaleable Analytics Adapter * Removed commented out code --- modules/scaleableAnalyticsAdapter.js | 153 ++++++++++++++++++ modules/scaleableAnalyticsAdapter.md | 20 +++ .../modules/scaleableAnalyticsAdapter_spec.js | 136 ++++++++++++++++ 3 files changed, 309 insertions(+) create mode 100644 modules/scaleableAnalyticsAdapter.js create mode 100644 modules/scaleableAnalyticsAdapter.md create mode 100644 test/spec/modules/scaleableAnalyticsAdapter_spec.js diff --git a/modules/scaleableAnalyticsAdapter.js b/modules/scaleableAnalyticsAdapter.js new file mode 100644 index 00000000000..c875dab7e18 --- /dev/null +++ b/modules/scaleableAnalyticsAdapter.js @@ -0,0 +1,153 @@ +/* COPYRIGHT SCALEABLE LLC 2019 */ + +import { ajax } from '../src/ajax'; +import CONSTANTS from '../src/constants.json'; +import adapter from '../src/AnalyticsAdapter'; +import adapterManager from '../src/adapterManager'; +import * as utils from '../src/utils'; + +const BID_TIMEOUT = CONSTANTS.EVENTS.BID_TIMEOUT; +const AUCTION_INIT = CONSTANTS.EVENTS.AUCTION_INIT; +const BID_RESPONSE = CONSTANTS.EVENTS.BID_RESPONSE; +const BID_WON = CONSTANTS.EVENTS.BID_WON; +const AUCTION_END = CONSTANTS.EVENTS.AUCTION_END; + +const URL = 'https://auction.scaleable.ai/'; +const ANALYTICS_TYPE = 'endpoint'; + +let auctionData = {}; + +let scaleableAnalytics = Object.assign({}, + adapter({ + URL, + ANALYTICS_TYPE + }), + { + // Override AnalyticsAdapter functions by supplying custom methods + track({ eventType, args }) { + switch (eventType) { + case AUCTION_INIT: + onAuctionInit(args); + break; + case AUCTION_END: + onAuctionEnd(args); + break; + case BID_WON: + onBidWon(args); + break; + case BID_RESPONSE: + onBidResponse(args); + break; + case BID_TIMEOUT: + onBidTimeout(args); + break; + default: + break; + } + } + } +); + +scaleableAnalytics.config = {}; +scaleableAnalytics.originEnableAnalytics = scaleableAnalytics.enableAnalytics; +scaleableAnalytics.enableAnalytics = config => { + scaleableAnalytics.config = config; + + scaleableAnalytics.originEnableAnalytics(config); + + scaleableAnalytics.enableAnalytics = function _enable() { + return utils.logMessage(`Analytics adapter for "${global}" already enabled, unnecessary call to \`enableAnalytics\`.`); + }; +} + +scaleableAnalytics.getAuctionData = () => { + return auctionData; +}; + +const sendDataToServer = data => ajax(URL, () => {}, JSON.stringify(data)); + +// Track auction initiated +const onAuctionInit = args => { + const config = scaleableAnalytics.config || {options: {}}; + + for (let idx = args.adUnitCodes.length; idx--;) { + const data = { + event: 'request', + site: config.options.site, + adunit: args.adUnitCodes[idx] + }; + + sendDataToServer(data); + } +} + +// Handle all events besides requests and wins +const onAuctionEnd = args => { + for (let adunit in auctionData) { + sendDataToServer(auctionData[adunit]); + } +} + +// Bid Win Events occur after auction end +const onBidWon = args => { + const config = scaleableAnalytics.config || {options: {}}; + + const data = { + event: 'win', + site: config.options.site, + adunit: args.adUnitCode, + code: args.bidderCode, + cpm: args.cpm, + ttr: args.timeToRespond + }; + + sendDataToServer(data); +} + +const onBidResponse = args => { + const config = scaleableAnalytics.config || {options: {}}; + + if (!auctionData[args.adUnitCode]) { + auctionData[args.adUnitCode] = { + event: 'bids', + bids: [], + adunit: args.adUnitCode, + site: config.options.site + }; + } + + const currBidData = { + code: args.bidderCode, + cpm: args.cpm, + ttr: args.timeToRespond + }; + + auctionData[args.adUnitCode].bids.push(currBidData); +} + +const onBidTimeout = args => { + const config = scaleableAnalytics.config || {options: {}}; + + for (let i = args.length; i--;) { + let currObj = args[i]; + + if (!auctionData[currObj.adUnitCode]) { + auctionData[currObj.adUnitCode] = { + event: 'bids', + bids: [], + timeouts: [], + adunit: currObj.adUnitCode, + site: config.options.site + }; + } + + auctionData[currObj.adUnitCode].timeouts.push(currObj.bidder); + } +} + +adapterManager.registerAnalyticsAdapter({ + adapter: scaleableAnalytics, + code: 'scaleable' +}) + +export default scaleableAnalytics; diff --git a/modules/scaleableAnalyticsAdapter.md b/modules/scaleableAnalyticsAdapter.md new file mode 100644 index 00000000000..0f6fbada55a --- /dev/null +++ b/modules/scaleableAnalyticsAdapter.md @@ -0,0 +1,20 @@ +# Overview + +Module Name: Scaleable Analytics Adapter +Module Type: Analytics Adapter +Maintainer: chris@scaleable.ai + +# Description + +Analytics adapter for scaleable.ai. Contact team@scaleable.ai for more information or to sign up for analytics. + +# Implementation Code + +``` +pbjs.enableAnalytics({ + provider: 'scaleable', + options: { + site: '' // Contact Scaleable to receive your unique site id + } +}); +``` diff --git a/test/spec/modules/scaleableAnalyticsAdapter_spec.js b/test/spec/modules/scaleableAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..300f72751a4 --- /dev/null +++ b/test/spec/modules/scaleableAnalyticsAdapter_spec.js @@ -0,0 +1,136 @@ +import scaleableAnalytics from 'modules/scaleableAnalyticsAdapter'; +import { expect } from 'chai'; +import events from 'src/events'; +import CONSTANTS from 'src/constants.json'; +import adapterManager from 'src/adapterManager'; + +const BID_TIMEOUT = CONSTANTS.EVENTS.BID_TIMEOUT; +const AUCTION_INIT = CONSTANTS.EVENTS.AUCTION_INIT; +const BID_RESPONSE = CONSTANTS.EVENTS.BID_RESPONSE; +const BID_WON = CONSTANTS.EVENTS.BID_WON; +const AUCTION_END = CONSTANTS.EVENTS.AUCTION_END; + +describe('Scaleable Analytics Adapter', function() { + const MOCK_DATA = { + adUnitCode: '12345', + site: '5c4fab7a829e955d6c265e72', + bidResponse: { + adUnitCode: '12345', + bidderCode: 'test-code', + cpm: 3.14, + timeToRespond: 285 + }, + bidTimeout: [ + { + adUnitCode: '67890', + bidder: 'test-code' + } + ] + }; + + MOCK_DATA.expectedBidResponse = { + event: 'bids', + bids: [{ + code: MOCK_DATA.bidResponse.bidderCode, + cpm: MOCK_DATA.bidResponse.cpm, + ttr: MOCK_DATA.bidResponse.timeToRespond + }], + adunit: MOCK_DATA.adUnitCode, + site: MOCK_DATA.site + }; + + MOCK_DATA.expectedBidTimeout = { + event: 'bids', + bids: [], + timeouts: [MOCK_DATA.bidTimeout[0].bidder], + adunit: MOCK_DATA.bidTimeout[0].adUnitCode, + site: MOCK_DATA.site + }; + + let xhr; + let requests; + + before(function() { + xhr = sinon.useFakeXMLHttpRequest(); + xhr.onCreate = request => requests.push(request); + }); + + after(function() { + xhr.restore(); + }); + + describe('Event Handling', function() { + beforeEach(function() { + requests = []; + sinon.stub(events, 'getEvents').returns([]); + + scaleableAnalytics.enableAnalytics({ + provider: 'scaleable', + options: { + site: MOCK_DATA.site + } + }); + }); + + afterEach(function() { + events.getEvents.restore(); + scaleableAnalytics.disableAnalytics(); + }); + + it('should handle the auction init event', function(done) { + events.emit(AUCTION_INIT, { + adUnitCodes: [MOCK_DATA.adUnitCode] + }); + + const result = JSON.parse(requests[0].requestBody); + expect(result).to.deep.equal({ + event: 'request', + site: MOCK_DATA.site, + adunit: MOCK_DATA.adUnitCode + }); + + done(); + }); + + it('should handle the bid response event', function() { + events.emit(BID_RESPONSE, MOCK_DATA.bidResponse); + + const actual = scaleableAnalytics.getAuctionData(); + + expect(actual[MOCK_DATA.adUnitCode]).to.deep.equal(MOCK_DATA.expectedBidResponse); + }); + + it('should handle the bid timeout event', function() { + events.emit(BID_TIMEOUT, MOCK_DATA.bidTimeout); + + const actual = scaleableAnalytics.getAuctionData(); + + expect(actual[MOCK_DATA.bidTimeout[0].adUnitCode]).to.deep.equal(MOCK_DATA.expectedBidTimeout); + }); + + it('should handle the bid won event', function(done) { + events.emit(BID_WON, MOCK_DATA.bidResponse); + + const result = JSON.parse(requests[0].requestBody); + expect(result).to.deep.equal({ + adunit: MOCK_DATA.adUnitCode, + code: MOCK_DATA.bidResponse.bidderCode, + cpm: MOCK_DATA.bidResponse.cpm, + ttr: MOCK_DATA.bidResponse.timeToRespond, + event: 'win', + site: MOCK_DATA.site + }); + + done(); + }); + + it('should handle the auction end event', function(done) { + events.emit(AUCTION_END, {}); + + const result = JSON.parse(requests[0].requestBody); + expect(result).to.deep.equal(MOCK_DATA.expectedBidResponse); + + done(); + }); + }); +}); From 1dc47c8c5fd0c8854d2070178ec463ecb69f3e4e Mon Sep 17 00:00:00 2001 From: Luis Date: Thu, 13 Jun 2019 14:04:44 -0400 Subject: [PATCH 122/146] Fix filepath reference (#3905) --- modules/optimeraBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/optimeraBidAdapter.js b/modules/optimeraBidAdapter.js index ab4c75c1c1f..7025045c7de 100644 --- a/modules/optimeraBidAdapter.js +++ b/modules/optimeraBidAdapter.js @@ -1,4 +1,4 @@ -import { registerBidder } from 'src/adapters/bidderFactory'; +import { registerBidder } from '../src/adapters/bidderFactory'; const BIDDER_CODE = 'optimera'; const SCORES_BASE_URL = 'https://dyv1bugovvq1g.cloudfront.net/'; From 81e87186164f0a427e2aaefd189b23d65434340e Mon Sep 17 00:00:00 2001 From: Bret Gorsline Date: Thu, 13 Jun 2019 15:14:21 -0400 Subject: [PATCH 123/146] Prebid 2.19.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 468b16c0ebb..5125a45ef16 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.19.0-pre", + "version": "2.19.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 20d8c8b2cbfaf080f8eb39e55ae27b8eabc3cdb4 Mon Sep 17 00:00:00 2001 From: Bret Gorsline Date: Thu, 13 Jun 2019 15:38:03 -0400 Subject: [PATCH 124/146] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5125a45ef16..4a3d8b2c959 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.19.0", + "version": "2.20.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 842cc19f712c644db84aae10d7d3567514d481e8 Mon Sep 17 00:00:00 2001 From: Chris Cole Date: Mon, 17 Jun 2019 10:19:09 -0700 Subject: [PATCH 125/146] digiTrustIdSystem.js add the synchronous behavior to facade call of DigiTrust.getId. Fix spelling error of facade in code. (#3913) --- modules/digiTrustIdSystem.js | 56 ++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/modules/digiTrustIdSystem.js b/modules/digiTrustIdSystem.js index b587913e1ed..07792cc49d3 100644 --- a/modules/digiTrustIdSystem.js +++ b/modules/digiTrustIdSystem.js @@ -85,12 +85,12 @@ function writeDigiId(id) { } /** - * Set up a DigiTrust fascade object to mimic the API + * Set up a DigiTrust facade object to mimic the API * */ -function initDigitrustFascade(config) { +function initDigitrustFacade(config) { var _savedId = null; // closure variable for storing Id to avoid additional requests - var fascade = { + var facade = { isClient: true, isMock: true, _internals: { @@ -98,8 +98,10 @@ function initDigitrustFascade(config) { initCallback: null }, getUser: function (obj, callback) { - var cb = callback || noop; - var inter = fascade._internals; + var isAsync = !!isFunc(callback); + var cb = isAsync ? callback : noop; + var errResp = { success: false }; + var inter = facade._internals; inter.callCount++; // wrap the initializer callback, if present @@ -113,10 +115,23 @@ function initDigitrustFascade(config) { } } + if (!isMemberIdValid) { + if (!isAsync) { + return errResp + } else { + cb(errResp); + return; + } + } + if (_savedId != null) { checkCallInitializeCb(_savedId); - cb(_savedId); - return; + if (isAsync) { + cb(_savedId); + return; + } else { + return _savedId; + } } var opts = { @@ -140,14 +155,31 @@ function initDigitrustFascade(config) { } callApi(opts); + + if (!isAsync) { + return errResp; // even if it will be successful later, without a callback we report a "failure in this moment" + } } } if (window && window.DigiTrust == null) { - window.DigiTrust = fascade; + window.DigiTrust = facade; } } +/** + * Tests to see if a member ID is valid within facade + * @param {any} memberId + */ +var isMemberIdValid = function (memberId) { + if (memberId && memberId.length > 0) { + return true; + } else { + utils.logError('[DigiTrust Prebid Client Error] Missing member ID, add the member ID to the function call options'); + return false; + } +}; + /** * Encapsulation of needed info for the callback return. * @@ -237,9 +269,9 @@ function getDigiTrustId(configParams) { getDigiTrustId(configParams); }, 100 * (1 + resultHandler.retries++)); return resultHandler.userIdCallback; - } else if (!isInitialized()) { // Second see if we should build a fascade object + } else if (!isInitialized()) { // Second see if we should build a facade object if (resultHandler.retries >= MAX_RETRIES) { - initDigitrustFascade(configParams); // initialize a fascade object that relies on the AJAX call + initDigitrustFacade(configParams); // initialize a facade object that relies on the AJAX call resultHandler.executeIdRequest(configParams); } else { // use expanding envelope @@ -264,7 +296,7 @@ function initializeDigiTrust(config) { dt.initialize(config.init, config.callback); } else if (dt == null) { // Assume we are already on a delay and DigiTrust is not on page - initDigitrustFascade(config); + initDigitrustFacade(config); } } @@ -278,7 +310,7 @@ function surfaceTestHook() { digitrustIdModule['_testHook'] = testHook; } -testHook.initDigitrustFascade = initDigitrustFascade; +testHook.initDigitrustFacade = initDigitrustFacade; /** @type {Submodule} */ export const digiTrustIdSubmodule = { From 1c1035aa1995f9d098c55b0aa1c22df7560eecfa Mon Sep 17 00:00:00 2001 From: ujuettner Date: Tue, 18 Jun 2019 16:42:36 +0200 Subject: [PATCH 126/146] Feature/remove on set targeting (#3919) * initial orbidder version in personal github repo * use adUnits from orbidder_example.html * replace obsolete functions * forgot to commit the test * check if bidderRequest object is available * try to fix weird safari/ie issue * ebayK: add more params * update orbidderBidAdapter.md * use spec. instead of this. for consistency reasons * add bidfloor parameter to params object * fix gdpr object handling * default to consentRequired: false when not explicitly given * wip - use onSetTargeting callback * add tests for onSetTargeting callback * fix params and respective tests * Remove onSetTargeting --- modules/orbidderBidAdapter.js | 4 ---- test/spec/modules/orbidderBidAdapter_spec.js | 6 ------ 2 files changed, 10 deletions(-) diff --git a/modules/orbidderBidAdapter.js b/modules/orbidderBidAdapter.js index e085a14c6b8..fc6eac6e479 100644 --- a/modules/orbidderBidAdapter.js +++ b/modules/orbidderBidAdapter.js @@ -74,10 +74,6 @@ export const spec = { this.onHandler(bid, '/win'); }, - onSetTargeting (bid) { - this.onHandler(bid, '/targeting'); - }, - onHandler (bid, route) { const getRefererInfo = detectReferer(window); diff --git a/test/spec/modules/orbidderBidAdapter_spec.js b/test/spec/modules/orbidderBidAdapter_spec.js index 55f5e2cae4c..4a972c42d30 100644 --- a/test/spec/modules/orbidderBidAdapter_spec.js +++ b/test/spec/modules/orbidderBidAdapter_spec.js @@ -178,12 +178,6 @@ describe('orbidderBidAdapter', () => { expect(ajaxStub.firstCall.args[0].indexOf('https://')).to.equal(0); expect(ajaxStub.firstCall.args[0]).to.equal(`${spec.orbidderHost}/win`); expect(ajaxStub.firstCall.args[1]).to.equal(JSON.stringify(bidObjClone)); - - spec.onSetTargeting(bidObj); - expect(ajaxStub.calledTwice).to.equal(true); - expect(ajaxStub.secondCall.args[0].indexOf('https://')).to.equal(0); - expect(ajaxStub.secondCall.args[0]).to.equal(`${spec.orbidderHost}/targeting`); - expect(ajaxStub.secondCall.args[1]).to.equal(JSON.stringify(bidObjClone)); }); }); From 4b84beb1a326adb2b918e81d281af2433f310831 Mon Sep 17 00:00:00 2001 From: couchcrew-thomas Date: Tue, 18 Jun 2019 16:59:36 +0200 Subject: [PATCH 127/146] FeedAd bidder adapter (#3891) * added file scaffold * added isBidRequestValid implementation * added local prototype of ad integration * added implementation for placement ID validation * fixed video context filter * applied lint to feedad bid adapter * added unit test for bid request validation * added buildRequest unit test * added unit tests for timeout and bid won callbacks * updated bid request to FeedAd API * added parsing of feedad api bid response * added transmisison of tracking events to FeedAd Api * code cleanup * updated feedad unit tests for buildRequest method * added unit tests for event tracking implementation * added unit test for interpretResponse method * added adapter documentation * added dedicated feedad example page * updated feedad adapter to use live system * updated FeedAd adapter placement ID regex * removed groups from FeedAd adapter placement ID regex * removed dedicated feedad example page * updated imports in FeedAd adapter file to use relative paths * updated FeedAd adapter unit test to use sinon.useFakeXMLHttpRequest() --- modules/feedadBidAdapter.js | 290 ++++++++++++++ modules/feedadBidAdapter.md | 44 +++ test/spec/modules/feedadBidAdapter_spec.js | 433 +++++++++++++++++++++ 3 files changed, 767 insertions(+) create mode 100644 modules/feedadBidAdapter.js create mode 100644 modules/feedadBidAdapter.md create mode 100644 test/spec/modules/feedadBidAdapter_spec.js diff --git a/modules/feedadBidAdapter.js b/modules/feedadBidAdapter.js new file mode 100644 index 00000000000..1e995ee8914 --- /dev/null +++ b/modules/feedadBidAdapter.js @@ -0,0 +1,290 @@ +import * as utils from '../src/utils'; +import {registerBidder} from '../src/adapters/bidderFactory'; +import {BANNER, VIDEO} from '../src/mediaTypes'; +import {ajax} from '../src/ajax'; + +/** + * Version of the FeedAd bid adapter + * @type {string} + */ +const VERSION = '1.0.0'; + +/** + * @typedef {object} FeedAdApiBidRequest + * @inner + * + * @property {number} ad_type + * @property {string} client_token + * @property {string} placement_id + * @property {string} sdk_version + * @property {boolean} app_hybrid + * + * @property {string} [app_bundle_id] + * @property {string} [app_name] + * @property {object} [custom_params] + * @property {number} [connectivity] + * @property {string} [device_adid] + * @property {string} [device_platform] + */ + +/** + * @typedef {object} FeedAdApiBidResponse + * @inner + * + * @property {string} ad - Ad HTML payload + * @property {number} cpm - number / float + * @property {string} creativeId - ID of creative for tracking + * @property {string} currency - 3-letter ISO 4217 currency-code + * @property {number} height - Height of creative returned in [].ad + * @property {boolean} netRevenue - Is the CPM net (true) or gross (false)? + * @property {string} requestId - bids[].bidId + * @property {number} ttl - Time to live for this ad + * @property {number} width - Width of creative returned in [].ad + */ + +/** + * @typedef {object} FeedAdApiTrackingParams + * @inner + * + * @property app_hybrid {boolean} + * @property client_token {string} + * @property klass {'prebid_bidWon'|'prebid_bidTimeout'} + * @property placement_id {string} + * @property prebid_auction_id {string} + * @property prebid_bid_id {string} + * @property prebid_transaction_id {string} + * @property referer {string} + * @property sdk_version {string} + * @property [app_bundle_id] {string} + * @property [app_name] {string} + * @property [device_adid] {string} + * @property [device_platform] {1|2|3} 1 - Android | 2 - iOS | 3 - Windows + */ + +/** + * Bidder network identity code + * @type {string} + */ +const BIDDER_CODE = 'feedad'; + +/** + * The media types supported by FeedAd + * @type {MediaType[]} + */ +const MEDIA_TYPES = [VIDEO, BANNER]; + +/** + * Tag for logging + * @type {string} + */ +const TAG = '[FeedAd]'; + +/** + * Pattern for valid placement IDs + * @type {RegExp} + */ +const PLACEMENT_ID_PATTERN = /^[a-z0-9][a-z0-9_-]+[a-z0-9]$/; + +const API_ENDPOINT = 'https://api.feedad.com'; +const API_PATH_BID_REQUEST = '/1/prebid/web/bids'; +const API_PATH_TRACK_REQUEST = '/1/prebid/web/events'; + +/** + * Stores temporary auction metadata + * @type {Object.} + */ +const BID_METADATA = {}; + +/** + * Checks if the bid is compatible with FeedAd. + * + * @param {BidRequest} bid - the bid to check + * @return {boolean} true if the bid is valid + */ +function isBidRequestValid(bid) { + const clientToken = utils.deepAccess(bid, 'params.clientToken'); + if (!clientToken || !isValidClientToken(clientToken)) { + utils.logWarn(TAG, "missing or invalid parameter 'clientToken'. found value:", clientToken); + return false; + } + + const placementId = utils.deepAccess(bid, 'params.placementId'); + if (!placementId || !isValidPlacementId(placementId)) { + utils.logWarn(TAG, "missing or invalid parameter 'placementId'. found value:", placementId); + return false; + } + + return true; +} + +/** + * Checks if a client token is valid + * @param {string} clientToken - the client token + * @return {boolean} true if the token is valid + */ +function isValidClientToken(clientToken) { + return typeof clientToken === 'string' && clientToken.length > 0; +} + +/** + * Checks if the given placement id is of a correct format. + * Valid IDs are words of lowercase letters from a to z and numbers from 0 to 9. + * The words can be separated by hyphens or underscores. + * Multiple separators must not follow each other. + * The whole placement ID must not be larger than 256 characters. + * + * @param placementId - the placement id to verify + * @returns if the placement ID is valid. + */ +function isValidPlacementId(placementId) { + return typeof placementId === 'string' && + placementId.length > 0 && + placementId.length <= 256 && + PLACEMENT_ID_PATTERN.test(placementId); +} + +/** + * Checks if the given media types contain unsupported settings + * @param {MediaTypes} mediaTypes - the media types to check + * @return {MediaTypes} the unsupported settings, empty when all types are supported + */ +function filterSupportedMediaTypes(mediaTypes) { + return { + banner: mediaTypes.banner, + video: mediaTypes.video && mediaTypes.video.context === 'outstream' ? mediaTypes.video : undefined, + native: undefined + }; +} + +/** + * Checks if the given media types are empty + * @param {MediaTypes} mediaTypes - the types to check + * @return {boolean} true if the types are empty + */ +function isMediaTypesEmpty(mediaTypes) { + return Object.keys(mediaTypes).every(type => mediaTypes[type] === undefined); +} + +/** + * Creates the bid request params the api expects from the prebid bid request + * @param {BidRequest} request - the validated prebid bid request + * @return {FeedAdApiBidRequest} + */ +function createApiBidRParams(request) { + return { + ad_type: 0, + client_token: request.params.clientToken, + placement_id: request.params.placementId, + sdk_version: `prebid_${VERSION}`, + app_hybrid: false, + }; +} + +/** + * Builds the bid request to the FeedAd Server + * @param {BidRequest[]} validBidRequests - all validated bid requests + * @param {object} bidderRequest - meta information + * @return {ServerRequest|ServerRequest[]} + */ +function buildRequests(validBidRequests, bidderRequest) { + if (!bidderRequest) { + return []; + } + let acceptableRequests = validBidRequests.filter(request => !isMediaTypesEmpty(filterSupportedMediaTypes(request.mediaTypes))); + if (acceptableRequests.length === 0) { + return []; + } + let data = Object.assign({}, bidderRequest, { + bids: acceptableRequests.map(req => { + req.params = createApiBidRParams(req); + return req; + }) + }); + data.bids.forEach(bid => BID_METADATA[bid.bidId] = { + referer: data.refererInfo.referer, + transactionId: bid.transactionId + }); + return { + method: 'POST', + url: `${API_ENDPOINT}${API_PATH_BID_REQUEST}`, + data, + options: { + contentType: 'application/json' + } + }; +} + +/** + * Adapts the FeedAd server response to Prebid format + * @param {ServerResponse} serverResponse - the FeedAd server response + * @param {BidRequest} request - the initial bid request + * @returns {Bid[]} the FeedAd bids + */ +function interpretResponse(serverResponse, request) { + /** + * @type FeedAdApiBidResponse[] + */ + return typeof serverResponse.body === 'string' ? JSON.parse(serverResponse.body) : serverResponse.body; +} + +/** + * Creates the parameters for the FeedAd tracking call + * @param {object} data - prebid data + * @param {'prebid_bidWon'|'prebid_bidTimeout'} klass - type of tracking call + * @return {FeedAdApiTrackingParams|null} + */ +function createTrackingParams(data, klass) { + const bidId = data.bidId || data.requestId; + if (!BID_METADATA.hasOwnProperty(bidId)) { + return null; + } + const {referer, transactionId} = BID_METADATA[bidId]; + delete BID_METADATA[bidId]; + return { + app_hybrid: false, + client_token: data.params[0].clientToken, + placement_id: data.params[0].placementId, + klass, + prebid_auction_id: data.auctionId, + prebid_bid_id: bidId, + prebid_transaction_id: transactionId, + referer, + sdk_version: VERSION + }; +} + +/** + * Creates a tracking handler for the given event type + * @param klass - the event type + * @return {Function} the tracking handler function + */ +function trackingHandlerFactory(klass) { + return (data) => { + if (!data) { + return; + } + let params = createTrackingParams(data, klass); + if (params) { + ajax(`${API_ENDPOINT}${API_PATH_TRACK_REQUEST}`, null, JSON.stringify(params), { + withCredentials: true, + method: 'POST', + contentType: 'application/json' + }); + } + } +} + +/** + * @type {BidderSpec} + */ +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: MEDIA_TYPES, + isBidRequestValid, + buildRequests, + interpretResponse, + onTimeout: trackingHandlerFactory('prebid_bidTimeout'), + onBidWon: trackingHandlerFactory('prebid_bidWon') +}; + +registerBidder(spec); diff --git a/modules/feedadBidAdapter.md b/modules/feedadBidAdapter.md new file mode 100644 index 00000000000..fd57025c29e --- /dev/null +++ b/modules/feedadBidAdapter.md @@ -0,0 +1,44 @@ +# Overview + +``` +Module Name: FeedAd Adapter +Module Type: Bidder Adapter +Maintainer: mail@feedad.com +``` + +# Description + +Prebid.JS adapter that connects to the FeedAd demand sources. + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div', + mediaTypes: { + banner: { // supports all banner sizes + sizes: [[300, 250]], + }, + video: { // supports only outstream video + context: 'outstream' + } + }, + bids: [ + { + bidder: "feedad", + params: { + clientToken: 'your-client-token' // see below for more info + placementId: 'your-placement-id' // see below for more info + } + } + ] + } + ]; +``` + +# Required Parameters + +| Parameter | Description | +| --------- | ----------- | +| `clientToken` | Your FeedAd web client token. You can view your client token inside the FeedAd admin panel. | +| `placementId` | You can choose placement IDs yourself. A placement ID should be named after the ad position inside your product. For example, if you want to display an ad inside a list of news articles, you could name it "ad-news-overview".
    A placement ID may consist of lowercase `a-z`, `0-9`, `-` and `_`. You do not have to manually create the placement IDs before using them. Just specify them within the code, and they will appear in the FeedAd admin panel after the first request.
    [Learn more](/concept/feed_ad/index.html) about Placement IDs and how they are grouped to play the same Creative. | diff --git a/test/spec/modules/feedadBidAdapter_spec.js b/test/spec/modules/feedadBidAdapter_spec.js new file mode 100644 index 00000000000..3432a40eca4 --- /dev/null +++ b/test/spec/modules/feedadBidAdapter_spec.js @@ -0,0 +1,433 @@ +import {expect} from 'chai'; +import {spec} from 'modules/feedadBidAdapter'; +import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes'; +import * as sinon from 'sinon'; + +const CODE = 'feedad'; + +describe('FeedAdAdapter', function () { + describe('Public API', function () { + it('should have the FeedAd bidder code', function () { + expect(spec.code).to.equal(CODE); + }); + it('should only support video and banner ads', function () { + expect(spec.supportedMediaTypes).to.be.a('array'); + expect(spec.supportedMediaTypes).to.include(BANNER); + expect(spec.supportedMediaTypes).to.include(VIDEO); + expect(spec.supportedMediaTypes).not.to.include(NATIVE); + }); + it('should export the BidderSpec functions', function () { + expect(spec.isBidRequestValid).to.be.a('function'); + expect(spec.buildRequests).to.be.a('function'); + expect(spec.interpretResponse).to.be.a('function'); + expect(spec.onTimeout).to.be.a('function'); + expect(spec.onBidWon).to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + it('should detect missing params', function () { + let result = spec.isBidRequestValid({ + bidder: 'feedad', + sizes: [] + }); + expect(result).to.equal(false); + }); + it('should detect missing client token', function () { + let result = spec.isBidRequestValid({ + bidder: 'feedad', + sizes: [], + params: {placementId: 'placement'} + }); + expect(result).to.equal(false); + }); + it('should detect zero length client token', function () { + let result = spec.isBidRequestValid({ + bidder: 'feedad', + sizes: [], + params: {clientToken: '', placementId: 'placement'} + }); + expect(result).to.equal(false); + }); + it('should detect missing placement id', function () { + let result = spec.isBidRequestValid({ + bidder: 'feedad', + sizes: [], + params: {clientToken: 'clientToken'} + }); + expect(result).to.equal(false); + }); + it('should detect zero length placement id', function () { + let result = spec.isBidRequestValid({ + bidder: 'feedad', + sizes: [], + params: {clientToken: 'clientToken', placementId: ''} + }); + expect(result).to.equal(false); + }); + it('should detect too long placement id', function () { + var placementId = ''; + for (var i = 0; i < 300; i++) { + placementId += 'a'; + } + let result = spec.isBidRequestValid({ + bidder: 'feedad', + sizes: [], + params: {clientToken: 'clientToken', placementId} + }); + expect(result).to.equal(false); + }); + it('should detect invalid placement id', function () { + [ + 'placement id with spaces', + 'some|id', + 'PLACEMENTID', + 'placeme:ntId' + ].forEach(id => { + let result = spec.isBidRequestValid({ + bidder: 'feedad', + sizes: [], + params: {clientToken: 'clientToken', placementId: id} + }); + expect(result).to.equal(false); + }); + }); + it('should accept valid parameters', function () { + let result = spec.isBidRequestValid({ + bidder: 'feedad', + sizes: [], + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }); + expect(result).to.equal(true); + }); + }); + + describe('buildRequests', function () { + const bidderRequest = { + refererInfo: { + referer: 'the referer' + }, + some: 'thing' + }; + + it('should accept empty lists', function () { + let result = spec.buildRequests([], bidderRequest); + expect(result).to.be.empty; + }); + it('should filter native media types', function () { + let bid = { + code: 'feedad', + mediaTypes: { + native: { + sizes: [[300, 250], [300, 600]], + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid], bidderRequest); + expect(result).to.be.empty; + }); + it('should filter video media types without outstream context', function () { + let bid = { + code: 'feedad', + mediaTypes: { + video: { + context: 'instream' + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid], bidderRequest); + expect(result).to.be.empty; + }); + it('should pass through outstream video media', function () { + let bid = { + code: 'feedad', + mediaTypes: { + video: { + context: 'outstream' + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid], bidderRequest); + expect(result.data.bids).to.be.lengthOf(1); + expect(result.data.bids[0]).to.deep.equal(bid); + }); + it('should pass through banner media', function () { + let bid = { + code: 'feedad', + mediaTypes: { + banner: { + sizes: [[320, 250]] + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid], bidderRequest); + expect(result.data.bids).to.be.lengthOf(1); + expect(result.data.bids[0]).to.deep.equal(bid); + }); + it('should detect empty media types', function () { + let bid = { + code: 'feedad', + mediaTypes: { + banner: undefined, + video: undefined, + native: undefined + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid], bidderRequest); + expect(result).to.be.empty; + }); + it('should use POST', function () { + let bid = { + code: 'feedad', + mediaTypes: { + banner: { + sizes: [[320, 250]] + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid], bidderRequest); + expect(result.method).to.equal('POST'); + }); + it('should use the correct URL', function () { + let bid = { + code: 'feedad', + mediaTypes: { + banner: { + sizes: [[320, 250]] + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid], bidderRequest); + expect(result.url).to.equal('https://api.feedad.com/1/prebid/web/bids'); + }); + it('should specify the content type explicitly', function () { + let bid = { + code: 'feedad', + mediaTypes: { + banner: { + sizes: [[320, 250]] + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid], bidderRequest); + expect(result.options).to.deep.equal({ + contentType: 'application/json' + }) + }); + it('should include the bidder request', function () { + let bid = { + code: 'feedad', + mediaTypes: { + banner: { + sizes: [[320, 250]] + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid, bid, bid], bidderRequest); + expect(result.data).to.deep.include(bidderRequest); + }); + it('should detect missing bidder request parameter', function () { + let bid = { + code: 'feedad', + mediaTypes: { + banner: { + sizes: [[320, 250]] + } + }, + params: {clientToken: 'clientToken', placementId: 'placement-id'} + }; + let result = spec.buildRequests([bid, bid, bid]); + expect(result).to.be.empty; + }); + }); + + describe('interpretResponse', function () { + const body = [{ + foo: 'bar', + sub: { + obj: 5 + } + }, { + bar: 'foo' + }]; + + it('should convert string bodies to JSON', function () { + let result = spec.interpretResponse({body: JSON.stringify(body)}); + expect(result).to.deep.equal(body); + }); + + it('should pass through body objects', function () { + let result = spec.interpretResponse({body}); + expect(result).to.deep.equal(body); + }); + }); + + describe('event tracking calls', function () { + const clientToken = 'clientToken'; + const placementId = 'placement id'; + const auctionId = 'the auction id'; + const bidId = 'the bid id'; + const transactionId = 'the transaction id'; + const referer = 'the referer'; + const bidderRequest = { + refererInfo: { + referer: referer + }, + some: 'thing' + }; + const bid = { + 'bidder': 'feedad', + 'params': { + 'clientToken': 'fupp', + 'placementId': 'prebid-test' + }, + 'crumbs': { + 'pubcid': '6254a85f-bded-489a-9736-83c45d45ef1d' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ] + ] + } + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': transactionId, + 'sizes': [ + [ + 300, + 250 + ] + ], + 'bidId': bidId, + 'bidderRequestId': '10739fe6fe2127', + 'auctionId': '5ac67dff-d971-4b56-84a3-345a87a1f786', + 'src': 'client', + 'bidRequestsCount': 1 + }; + const timeoutData = { + 'bidId': bidId, + 'bidder': 'feedad', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'auctionId': auctionId, + 'params': [ + { + 'clientToken': clientToken, + 'placementId': placementId + } + ], + 'timeout': 3000 + }; + const bidWonData = { + 'bidderCode': 'feedad', + 'width': 300, + 'height': 250, + 'statusMessage': 'Bid available', + 'adId': '3a4529aa05114d', + 'requestId': bidId, + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 0.5, + 'ad': 'ad content', + 'ttl': 60, + 'creativeId': 'feedad-21-0', + 'netRevenue': true, + 'currency': 'EUR', + 'auctionId': auctionId, + 'responseTimestamp': 1558365914596, + 'requestTimestamp': 1558365914506, + 'bidder': 'feedad', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'timeToRespond': 90, + 'pbLg': '0.50', + 'pbMg': '0.50', + 'pbHg': '0.50', + 'pbAg': '0.50', + 'pbDg': '0.50', + 'pbCg': '', + 'size': '300x250', + 'adserverTargeting': { + 'hb_bidder': 'feedad', + 'hb_adid': '3a4529aa05114d', + 'hb_pb': '0.50', + 'hb_size': '300x250', + 'hb_source': 'client', + 'hb_format': 'banner' + }, + 'status': 'rendered', + 'params': [ + { + 'clientToken': clientToken, + 'placementId': placementId + } + ] + }; + const cases = [ + ['onTimeout', timeoutData, 'prebid_bidTimeout'], + ['onBidWon', bidWonData, 'prebid_bidWon'], + ]; + + cases.forEach(([name, data, eventKlass]) => { + let subject = spec[name]; + describe(name + ' handler', function () { + let xhr; + let requests; + + beforeEach(function () { + xhr = sinon.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = xhr => requests.push(xhr); + }); + + afterEach(function () { + xhr.restore(); + }); + + it('should do nothing on empty data', function () { + subject(undefined); + subject(null); + expect(requests.length).to.equal(0); + }); + + it('should do nothing when bid metadata is not set', function () { + subject(data); + expect(requests.length).to.equal(0); + }); + + it('should send tracking params when correct metadata was set', function () { + spec.buildRequests([bid], bidderRequest); + let expectedData = { + app_hybrid: false, + client_token: clientToken, + placement_id: placementId, + klass: eventKlass, + prebid_auction_id: auctionId, + prebid_bid_id: bidId, + prebid_transaction_id: transactionId, + referer, + sdk_version: '1.0.0' + }; + subject(data); + expect(requests.length).to.equal(1); + let call = requests[0]; + expect(call.url).to.equal('https://api.feedad.com/1/prebid/web/events'); + expect(JSON.parse(call.requestBody)).to.deep.equal(expectedData); + expect(call.method).to.equal('POST'); + expect(call.requestHeaders).to.include({'Content-Type': 'application/json;charset=utf-8'}); + }) + }); + }); + }); +}); From 6e7eb3b81a2acf60e8c0171e10a520e5b2c11d7d Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Tue, 18 Jun 2019 12:04:44 -0400 Subject: [PATCH 128/146] Fix #3813 move auctionEnd events so it always executes when auction completes (#3841) * move auctionEnd events so it always executes * remove some commented code --- src/auction.js | 25 +++++++++++++------------ test/spec/auctionmanager_spec.js | 11 +++++++++-- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/auction.js b/src/auction.js index 1d758997035..a1e8c33adfb 100644 --- a/src/auction.js +++ b/src/auction.js @@ -140,7 +140,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a clearTimeout(_timer); } - if (_callback != null) { + if (_auctionEnd === undefined) { let timedOutBidders = []; if (timedOut) { utils.logMessage(`Auction ${_auctionId} timedOut`); @@ -150,17 +150,19 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a } } - try { - _auctionStatus = AUCTION_COMPLETED; - _auctionEnd = Date.now(); - - events.emit(CONSTANTS.EVENTS.AUCTION_END, getProperties()); + _auctionStatus = AUCTION_COMPLETED; + _auctionEnd = Date.now(); - const adUnitCodes = _adUnitCodes; - const bids = _bidsReceived - .filter(utils.bind.call(adUnitsFilter, this, adUnitCodes)) - .reduce(groupByPlacement, {}); - _callback.apply($$PREBID_GLOBAL$$, [bids, timedOut]); + events.emit(CONSTANTS.EVENTS.AUCTION_END, getProperties()); + try { + if (_callback != null) { + const adUnitCodes = _adUnitCodes; + const bids = _bidsReceived + .filter(utils.bind.call(adUnitsFilter, this, adUnitCodes)) + .reduce(groupByPlacement, {}); + _callback.apply($$PREBID_GLOBAL$$, [bids, timedOut]); + _callback = null; + } } catch (e) { utils.logError('Error executing bidsBackHandler', null, e); } finally { @@ -175,7 +177,6 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a syncUsers(userSyncConfig.syncDelay); } } - _callback = null; } } diff --git a/test/spec/auctionmanager_spec.js b/test/spec/auctionmanager_spec.js index 2d8ee562ce4..577b1bec333 100644 --- a/test/spec/auctionmanager_spec.js +++ b/test/spec/auctionmanager_spec.js @@ -1,4 +1,4 @@ -import { getKeyValueTargetingPairs, auctionCallbacks } from 'src/auction'; +import { getKeyValueTargetingPairs, auctionCallbacks, AUCTION_COMPLETED } from 'src/auction'; import CONSTANTS from 'src/constants.json'; import { adjustBids } from 'src/auction'; import * as auctionModule from 'src/auction'; @@ -783,7 +783,8 @@ describe('auctionmanager.js', function () { server.restore(); events.emit.restore(); }); - it('should emit BID_TIMEOUT for timed out bids', function (done) { + + it('should emit BID_TIMEOUT and AUCTION_END for timed out bids', function (done) { const spec1 = mockBidder(BIDDER_CODE, [bids[0]]); registerBidder(spec1); const spec2 = mockBidder(BIDDER_CODE1, [bids[1]]); @@ -797,6 +798,12 @@ describe('auctionmanager.js', function () { const timedOutBids = bidTimeoutCall.args[1]; assert.equal(timedOutBids.length, 1); assert.equal(timedOutBids[0].bidder, BIDDER_CODE1); + + const auctionEndCall = eventsEmitSpy.withArgs(CONSTANTS.EVENTS.AUCTION_END).getCalls()[0]; + const auctionProps = auctionEndCall.args[1]; + assert.equal(auctionProps.adUnits, adUnits); + assert.equal(auctionProps.timeout, 20); + assert.equal(auctionProps.auctionStatus, AUCTION_COMPLETED) done(); } auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: auctionCallback, cbTimeout: 20}); From bd5f2a0f18990e21e29309f4dcbad3591b0b666b Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Tue, 18 Jun 2019 12:14:39 -0400 Subject: [PATCH 129/146] fix import paths for various adapters (#3921) --- modules/advenueBidAdapter.js | 6 +++--- modules/bidphysicsBidAdapter.js | 2 +- modules/cedatoBidAdapter.js | 6 +++--- modules/digiTrustIdSystem.js | 2 +- modules/emx_digitalBidAdapter.js | 10 +++++----- modules/imonomyBidAdapter.js | 4 ++-- modules/loopmeBidAdapter.js | 6 +++--- modules/mgidBidAdapter.js | 4 ++-- modules/microadBidAdapter.js | 4 ++-- modules/open8BidAdapter.js | 6 +++--- modules/otmBidAdapter.js | 4 ++-- modules/prebidmanagerAnalyticsAdapter.js | 4 ++-- modules/reklamstoreBidAdapter.js | 6 +++--- modules/slimcutBidAdapter.js | 6 +++--- modules/smartrtbBidAdapter.js | 4 ++-- modules/sortableAnalyticsAdapter.js | 14 +++++++------- modules/timBidAdapter.js | 6 +++--- modules/yieldoneBidAdapter.js | 2 +- 18 files changed, 48 insertions(+), 48 deletions(-) diff --git a/modules/advenueBidAdapter.js b/modules/advenueBidAdapter.js index 3e7711cca0d..6dc5856eacb 100644 --- a/modules/advenueBidAdapter.js +++ b/modules/advenueBidAdapter.js @@ -1,6 +1,6 @@ -import { registerBidder } from 'src/adapters/bidderFactory'; -import { BANNER, NATIVE, VIDEO } from 'src/mediaTypes'; -import * as utils from 'src/utils'; +import { registerBidder } from '../src/adapters/bidderFactory'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes'; +import * as utils from '../src/utils'; const BIDDER_CODE = 'advenue'; const URL_MULTI = '//ssp.advenuemedia.co.uk/?c=o&m=multi'; diff --git a/modules/bidphysicsBidAdapter.js b/modules/bidphysicsBidAdapter.js index 260a473c631..9f1dc83d427 100644 --- a/modules/bidphysicsBidAdapter.js +++ b/modules/bidphysicsBidAdapter.js @@ -1,4 +1,4 @@ -import {registerBidder} from 'src/adapters/bidderFactory'; +import {registerBidder} from '../src/adapters/bidderFactory'; import * as utils from '../src/utils'; import {BANNER} from '../src/mediaTypes'; diff --git a/modules/cedatoBidAdapter.js b/modules/cedatoBidAdapter.js index 78bb7b45c7b..c367c57fc25 100644 --- a/modules/cedatoBidAdapter.js +++ b/modules/cedatoBidAdapter.js @@ -1,6 +1,6 @@ -import * as utils from 'src/utils'; -import { registerBidder } from 'src/adapters/bidderFactory'; -import { BANNER } from 'src/mediaTypes'; +import * as utils from '../src/utils'; +import { registerBidder } from '../src/adapters/bidderFactory'; +import { BANNER } from '../src/mediaTypes'; const BIDDER_CODE = 'cedato'; const BID_URL = '//h.cedatoplayer.com/hb'; diff --git a/modules/digiTrustIdSystem.js b/modules/digiTrustIdSystem.js index 07792cc49d3..454e6864846 100644 --- a/modules/digiTrustIdSystem.js +++ b/modules/digiTrustIdSystem.js @@ -11,7 +11,7 @@ // import { config } from 'src/config'; import * as utils from '../src/utils' -import { ajax } from 'src/ajax'; +import { ajax } from '../src/ajax'; import { attachIdSystem } from '../modules/userId'; // import { getGlobal } from 'src/prebidGlobal'; diff --git a/modules/emx_digitalBidAdapter.js b/modules/emx_digitalBidAdapter.js index 75ce47aae0a..69e02d5c860 100644 --- a/modules/emx_digitalBidAdapter.js +++ b/modules/emx_digitalBidAdapter.js @@ -1,8 +1,8 @@ -import * as utils from 'src/utils'; -import { registerBidder } from 'src/adapters/bidderFactory'; -import { BANNER, VIDEO } from 'src/mediaTypes'; -import { config } from 'src/config'; -import { Renderer } from 'src/Renderer'; +import * as utils from '../src/utils'; +import { registerBidder } from '../src/adapters/bidderFactory'; +import { BANNER, VIDEO } from '../src/mediaTypes'; +import { config } from '../src/config'; +import { Renderer } from '../src/Renderer'; import includes from 'core-js/library/fn/array/includes'; const BIDDER_CODE = 'emx_digital'; diff --git a/modules/imonomyBidAdapter.js b/modules/imonomyBidAdapter.js index fa3ad0cfea2..9a0d29a1374 100644 --- a/modules/imonomyBidAdapter.js +++ b/modules/imonomyBidAdapter.js @@ -1,5 +1,5 @@ -import * as utils from 'src/utils'; -import { registerBidder } from 'src/adapters/bidderFactory'; +import * as utils from '../src/utils'; +import { registerBidder } from '../src/adapters/bidderFactory'; const BIDDER_CODE = 'imonomy'; const ENDPOINT = '//b.imonomy.com/openrtb/hb/00000'; diff --git a/modules/loopmeBidAdapter.js b/modules/loopmeBidAdapter.js index fb2f891d3b0..fcfe1fd0687 100644 --- a/modules/loopmeBidAdapter.js +++ b/modules/loopmeBidAdapter.js @@ -1,6 +1,6 @@ -import * as utils from 'src/utils'; -import { registerBidder } from 'src/adapters/bidderFactory'; -import { BANNER } from 'src/mediaTypes'; +import * as utils from '../src/utils'; +import { registerBidder } from '../src/adapters/bidderFactory'; +import { BANNER } from '../src/mediaTypes'; const LOOPME_ENDPOINT = 'https://loopme.me/api/hb'; diff --git a/modules/mgidBidAdapter.js b/modules/mgidBidAdapter.js index e1b15ef4b51..c6744d28f45 100644 --- a/modules/mgidBidAdapter.js +++ b/modules/mgidBidAdapter.js @@ -1,7 +1,7 @@ -import {registerBidder} from 'src/adapters/bidderFactory'; +import {registerBidder} from '../src/adapters/bidderFactory'; import * as utils from '../src/utils'; import * as urlUtils from '../src/url'; -import {BANNER, NATIVE} from 'src/mediaTypes'; +import {BANNER, NATIVE} from '../src/mediaTypes'; import {config} from '../src/config'; const DEFAULT_CUR = 'USD'; const BIDDER_CODE = 'mgid'; diff --git a/modules/microadBidAdapter.js b/modules/microadBidAdapter.js index d42e4053fda..391be2c465b 100644 --- a/modules/microadBidAdapter.js +++ b/modules/microadBidAdapter.js @@ -1,5 +1,5 @@ -import { registerBidder } from 'src/adapters/bidderFactory'; -import { BANNER } from 'src/mediaTypes'; +import { registerBidder } from '../src/adapters/bidderFactory'; +import { BANNER } from '../src/mediaTypes'; const BIDDER_CODE = 'microad'; diff --git a/modules/open8BidAdapter.js b/modules/open8BidAdapter.js index be616d0ec30..3c2b94a528a 100644 --- a/modules/open8BidAdapter.js +++ b/modules/open8BidAdapter.js @@ -1,8 +1,8 @@ -import { Renderer } from 'src/Renderer'; +import { Renderer } from '../src/Renderer'; import {ajax} from '../src/ajax'; import * as utils from 'src/utils'; -import { registerBidder } from 'src/adapters/bidderFactory'; -import { VIDEO, BANNER } from 'src/mediaTypes'; +import { registerBidder } from '../src/adapters/bidderFactory'; +import { VIDEO, BANNER } from '../src/mediaTypes'; const BIDDER_CODE = 'open8'; const URL = '//as.vt.open8.com/v1/control/prebid'; diff --git a/modules/otmBidAdapter.js b/modules/otmBidAdapter.js index 57ac414c7b6..ddb4d356f5c 100644 --- a/modules/otmBidAdapter.js +++ b/modules/otmBidAdapter.js @@ -1,5 +1,5 @@ -import {BANNER} from 'src/mediaTypes'; -import {registerBidder} from 'src/adapters/bidderFactory'; +import {BANNER} from '../src/mediaTypes'; +import {registerBidder} from '../src/adapters/bidderFactory'; export const spec = { code: 'otm', diff --git a/modules/prebidmanagerAnalyticsAdapter.js b/modules/prebidmanagerAnalyticsAdapter.js index eb9ad344253..f3474abe95a 100644 --- a/modules/prebidmanagerAnalyticsAdapter.js +++ b/modules/prebidmanagerAnalyticsAdapter.js @@ -9,8 +9,8 @@ const DEFAULT_EVENT_URL = 'https://endpoint.prebidmanager.com/endpoint' const analyticsType = 'endpoint'; const analyticsName = 'Prebid Manager Analytics: '; -var utils = require('src/utils'); -var CONSTANTS = require('src/constants.json'); +var utils = require('../src/utils'); +var CONSTANTS = require('../src/constants.json'); let ajax = ajaxBuilder(0); var _VERSION = 1; diff --git a/modules/reklamstoreBidAdapter.js b/modules/reklamstoreBidAdapter.js index 49f08ab0473..2a659ec0f07 100644 --- a/modules/reklamstoreBidAdapter.js +++ b/modules/reklamstoreBidAdapter.js @@ -1,6 +1,6 @@ -import * as utils from 'src/utils'; -import { registerBidder } from 'src/adapters/bidderFactory'; -import { BANNER } from 'src/mediaTypes'; +import * as utils from '../src/utils'; +import { registerBidder } from '../src/adapters/bidderFactory'; +import { BANNER } from '../src/mediaTypes'; const BIDDER_CODE = 'reklamstore'; const ENDPOINT_URL = '//ads.rekmob.com/m/prebid'; diff --git a/modules/slimcutBidAdapter.js b/modules/slimcutBidAdapter.js index def490d6ab9..1f52e2cbc30 100644 --- a/modules/slimcutBidAdapter.js +++ b/modules/slimcutBidAdapter.js @@ -1,6 +1,6 @@ -import * as utils from 'src/utils'; -import { registerBidder } from 'src/adapters/bidderFactory'; -import { ajax } from 'src/ajax'; +import * as utils from '../src/utils'; +import { registerBidder } from '../src/adapters/bidderFactory'; +import { ajax } from '../src/ajax'; const BIDDER_CODE = 'slimcut'; const ENDPOINT_URL = '//sb.freeskreen.com/pbr'; diff --git a/modules/smartrtbBidAdapter.js b/modules/smartrtbBidAdapter.js index 561ce58e016..1cdef1c474b 100644 --- a/modules/smartrtbBidAdapter.js +++ b/modules/smartrtbBidAdapter.js @@ -1,5 +1,5 @@ -import * as utils from 'src/utils'; -import {registerBidder} from 'src/adapters/bidderFactory'; +import * as utils from '../src/utils'; +import {registerBidder} from '../src/adapters/bidderFactory'; const BIDDER_CODE = 'smartrtb'; function getDomain () { diff --git a/modules/sortableAnalyticsAdapter.js b/modules/sortableAnalyticsAdapter.js index 4682dc09b49..17458065f9a 100644 --- a/modules/sortableAnalyticsAdapter.js +++ b/modules/sortableAnalyticsAdapter.js @@ -1,10 +1,10 @@ -import adapter from 'src/AnalyticsAdapter'; -import CONSTANTS from 'src/constants.json'; -import adapterManager from 'src/adapterManager'; -import * as utils from 'src/utils'; -import {ajax} from 'src/ajax'; -import {getGlobal} from 'src/prebidGlobal'; -import { config } from 'src/config'; +import adapter from '../src/AnalyticsAdapter'; +import CONSTANTS from '../src/constants.json'; +import adapterManager from '../src/adapterManager'; +import * as utils from '../src/utils'; +import {ajax} from '../src/ajax'; +import {getGlobal} from '../src/prebidGlobal'; +import { config } from '../src/config'; const DEFAULT_PROTOCOL = 'https'; const DEFAULT_HOST = 'pa.deployads.com'; diff --git a/modules/timBidAdapter.js b/modules/timBidAdapter.js index 0539f37deef..449254671f4 100644 --- a/modules/timBidAdapter.js +++ b/modules/timBidAdapter.js @@ -1,7 +1,7 @@ -import * as utils from 'src/utils'; -import {registerBidder} from 'src/adapters/bidderFactory'; +import * as utils from '../src/utils'; +import {registerBidder} from '../src/adapters/bidderFactory'; import * as bidfactory from '../src/bidfactory'; -var CONSTANTS = require('src/constants.json'); +var CONSTANTS = require('../src/constants.json'); const BIDDER_CODE = 'tim'; function parseBidRequest(bidRequest) { diff --git a/modules/yieldoneBidAdapter.js b/modules/yieldoneBidAdapter.js index 1e6abf22838..1caf44e790f 100644 --- a/modules/yieldoneBidAdapter.js +++ b/modules/yieldoneBidAdapter.js @@ -1,7 +1,7 @@ import * as utils from '../src/utils'; import {config} from '../src/config'; import {registerBidder} from '../src/adapters/bidderFactory'; -import { Renderer } from 'src/Renderer'; +import { Renderer } from '../src/Renderer'; import { BANNER, VIDEO } from '../src/mediaTypes'; const BIDDER_CODE = 'yieldone'; From e53dad0fedbcca91dffc31279551955e8bdc9595 Mon Sep 17 00:00:00 2001 From: Rich Snapp Date: Tue, 18 Jun 2019 10:49:40 -0600 Subject: [PATCH 130/146] add --analyze arg for webpack bundle analyzing (#3914) --- package-lock.json | 517 ++++++++++++++++++++++++++++++++++++++++------ package.json | 1 + webpack.conf.js | 35 ++-- 3 files changed, 482 insertions(+), 71 deletions(-) diff --git a/package-lock.json b/package-lock.json index d313ff22ea0..199aff8c33b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.16.0-pre", + "version": "2.20.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -922,6 +922,12 @@ } } }, + "acorn-walk": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", + "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", + "dev": true + }, "after": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", @@ -1194,6 +1200,12 @@ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", "dev": true }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, "array-from": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", @@ -2670,6 +2682,18 @@ "callsite": "1.0.0" } }, + "bfj": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.1.tgz", + "integrity": "sha512-+GUNvzHR4nRyGybQc2WpNJL4MJazMuvf92ueIyA0bIkPRwhhQu3IfZQ2PSoVPpCBJfmoSdOxu5rnotfFLlvYRQ==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "check-types": "^7.3.0", + "hoopy": "^0.1.2", + "tryer": "^1.0.0" + } + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -3108,7 +3132,7 @@ }, "query-string": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "resolved": "http://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", "dev": true, "requires": { @@ -3262,6 +3286,12 @@ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", "dev": true }, + "check-types": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-7.4.0.tgz", + "integrity": "sha512-YbulWHdfP99UfZ73NcUDlNJhEIDgm9Doq9GhpyXbF+7Aegi3CVV7qqMCKTTqJxlvEvnQBp9IA+dxsGN6xK/nSg==", + "dev": true + }, "chokidar": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.5.tgz", @@ -3480,7 +3510,7 @@ }, "commander": { "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true }, @@ -3622,6 +3652,15 @@ "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", "dev": true }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", @@ -3649,6 +3688,12 @@ "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", "dev": true }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", @@ -4534,6 +4579,12 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", "dev": true }, + "ejs": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", + "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==", + "dev": true + }, "electron-to-chromium": { "version": "1.3.124", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.124.tgz", @@ -4585,7 +4636,7 @@ "engine.io": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", - "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", + "integrity": "sha1-tgKBw1SEpw7gNR6g6/+D7IyVIqI=", "dev": true, "requires": { "accepts": "~1.3.4", @@ -4599,7 +4650,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -4615,7 +4666,7 @@ }, "engine.io-client": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", + "resolved": "http://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", "dev": true, "requires": { @@ -4635,7 +4686,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -5326,7 +5377,7 @@ }, "event-stream": { "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", "dev": true, "requires": { @@ -5535,6 +5586,249 @@ "homedir-polyfill": "^1.0.1" } }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dev": true, + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "dev": true + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "dev": true, + "requires": { + "mime-db": "1.40.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + } + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -5733,6 +6027,12 @@ "minimatch": "^3.0.3" } }, + "filesize": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", + "dev": true + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -5865,7 +6165,7 @@ "flatted": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", - "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", + "integrity": "sha1-VRIrZTbqSWtLRIk+4mCBQdENmRY=", "dev": true }, "flush-write-stream": { @@ -5951,6 +6251,12 @@ "samsam": "~1.1" } }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -6074,8 +6380,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -6099,15 +6404,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6124,22 +6427,19 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -6270,8 +6570,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -6285,7 +6584,6 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6302,7 +6600,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6311,15 +6608,13 @@ "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -6340,7 +6635,6 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -6429,8 +6723,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -6444,7 +6737,6 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -6540,8 +6832,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -6583,7 +6874,6 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6605,7 +6895,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6654,15 +6943,13 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true, - "optional": true + "dev": true } } }, @@ -7337,7 +7624,7 @@ "gulp-connect": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/gulp-connect/-/gulp-connect-5.7.0.tgz", - "integrity": "sha512-8tRcC6wgXMLakpPw9M7GRJIhxkYdgZsXwn7n56BA2bQYGLR9NOPhMzx7js+qYDy6vhNkbApGKURjAw1FjY4pNA==", + "integrity": "sha1-fpJfXkw06/7fnzGFdpZuj+iEDVo=", "dev": true, "requires": { "ansi-colors": "^2.0.5", @@ -7354,7 +7641,7 @@ "ansi-colors": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-2.0.5.tgz", - "integrity": "sha512-yAdfUZ+c2wetVNIFsNRn44THW+Lty6S5TwMpUfLA/UaGhiXbBv/F8E60/1hMLd0cnF/CDoWH8vzVaI5bAcHCjw==", + "integrity": "sha1-XaN4Jf7z51872kf3YNZL/RDhXhA=", "dev": true } } @@ -7764,6 +8051,16 @@ "glogg": "^1.0.0" } }, + "gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "dev": true, + "requires": { + "duplexer": "^0.1.1", + "pify": "^4.0.1" + } + }, "handlebars": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.1.tgz", @@ -8037,6 +8334,12 @@ "parse-passwd": "^1.0.0" } }, + "hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "dev": true + }, "hosted-git-info": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", @@ -8057,7 +8360,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -8286,6 +8589,12 @@ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", "dev": true }, + "ipaddr.js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==", + "dev": true + }, "is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", @@ -9053,7 +9362,7 @@ "karma": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/karma/-/karma-3.1.4.tgz", - "integrity": "sha512-31Vo8Qr5glN+dZEVIpnPCxEGleqE0EY6CtC2X9TagRV3rRQ3SNrvfhddICkJgUK3AgqpeKSZau03QumTGhGoSw==", + "integrity": "sha1-OJDKlyKxDR0UtybhM1kxRVeISZ4=", "dev": true, "requires": { "bluebird": "^3.3.0", @@ -9678,7 +9987,7 @@ "log4js": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.6.tgz", - "integrity": "sha512-ezXZk6oPJCWL483zj64pNkMuY/NcRX5MPiB0zE6tjZM137aeusrOnW1ecxgF9cmwMWkBMhjteQxBPoZBh9FDxQ==", + "integrity": "sha1-5srO2Uln7uuc45n5+GgqSysoyP8=", "dev": true, "requires": { "circular-json": "^0.5.5", @@ -9691,7 +10000,7 @@ "circular-json": { "version": "0.5.9", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", - "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==", + "integrity": "sha1-kydjroj0996teg0JyKUaR0OlOx0=", "dev": true }, "debug": { @@ -10132,6 +10441,12 @@ } } }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, "merge-stream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", @@ -10141,6 +10456,12 @@ "readable-stream": "^2.0.1" } }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -10228,7 +10549,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -10264,7 +10585,7 @@ "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true } @@ -10851,6 +11172,12 @@ "mimic-fn": "^1.0.0" } }, + "opener": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", + "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", + "dev": true + }, "opn": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", @@ -10872,7 +11199,7 @@ "dependencies": { "minimist": { "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", "dev": true }, @@ -11433,6 +11760,16 @@ "integrity": "sha512-Fx65lf9/YDn3hUX08XUc0J8rSux36rEsyiv21ZGUC1mOyeM3lTRpZLcrm8aAolzS4itwVfm7TAPyxC2E5zd6xg==", "dev": true }, + "proxy-addr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.0" + } + }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -12145,7 +12482,7 @@ "rfdc": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.2.tgz", - "integrity": "sha512-92ktAgvZhBzYTIK0Mja9uen5q5J3NRVMoDkJL2VMwq6SXjVCgqvQeVP2XAaUY6HT+XpQYeLSjb3UoitBryKmdA==", + "integrity": "sha1-5uctdPXcOd6PU49l4Aw2wYAY40k=", "dev": true }, "rgb2hex": { @@ -12614,7 +12951,7 @@ "socket.io": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", - "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", + "integrity": "sha1-oGnF/qvuPmshSnW0DOBlLhz7mYA=", "dev": true, "requires": { "debug": "~3.1.0", @@ -12628,7 +12965,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -12651,7 +12988,7 @@ "socket.io-client": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", - "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", + "integrity": "sha1-3LOBA0NqtFeN2wJmOK4vIbYjZx8=", "dev": true, "requires": { "backo2": "1.0.2", @@ -12673,7 +13010,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -12689,7 +13026,7 @@ }, "socket.io-parser": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", + "resolved": "http://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", "dev": true, "requires": { @@ -12701,7 +13038,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -12819,7 +13156,7 @@ }, "split": { "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "resolved": "http://registry.npmjs.org/split/-/split-0.3.3.tgz", "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", "dev": true, "requires": { @@ -12953,7 +13290,7 @@ }, "stream-combiner": { "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", "dev": true, "requires": { @@ -13420,6 +13757,12 @@ "through2": "^2.0.3" } }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", @@ -13474,6 +13817,12 @@ "integrity": "sha512-fwkLWH+DimvA4YCy+/nvJd61nWQQ2liO/nF/RjkTpiOGi+zxZzVkhb1mvbHIIW4b/8nDsYI8uTmAlc0nNkRMOw==", "dev": true }, + "tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", + "dev": true + }, "tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", @@ -13954,7 +14303,7 @@ "useragent": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", - "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", + "integrity": "sha1-IX+UOtVAyyEoZYqyP8lg9qiMmXI=", "dev": true, "requires": { "lru-cache": "4.1.x", @@ -14023,6 +14372,12 @@ "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=", "dev": true }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -14565,6 +14920,50 @@ } } }, + "webpack-bundle-analyzer": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.3.2.tgz", + "integrity": "sha512-7qvJLPKB4rRWZGjVp5U1KEjwutbDHSKboAl0IfafnrdXMrgC0tOtZbQD6Rw0u4cmpgRN4O02Fc0t8eAT+FgGzA==", + "dev": true, + "requires": { + "acorn": "^6.0.7", + "acorn-walk": "^6.1.1", + "bfj": "^6.1.1", + "chalk": "^2.4.1", + "commander": "^2.18.0", + "ejs": "^2.6.1", + "express": "^4.16.3", + "filesize": "^3.6.1", + "gzip-size": "^5.0.0", + "lodash": "^4.17.10", + "mkdirp": "^0.5.1", + "opener": "^1.5.1", + "ws": "^6.0.0" + }, + "dependencies": { + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", + "dev": true + }, + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "dev": true + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, "webpack-core": { "version": "0.6.9", "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", @@ -14594,7 +14993,7 @@ }, "webpack-dev-middleware": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz", + "resolved": "http://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz", "integrity": "sha512-tj5LLD9r4tDuRIDa5Mu9lnY2qBBehAITv6A9irqXhw/HQquZgTx3BCd57zYbU2gMDnncA49ufK2qVQSbaKJwOw==", "dev": true, "requires": { diff --git a/package.json b/package.json index 4a3d8b2c959..11d5ee5901f 100755 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "wdio-spec-reporter": "^0.1.5", "webdriverio": "^4.13.2", "webpack": "^3.0.0", + "webpack-bundle-analyzer": "^3.3.2", "webpack-stream": "^3.2.0", "yargs": "^1.3.1" }, diff --git a/webpack.conf.js b/webpack.conf.js index 9518b972b1b..61cdf82df32 100644 --- a/webpack.conf.js +++ b/webpack.conf.js @@ -3,12 +3,34 @@ var path = require('path'); var webpack = require('webpack'); var helpers = require('./gulpHelpers'); var RequireEnsureWithoutJsonp = require('./plugins/RequireEnsureWithoutJsonp.js'); +var { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); +var argv = require('yargs').argv; // list of module names to never include in the common bundle chunk var neverBundle = [ 'AnalyticsAdapter.js' ]; +var plugins = [ + new RequireEnsureWithoutJsonp() +]; + +if (argv.analyze) { + plugins.push( + new BundleAnalyzerPlugin() + ) +} + +plugins.push( // this plugin must be last so it can be easily removed for karma unit tests + new webpack.optimize.CommonsChunkPlugin({ + name: 'prebid', + filename: 'prebid-core.js', + minChunks: function(module, count) { + return !(count < 2 || neverBundle.indexOf(path.basename(module.resource)) !== -1) + } + }) +); + module.exports = { devtool: 'source-map', resolve: { @@ -43,16 +65,5 @@ module.exports = { } ] }, - plugins: [ - new RequireEnsureWithoutJsonp(), - - // this plugin must be last so it can be easily removed for karma unit tests - new webpack.optimize.CommonsChunkPlugin({ - name: 'prebid', - filename: 'prebid-core.js', - minChunks: function(module, count) { - return !(count < 2 || neverBundle.indexOf(path.basename(module.resource)) !== -1) - } - }) - ] + plugins }; From f6239dea8941c957169e31ea62d1723645a0a741 Mon Sep 17 00:00:00 2001 From: Michael Rooke Date: Tue, 18 Jun 2019 12:50:17 -0400 Subject: [PATCH 131/146] Standardize permission bits (#3872) * Standardize the perimssion bits used for modules - Most adapters use 664 (i.e. read/write, but are not executable), but some use 775. Make all adapters use 664. * Update link in documentation --- CONTRIBUTING.md | 2 +- modules/advangelistsBidAdapter.js | 0 modules/advangelistsBidAdapter.md | 0 modules/cpmstarBidAdapter.js | 0 modules/cpmstarBidAdapter.md | 0 modules/criteoBidAdapter.js | 0 modules/criteoBidAdapter.md | 0 modules/danmarketBidAdapter.md | 0 modules/fairtradeBidAdapter.md | 0 modules/gridBidAdapter.md | 0 modules/gxoneBidAdapter.md | 0 modules/oneVideoBidAdapter.md | 0 modules/saraBidAdapter.md | 0 modules/sekindoUMBidAdapter.md | 0 modules/supply2BidAdapter.md | 0 modules/trustxBidAdapter.md | 0 modules/visxBidAdapter.js | 0 modules/visxBidAdapter.md | 0 18 files changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 modules/advangelistsBidAdapter.js mode change 100755 => 100644 modules/advangelistsBidAdapter.md mode change 100755 => 100644 modules/cpmstarBidAdapter.js mode change 100755 => 100644 modules/cpmstarBidAdapter.md mode change 100755 => 100644 modules/criteoBidAdapter.js mode change 100755 => 100644 modules/criteoBidAdapter.md mode change 100755 => 100644 modules/danmarketBidAdapter.md mode change 100755 => 100644 modules/fairtradeBidAdapter.md mode change 100755 => 100644 modules/gridBidAdapter.md mode change 100755 => 100644 modules/gxoneBidAdapter.md mode change 100755 => 100644 modules/oneVideoBidAdapter.md mode change 100755 => 100644 modules/saraBidAdapter.md mode change 100755 => 100644 modules/sekindoUMBidAdapter.md mode change 100755 => 100644 modules/supply2BidAdapter.md mode change 100755 => 100644 modules/trustxBidAdapter.md mode change 100755 => 100644 modules/visxBidAdapter.js mode change 100755 => 100644 modules/visxBidAdapter.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b82b249fa36..9c00a2bf51a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ commit your changes, and [open a pull request](https://help.github.com/articles/ master branch. Pull requests must have 80% code coverage before beign considered for merge. -Additional details about the process can be found [here](./pr_review.md). +Additional details about the process can be found [here](./PR_REVIEW.md). ## Issues [prebid.org](http://prebid.org/) contains documentation that may help answer questions you have about using Prebid.js. diff --git a/modules/advangelistsBidAdapter.js b/modules/advangelistsBidAdapter.js old mode 100755 new mode 100644 diff --git a/modules/advangelistsBidAdapter.md b/modules/advangelistsBidAdapter.md old mode 100755 new mode 100644 diff --git a/modules/cpmstarBidAdapter.js b/modules/cpmstarBidAdapter.js old mode 100755 new mode 100644 diff --git a/modules/cpmstarBidAdapter.md b/modules/cpmstarBidAdapter.md old mode 100755 new mode 100644 diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js old mode 100755 new mode 100644 diff --git a/modules/criteoBidAdapter.md b/modules/criteoBidAdapter.md old mode 100755 new mode 100644 diff --git a/modules/danmarketBidAdapter.md b/modules/danmarketBidAdapter.md old mode 100755 new mode 100644 diff --git a/modules/fairtradeBidAdapter.md b/modules/fairtradeBidAdapter.md old mode 100755 new mode 100644 diff --git a/modules/gridBidAdapter.md b/modules/gridBidAdapter.md old mode 100755 new mode 100644 diff --git a/modules/gxoneBidAdapter.md b/modules/gxoneBidAdapter.md old mode 100755 new mode 100644 diff --git a/modules/oneVideoBidAdapter.md b/modules/oneVideoBidAdapter.md old mode 100755 new mode 100644 diff --git a/modules/saraBidAdapter.md b/modules/saraBidAdapter.md old mode 100755 new mode 100644 diff --git a/modules/sekindoUMBidAdapter.md b/modules/sekindoUMBidAdapter.md old mode 100755 new mode 100644 diff --git a/modules/supply2BidAdapter.md b/modules/supply2BidAdapter.md old mode 100755 new mode 100644 diff --git a/modules/trustxBidAdapter.md b/modules/trustxBidAdapter.md old mode 100755 new mode 100644 diff --git a/modules/visxBidAdapter.js b/modules/visxBidAdapter.js old mode 100755 new mode 100644 diff --git a/modules/visxBidAdapter.md b/modules/visxBidAdapter.md old mode 100755 new mode 100644 From 6baa819e85a92a907b641b10b9e8f17ae04d68ad Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Tue, 18 Jun 2019 15:45:48 -0400 Subject: [PATCH 132/146] Prebid 2.20.0 release --- package-lock.json | 2933 +++++++++++++++++++++++++++------------------ package.json | 2 +- 2 files changed, 1758 insertions(+), 1177 deletions(-) diff --git a/package-lock.json b/package-lock.json index 199aff8c33b..4c03303a050 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.20.0-pre", + "version": "2.20.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -14,18 +14,18 @@ } }, "@babel/core": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.4.3.tgz", - "integrity": "sha512-oDpASqKFlbspQfzAE7yaeTmdljSH2ADIvBlb0RwbStltTuWa0+7CCI1fYVINNv9saHPa1W7oaKeuNuKj+RQCvA==", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.4.5.tgz", + "integrity": "sha512-OvjIh6aqXtlsA8ujtGKfC7LYWksYSX8yQcM8Ay3LuvVeQ63lcOKgoZWVqcpFwkd29aYU9rVx7jxhfhiEDV9MZA==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.0", - "@babel/helpers": "^7.4.3", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", + "@babel/generator": "^7.4.4", + "@babel/helpers": "^7.4.4", + "@babel/parser": "^7.4.5", + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.4.5", + "@babel/types": "^7.4.4", "convert-source-map": "^1.1.0", "debug": "^4.1.0", "json5": "^2.1.0", @@ -36,12 +36,12 @@ } }, "@babel/generator": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.0.tgz", - "integrity": "sha512-/v5I+a1jhGSKLgZDcmAUZ4K/VePi43eRkUs3yePW1HB1iANOD5tqJXwGSG4BZhSksP8J9ejSlwGeTiiOFZOrXQ==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", + "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", "dev": true, "requires": { - "@babel/types": "^7.4.0", + "@babel/types": "^7.4.4", "jsesc": "^2.5.1", "lodash": "^4.17.11", "source-map": "^0.5.0", @@ -68,24 +68,24 @@ } }, "@babel/helper-call-delegate": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.4.0.tgz", - "integrity": "sha512-SdqDfbVdNQCBp3WhK2mNdDvHd3BD6qbmIc43CAyjnsfCmgHMeqgDcM3BzY2lchi7HBJGJ2CVdynLWbezaE4mmQ==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz", + "integrity": "sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.4.0", - "@babel/traverse": "^7.4.0", - "@babel/types": "^7.4.0" + "@babel/helper-hoist-variables": "^7.4.4", + "@babel/traverse": "^7.4.4", + "@babel/types": "^7.4.4" } }, "@babel/helper-define-map": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.4.0.tgz", - "integrity": "sha512-wAhQ9HdnLIywERVcSvX40CEJwKdAa1ID4neI9NXQPDOHwwA+57DqwLiPEVy2AIyWzAk0CQ8qx4awO0VUURwLtA==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.4.4.tgz", + "integrity": "sha512-IX3Ln8gLhZpSuqHJSnTNBWGDE9kdkTEWl21A/K7PQ00tseBwbqCHTvNLHSBd9M0R5rER4h5Rsvj9vw0R5SieBg==", "dev": true, "requires": { "@babel/helper-function-name": "^7.1.0", - "@babel/types": "^7.4.0", + "@babel/types": "^7.4.4", "lodash": "^4.17.11" } }, @@ -120,12 +120,12 @@ } }, "@babel/helper-hoist-variables": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.0.tgz", - "integrity": "sha512-/NErCuoe/et17IlAQFKWM24qtyYYie7sFIrW/tIQXpck6vAu2hhtYYsKLBWQV+BQZMbcIYPU/QMYuTufrY4aQw==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz", + "integrity": "sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w==", "dev": true, "requires": { - "@babel/types": "^7.4.0" + "@babel/types": "^7.4.4" } }, "@babel/helper-member-expression-to-functions": { @@ -147,16 +147,16 @@ } }, "@babel/helper-module-transforms": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.4.3.tgz", - "integrity": "sha512-H88T9IySZW25anu5uqyaC1DaQre7ofM+joZtAaO2F8NBdFfupH0SZ4gKjgSFVcvtx/aAirqA9L9Clio2heYbZA==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.4.4.tgz", + "integrity": "sha512-3Z1yp8TVQf+B4ynN7WoHPKS8EkdTbgAEy0nU0rs/1Kw4pDgmvYH3rz3aI11KgxKCba2cn7N+tqzV1mY2HMN96w==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.0.0", "@babel/helper-simple-access": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.0.0", - "@babel/template": "^7.2.2", - "@babel/types": "^7.2.2", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/template": "^7.4.4", + "@babel/types": "^7.4.4", "lodash": "^4.17.11" } }, @@ -176,9 +176,9 @@ "dev": true }, "@babel/helper-regex": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.4.3.tgz", - "integrity": "sha512-hnoq5u96pLCfgjXuj8ZLX3QQ+6nAulS+zSgi6HulUwFbEruRAKwbGLU5OvXkE14L8XW6XsQEKsIDfgthKLRAyA==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.4.4.tgz", + "integrity": "sha512-Y5nuB/kESmR3tKjU8Nkn1wMGEx1tjJX076HBMeL3XLQCu6vA/YRzuTW0bbb+qRnXvQGn+d6Rx953yffl8vEy7Q==", "dev": true, "requires": { "lodash": "^4.17.11" @@ -198,15 +198,15 @@ } }, "@babel/helper-replace-supers": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.4.0.tgz", - "integrity": "sha512-PVwCVnWWAgnal+kJ+ZSAphzyl58XrFeSKSAJRiqg5QToTsjL+Xu1f9+RJ+d+Q0aPhPfBGaYfkox66k86thxNSg==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.4.4.tgz", + "integrity": "sha512-04xGEnd+s01nY1l15EuMS1rfKktNF+1CkKmHoErDppjAAZL+IUBZpzT748x262HF7fibaQPhbvWUl5HeSt1EXg==", "dev": true, "requires": { "@babel/helper-member-expression-to-functions": "^7.0.0", "@babel/helper-optimise-call-expression": "^7.0.0", - "@babel/traverse": "^7.4.0", - "@babel/types": "^7.4.0" + "@babel/traverse": "^7.4.4", + "@babel/types": "^7.4.4" } }, "@babel/helper-simple-access": { @@ -220,12 +220,12 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.0.tgz", - "integrity": "sha512-7Cuc6JZiYShaZnybDmfwhY4UYHzI6rlqhWjaIqbsJGsIqPimEYy5uh3akSRLMg65LSdSEnJ8a8/bWQN6u2oMGw==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", + "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", "dev": true, "requires": { - "@babel/types": "^7.4.0" + "@babel/types": "^7.4.4" } }, "@babel/helper-wrap-function": { @@ -241,14 +241,14 @@ } }, "@babel/helpers": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.3.tgz", - "integrity": "sha512-BMh7X0oZqb36CfyhvtbSmcWc3GXocfxv3yNsAEuM0l+fAqSO22rQrUpijr3oE/10jCTrB6/0b9kzmG4VetCj8Q==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.4.tgz", + "integrity": "sha512-igczbR/0SeuPR8RFfC7tGrbdTbFL3QTvH6D+Z6zNxnTe//GyqmtHmDkzrqDmyZ3eSwPqB/LhyKoU5DXsp+Vp2A==", "dev": true, "requires": { - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0" + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.4.4", + "@babel/types": "^7.4.4" } }, "@babel/highlight": { @@ -263,9 +263,9 @@ } }, "@babel/parser": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.3.tgz", - "integrity": "sha512-gxpEUhTS1sGA63EGQGuA+WESPR/6tz6ng7tSHFCmaTJK/cGK8y37cBTspX+U2xCAue2IQVvF6Z0oigmjwD8YGQ==", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz", + "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==", "dev": true }, "@babel/plugin-proposal-async-generator-functions": { @@ -290,9 +290,9 @@ } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.3.tgz", - "integrity": "sha512-xC//6DNSSHVjq8O2ge0dyYlhshsH4T7XdCVoxbi5HzLYWfsC5ooFlJjrXk8RcAT+hjHAK9UjBXdylzSoDK3t4g==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.4.tgz", + "integrity": "sha512-dMBG6cSPBbHeEBdFXeQ2QLc5gUpg4Vkaz8octD4aoW/ISO+jBOcsuxYL7bsb5WSu8RLP6boxrBIALEHgoHtO9g==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", @@ -310,13 +310,13 @@ } }, "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.0.tgz", - "integrity": "sha512-h/KjEZ3nK9wv1P1FSNb9G079jXrNYR0Ko+7XkOx85+gM24iZbPn0rh4vCftk+5QKY7y1uByFataBTmX7irEF1w==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz", + "integrity": "sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.0.0", + "@babel/helper-regex": "^7.4.4", "regexpu-core": "^4.5.4" } }, @@ -366,9 +366,9 @@ } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.0.tgz", - "integrity": "sha512-EeaFdCeUULM+GPFEsf7pFcNSxM7hYjoj5fiYbyuiXobW4JhFnjAv9OWzNwHyHcKoPNpAfeRDuW6VyaXEDUBa7g==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.4.tgz", + "integrity": "sha512-YiqW2Li8TXmzgbXw+STsSqPBPFnGviiaSp6CYOq55X8GQ2SGVLrXB6pNid8HkqkZAzOH6knbai3snhP7v0fNwA==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.0.0", @@ -386,9 +386,9 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.4.0.tgz", - "integrity": "sha512-AWyt3k+fBXQqt2qb9r97tn3iBwFpiv9xdAiG+Gr2HpAZpuayvbL55yWrsV3MyHvXk/4vmSiedhDRl1YI2Iy5nQ==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.4.4.tgz", + "integrity": "sha512-jkTUyWZcTrwxu5DD4rWz6rDB5Cjdmgz6z7M7RLXOJyCUkFBawssDGcGh8M/0FTSB87avyJI1HsTwUXp9nKA1PA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", @@ -396,18 +396,18 @@ } }, "@babel/plugin-transform-classes": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.4.3.tgz", - "integrity": "sha512-PUaIKyFUDtG6jF5DUJOfkBdwAS/kFFV3XFk7Nn0a6vR7ZT8jYw5cGtIlat77wcnd0C6ViGqo/wyNf4ZHytF/nQ==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.4.4.tgz", + "integrity": "sha512-/e44eFLImEGIpL9qPxSRat13I5QNRgBLu2hOQJCF7VLy/otSM/sypV1+XaIw5+502RX/+6YaSAPmldk+nhHDPw==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-define-map": "^7.4.0", + "@babel/helper-define-map": "^7.4.4", "@babel/helper-function-name": "^7.1.0", "@babel/helper-optimise-call-expression": "^7.0.0", "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.4.0", - "@babel/helper-split-export-declaration": "^7.4.0", + "@babel/helper-replace-supers": "^7.4.4", + "@babel/helper-split-export-declaration": "^7.4.4", "globals": "^11.1.0" } }, @@ -421,22 +421,22 @@ } }, "@babel/plugin-transform-destructuring": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.3.tgz", - "integrity": "sha512-rVTLLZpydDFDyN4qnXdzwoVpk1oaXHIvPEOkOLyr88o7oHxVc/LyrnDx+amuBWGOwUb7D1s/uLsKBNTx08htZg==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.4.tgz", + "integrity": "sha512-/aOx+nW0w8eHiEHm+BTERB2oJn5D127iye/SUQl7NjHy0lf+j7h4MKMMSOwdazGq9OxgiNADncE+SRJkCxjZpQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.3.tgz", - "integrity": "sha512-9Arc2I0AGynzXRR/oPdSALv3k0rM38IMFyto7kOCwb5F9sLUt2Ykdo3V9yUPR+Bgr4kb6bVEyLkPEiBhzcTeoA==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz", + "integrity": "sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.4.3", + "@babel/helper-regex": "^7.4.4", "regexpu-core": "^4.5.4" } }, @@ -460,18 +460,18 @@ } }, "@babel/plugin-transform-for-of": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.3.tgz", - "integrity": "sha512-UselcZPwVWNSURnqcfpnxtMehrb8wjXYOimlYQPBnup/Zld426YzIhNEvuRsEWVHfESIECGrxoI6L5QqzuLH5Q==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz", + "integrity": "sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-function-name": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.3.tgz", - "integrity": "sha512-uT5J/3qI/8vACBR9I1GlAuU/JqBtWdfCrynuOkrWG6nCDieZd5przB1vfP59FRHBZQ9DC2IUfqr/xKqzOD5x0A==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz", + "integrity": "sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA==", "dev": true, "requires": { "@babel/helper-function-name": "^7.1.0", @@ -507,23 +507,23 @@ } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.3.tgz", - "integrity": "sha512-sMP4JqOTbMJMimqsSZwYWsMjppD+KRyDIUVW91pd7td0dZKAvPmhCaxhOzkzLParKwgQc7bdL9UNv+rpJB0HfA==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.4.tgz", + "integrity": "sha512-4sfBOJt58sEo9a2BQXnZq+Q3ZTSAUXyK3E30o36BOGnJ+tvJ6YSxF0PG6kERvbeISgProodWuI9UVG3/FMY6iw==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.4.3", + "@babel/helper-module-transforms": "^7.4.4", "@babel/helper-plugin-utils": "^7.0.0", "@babel/helper-simple-access": "^7.1.0" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.4.0.tgz", - "integrity": "sha512-gjPdHmqiNhVoBqus5qK60mWPp1CmYWp/tkh11mvb0rrys01HycEGD7NvvSoKXlWEfSM9TcL36CpsK8ElsADptQ==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.4.4.tgz", + "integrity": "sha512-MSiModfILQc3/oqnG7NrP1jHaSPryO6tA2kOMmAQApz5dayPxWiHqmq4sWH2xF5LcQK56LlbKByCd8Aah/OIkQ==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.4.0", + "@babel/helper-hoist-variables": "^7.4.4", "@babel/helper-plugin-utils": "^7.0.0" } }, @@ -538,18 +538,18 @@ } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.2.tgz", - "integrity": "sha512-NsAuliSwkL3WO2dzWTOL1oZJHm0TM8ZY8ZSxk2ANyKkt5SQlToGA4pzctmq1BEjoacurdwZ3xp2dCQWJkME0gQ==", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz", + "integrity": "sha512-z7+2IsWafTBbjNsOxU/Iv5CvTJlr5w4+HGu1HovKYTtgJ362f7kBcQglkfmlspKKZ3bgrbSGvLfNx++ZJgCWsg==", "dev": true, "requires": { - "regexp-tree": "^0.1.0" + "regexp-tree": "^0.1.6" } }, "@babel/plugin-transform-new-target": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.0.tgz", - "integrity": "sha512-6ZKNgMQmQmrEX/ncuCwnnw1yVGoaOW5KpxNhoWI7pCQdA0uZ0HqHGqenCUIENAnxRjy2WwNQ30gfGdIgqJXXqw==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz", + "integrity": "sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" @@ -566,12 +566,12 @@ } }, "@babel/plugin-transform-parameters": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.3.tgz", - "integrity": "sha512-ULJYC2Vnw96/zdotCZkMGr2QVfKpIT/4/K+xWWY0MbOJyMZuk660BGkr3bEKWQrrciwz6xpmft39nA4BF7hJuA==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz", + "integrity": "sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==", "dev": true, "requires": { - "@babel/helper-call-delegate": "^7.4.0", + "@babel/helper-call-delegate": "^7.4.4", "@babel/helper-get-function-arity": "^7.0.0", "@babel/helper-plugin-utils": "^7.0.0" } @@ -586,12 +586,12 @@ } }, "@babel/plugin-transform-regenerator": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.3.tgz", - "integrity": "sha512-kEzotPuOpv6/iSlHroCDydPkKYw7tiJGKlmYp6iJn4a6C/+b2FdttlJsLKYxolYHgotTJ5G5UY5h0qey5ka3+A==", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz", + "integrity": "sha512-gBKRh5qAaCWntnd09S8QC7r3auLCqq5DI6O0DlfoyDjslSBVqBibrMdsqO+Uhmx3+BlOmE/Kw1HFxmGbv0N9dA==", "dev": true, "requires": { - "regenerator-transform": "^0.13.4" + "regenerator-transform": "^0.14.0" } }, "@babel/plugin-transform-reserved-words": { @@ -632,9 +632,9 @@ } }, "@babel/plugin-transform-template-literals": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.2.0.tgz", - "integrity": "sha512-FkPix00J9A/XWXv4VoKJBMeSkyY9x/TqIh76wzcdfl57RJJcf8CehQ08uwfhCDNtRQYtHQKBTwKZDEyjE13Lwg==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz", + "integrity": "sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.0.0", @@ -651,104 +651,104 @@ } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.3.tgz", - "integrity": "sha512-lnSNgkVjL8EMtnE8eSS7t2ku8qvKH3eqNf/IwIfnSPUqzgqYmRwzdsQWv4mNQAN9Nuo6Gz1Y0a4CSmdpu1Pp6g==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz", + "integrity": "sha512-il+/XdNw01i93+M9J9u4T7/e/Ue/vWfNZE4IRUQjplu2Mqb/AFTDimkw2tdEdSH50wuQXZAbXSql0UphQke+vA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.4.3", + "@babel/helper-regex": "^7.4.4", "regexpu-core": "^4.5.4" } }, "@babel/preset-env": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.4.3.tgz", - "integrity": "sha512-FYbZdV12yHdJU5Z70cEg0f6lvtpZ8jFSDakTm7WXeJbLXh4R0ztGEu/SW7G1nJ2ZvKwDhz8YrbA84eYyprmGqw==", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.4.5.tgz", + "integrity": "sha512-f2yNVXM+FsR5V8UwcFeIHzHWgnhXg3NpRmy0ADvALpnhB0SLbCvrCRr4BLOUYbQNLS+Z0Yer46x9dJXpXewI7w==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.0.0", "@babel/helper-plugin-utils": "^7.0.0", "@babel/plugin-proposal-async-generator-functions": "^7.2.0", "@babel/plugin-proposal-json-strings": "^7.2.0", - "@babel/plugin-proposal-object-rest-spread": "^7.4.3", + "@babel/plugin-proposal-object-rest-spread": "^7.4.4", "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", "@babel/plugin-syntax-async-generators": "^7.2.0", "@babel/plugin-syntax-json-strings": "^7.2.0", "@babel/plugin-syntax-object-rest-spread": "^7.2.0", "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", "@babel/plugin-transform-arrow-functions": "^7.2.0", - "@babel/plugin-transform-async-to-generator": "^7.4.0", + "@babel/plugin-transform-async-to-generator": "^7.4.4", "@babel/plugin-transform-block-scoped-functions": "^7.2.0", - "@babel/plugin-transform-block-scoping": "^7.4.0", - "@babel/plugin-transform-classes": "^7.4.3", + "@babel/plugin-transform-block-scoping": "^7.4.4", + "@babel/plugin-transform-classes": "^7.4.4", "@babel/plugin-transform-computed-properties": "^7.2.0", - "@babel/plugin-transform-destructuring": "^7.4.3", - "@babel/plugin-transform-dotall-regex": "^7.4.3", + "@babel/plugin-transform-destructuring": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", "@babel/plugin-transform-duplicate-keys": "^7.2.0", "@babel/plugin-transform-exponentiation-operator": "^7.2.0", - "@babel/plugin-transform-for-of": "^7.4.3", - "@babel/plugin-transform-function-name": "^7.4.3", + "@babel/plugin-transform-for-of": "^7.4.4", + "@babel/plugin-transform-function-name": "^7.4.4", "@babel/plugin-transform-literals": "^7.2.0", "@babel/plugin-transform-member-expression-literals": "^7.2.0", "@babel/plugin-transform-modules-amd": "^7.2.0", - "@babel/plugin-transform-modules-commonjs": "^7.4.3", - "@babel/plugin-transform-modules-systemjs": "^7.4.0", + "@babel/plugin-transform-modules-commonjs": "^7.4.4", + "@babel/plugin-transform-modules-systemjs": "^7.4.4", "@babel/plugin-transform-modules-umd": "^7.2.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.4.2", - "@babel/plugin-transform-new-target": "^7.4.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.4.5", + "@babel/plugin-transform-new-target": "^7.4.4", "@babel/plugin-transform-object-super": "^7.2.0", - "@babel/plugin-transform-parameters": "^7.4.3", + "@babel/plugin-transform-parameters": "^7.4.4", "@babel/plugin-transform-property-literals": "^7.2.0", - "@babel/plugin-transform-regenerator": "^7.4.3", + "@babel/plugin-transform-regenerator": "^7.4.5", "@babel/plugin-transform-reserved-words": "^7.2.0", "@babel/plugin-transform-shorthand-properties": "^7.2.0", "@babel/plugin-transform-spread": "^7.2.0", "@babel/plugin-transform-sticky-regex": "^7.2.0", - "@babel/plugin-transform-template-literals": "^7.2.0", + "@babel/plugin-transform-template-literals": "^7.4.4", "@babel/plugin-transform-typeof-symbol": "^7.2.0", - "@babel/plugin-transform-unicode-regex": "^7.4.3", - "@babel/types": "^7.4.0", - "browserslist": "^4.5.2", - "core-js-compat": "^3.0.0", + "@babel/plugin-transform-unicode-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "browserslist": "^4.6.0", + "core-js-compat": "^3.1.1", "invariant": "^2.2.2", "js-levenshtein": "^1.1.3", "semver": "^5.5.0" } }, "@babel/template": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.0.tgz", - "integrity": "sha512-SOWwxxClTTh5NdbbYZ0BmaBVzxzTh2tO/TeLTbF6MO6EzVhHTnff8CdBXx3mEtazFBoysmEM6GU/wF+SuSx4Fw==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", + "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.0", - "@babel/types": "^7.4.0" + "@babel/parser": "^7.4.4", + "@babel/types": "^7.4.4" } }, "@babel/traverse": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.3.tgz", - "integrity": "sha512-HmA01qrtaCwwJWpSKpA948cBvU5BrmviAief/b3AVw936DtcdsTexlbyzNuDnthwhOQ37xshn7hvQaEQk7ISYQ==", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.5.tgz", + "integrity": "sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.0", + "@babel/generator": "^7.4.4", "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/types": "^7.4.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/parser": "^7.4.5", + "@babel/types": "^7.4.4", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.11" } }, "@babel/types": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.0.tgz", - "integrity": "sha512-aPvkXyU2SPOnztlgo8n9cEiXW755mgyvueUPcpStqdzoSPm0fjO0vQBjLkt3JKJW7ufikfcnMTTPsN1xaTsBPA==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", + "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -840,9 +840,9 @@ } }, "@sinonjs/samsam": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.1.tgz", - "integrity": "sha512-wRSfmyd81swH0hA1bxJZJ57xr22kC07a1N4zuIL47yTS04bDk6AoCkczcqHEjcRPmJ+FruGJ9WBQiJwMtIElFw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.2.tgz", + "integrity": "sha512-ILO/rR8LfAb60Y1Yfp9vxfYAASK43NFC2mLzpvLUbCQY/Qu8YwReboseu8aheCEkyElZF2L2T9mHcR2bgdvZyA==", "dev": true, "requires": { "@sinonjs/commons": "^1.0.2", @@ -873,13 +873,13 @@ "dev": true }, "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", "dev": true, "requires": { - "mime-types": "~2.1.18", - "negotiator": "0.6.1" + "mime-types": "~2.1.24", + "negotiator": "0.6.2" } }, "acorn": { @@ -935,9 +935,9 @@ "dev": true }, "agent-base": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", - "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", "dev": true, "requires": { "es6-promisify": "^5.0.0" @@ -990,13 +990,10 @@ "dev": true }, "ansi-colors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", - "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", - "dev": true, - "requires": { - "ansi-wrap": "^0.1.0" - } + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true }, "ansi-escapes": { "version": "3.2.0", @@ -1194,6 +1191,12 @@ "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", "dev": true }, + "array-filter": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", + "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=", + "dev": true + }, "array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", @@ -1212,6 +1215,16 @@ "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", "dev": true }, + "array-includes": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", + "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.7.0" + } + }, "array-initial": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", @@ -1247,6 +1260,18 @@ } } }, + "array-map": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", + "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", + "dev": true + }, + "array-reduce": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", + "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", + "dev": true + }, "array-slice": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", @@ -1311,11 +1336,12 @@ } }, "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", "dev": true, "requires": { + "object-assign": "^4.1.1", "util": "0.10.3" }, "dependencies": { @@ -1361,29 +1387,21 @@ "dev": true }, "async-done": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.1.tgz", - "integrity": "sha512-R1BaUeJ4PMoLNJuk+0tLJgjmEqVsdN118+Z8O+alhnQDQgy0kmD5Mqi0DNEmMx2LM0Ed5yekKu+ZXYvIHceicg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", + "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", "dev": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.2", - "process-nextick-args": "^1.0.7", + "process-nextick-args": "^2.0.0", "stream-exhaust": "^1.0.1" - }, - "dependencies": { - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - } } }, "async-each": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.2.tgz", - "integrity": "sha512-6xrbvN0MOBKSJDdonmSSz2OwFSgxRaVtBDes26mj9KIGtDo+g9xosFRSC+i1gQh2oAN/tQ62AI/pGZGQjVOiRg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", "dev": true }, "async-limiter": { @@ -1715,15 +1733,15 @@ } }, "babel-loader": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.5.tgz", - "integrity": "sha512-NTnHnVRd2JnRqPC0vW+iOQWU5pchDbYXsG2E6DMXEpMfUcQKclF9gmf3G3ZMhzG7IG9ji4coL0cm+FxeWxDpnw==", + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz", + "integrity": "sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==", "dev": true, "requires": { "find-cache-dir": "^2.0.0", "loader-utils": "^1.0.2", "mkdirp": "^0.5.1", - "util.promisify": "^1.0.0" + "pify": "^4.0.1" } }, "babel-messages": { @@ -2568,9 +2586,9 @@ "dev": true }, "bail": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.3.tgz", - "integrity": "sha512-1X8CnjFVQ+a+KW36uBNMTU5s8+v5FzeqrP7hTG5aTb4aPreSbZJlhwPon9VKMuEVgV++JM+SQrALY3kr7eswdg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.4.tgz", + "integrity": "sha512-S8vuDB4w6YpRhICUDET3guPlQpaJl7od94tpZ0Fvnyp+MKW/HyDTcRDck+29C9g+d/qQHnddRH3+94kZdrW0Ww==", "dev": true }, "balanced-match": { @@ -2729,9 +2747,9 @@ "dev": true }, "bluebird": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz", - "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==", + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", "dev": true }, "bn.js": { @@ -2753,27 +2771,27 @@ } }, "body-parser": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", - "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", "dev": true, "requires": { - "bytes": "3.0.0", + "bytes": "3.1.0", "content-type": "~1.0.4", "debug": "2.6.9", "depd": "~1.1.2", - "http-errors": "~1.6.3", - "iconv-lite": "0.4.23", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", "on-finished": "~2.3.0", - "qs": "6.5.2", - "raw-body": "2.3.3", - "type-is": "~1.6.16" + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" }, "dependencies": { "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", "dev": true }, "debug": { @@ -2785,13 +2803,17 @@ "ms": "2.0.0" } }, - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" } }, "ms": { @@ -2800,17 +2822,29 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + }, "raw-body": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", - "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", "dev": true, "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.3", - "iconv-lite": "0.4.23", + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", "unpipe": "1.0.0" } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true } } }, @@ -2954,14 +2988,14 @@ } }, "browserslist": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.5.4.tgz", - "integrity": "sha512-rAjx494LMjqKnMPhFkuLmLp8JWEX0o8ADTGeAbOqaF+XCvYLreZrG5uVjnPBlAQ8REZK4pzXGvp0bWgrFtKaag==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.3.tgz", + "integrity": "sha512-CNBqTCq22RKM8wKJNowcqihHJ4SkI8CGeK7KOR9tPboXUuS5Zk5lQgzzTbs4oxD8x+6HUshZUa2OyNI9lR93bQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30000955", - "electron-to-chromium": "^1.3.122", - "node-releases": "^1.1.13" + "caniuse-lite": "^1.0.30000975", + "electron-to-chromium": "^1.3.164", + "node-releases": "^1.1.23" } }, "browserstack": { @@ -2974,9 +3008,9 @@ } }, "browserstack-local": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.3.7.tgz", - "integrity": "sha512-ilZlmiy7XYJxsztYan7XueHVr3Ix9EVh/mCiYN1G53wRPEW/hg1KMsseM6UExzVbexEqFEfwjkBLeFlSqxh+bQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.4.0.tgz", + "integrity": "sha512-BUJWxIsJkJxqfTPJIvGWTsf+IYSqSFUeFNW9tnuyTG7va/0LkXLhIi/ErFGDle1urQkol48HlQUXj4QrliXFpg==", "dev": true, "requires": { "https-proxy-agent": "^2.2.1", @@ -3001,14 +3035,13 @@ } }, "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", "dev": true, "requires": { "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "ieee754": "^1.1.4" } }, "buffer-alloc": { @@ -3132,7 +3165,7 @@ }, "query-string": { "version": "5.1.1", - "resolved": "http://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", "dev": true, "requires": { @@ -3174,9 +3207,9 @@ "dev": true }, "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "camelcase-keys": { @@ -3198,9 +3231,9 @@ } }, "caniuse-lite": { - "version": "1.0.30000957", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000957.tgz", - "integrity": "sha512-8wxNrjAzyiHcLXN/iunskqQnJquQQ6VX8JHfW5kLgAPRSiSuKZiNfmIkP5j7jgyXqAQBSoXyJxfnbCFS0ThSiQ==", + "version": "1.0.30000975", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000975.tgz", + "integrity": "sha512-ZsXA9YWQX6ATu5MNg+Vx/cMQ+hM6vBBSqDeJs8ruk9z0ky4yIHML15MoxcFt088ST2uyjgqyUGRJButkptWf0w==", "dev": true }, "caseless": { @@ -3210,9 +3243,9 @@ "dev": true }, "ccount": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.3.tgz", - "integrity": "sha512-Jt9tIBkRc9POUof7QA/VwWd+58fKkEEfI+/t1/eOlxKM7ZhrczNzMFefge7Ai+39y1pR/pP6cI19guHy3FSLmw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.4.tgz", + "integrity": "sha512-fpZ81yYfzentuieinmGnphk0pLkOTMm6MZdVqwd77ROvhko6iujLNGrHH5E7utq3ygWklwfmwuG+A7P+NpqT6w==", "dev": true }, "center-align": { @@ -3251,27 +3284,27 @@ } }, "character-entities": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.2.tgz", - "integrity": "sha512-sMoHX6/nBiy3KKfC78dnEalnpn0Az0oSNvqUWYTtYrhRI5iUIYsROU48G+E+kMFQzqXaJ8kHJZ85n7y6/PHgwQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.3.tgz", + "integrity": "sha512-yB4oYSAa9yLcGyTbB4ItFwHw43QHdH129IJ5R+WvxOkWlyFnR5FAaBNnUq4mcxsTVZGh28bHoeTHMKXH1wZf3w==", "dev": true }, "character-entities-html4": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.2.tgz", - "integrity": "sha512-sIrXwyna2+5b0eB9W149izTPJk/KkJTg6mEzDGibwBUkyH1SbDa+nf515Ppdi3MaH35lW0JFJDWeq9Luzes1Iw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.3.tgz", + "integrity": "sha512-SwnyZ7jQBCRHELk9zf2CN5AnGEc2nA+uKMZLHvcqhpPprjkYhiLn0DywMHgN5ttFZuITMATbh68M6VIVKwJbcg==", "dev": true }, "character-entities-legacy": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.2.tgz", - "integrity": "sha512-9NB2VbXtXYWdXzqrvAHykE/f0QJxzaKIpZ5QzNZrrgQ7Iyxr2vnfS8fCBNVW9nUEZE0lo57nxKRqnzY/dKrwlA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.3.tgz", + "integrity": "sha512-YAxUpPoPwxYFsslbdKkhrGnXAtXoHNgYjlBM3WMXkWGTl5RsY3QmOyhwAgL8Nxm9l5LBThXGawxKPn68y6/fww==", "dev": true }, "character-reference-invalid": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.2.tgz", - "integrity": "sha512-7I/xceXfKyUJmSAn/jw8ve/9DyOP7XxufNYLI9Px7CmsKgEUaZLUTax6nZxGQtaoiZCjpu6cHPj20xC/vqRReQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.3.tgz", + "integrity": "sha512-VOq6PRzQBam/8Jm6XBGk2fNEnHXAdGd6go0rtd4weAGECBamHDwwCQSOT12TACIYUZegUXnV6xBXqUssijtxIg==", "dev": true }, "chardet": { @@ -3293,9 +3326,9 @@ "dev": true }, "chokidar": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.5.tgz", - "integrity": "sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz", + "integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==", "dev": true, "requires": { "anymatch": "^2.0.0", @@ -3367,14 +3400,31 @@ "dev": true }, "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } } }, "clone": { @@ -3405,9 +3455,9 @@ "dev": true }, "cloneable-readable": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.2.tgz", - "integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", "dev": true, "requires": { "inherits": "^2.0.1", @@ -3428,9 +3478,9 @@ "dev": true }, "collapse-white-space": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.4.tgz", - "integrity": "sha512-YfQ1tAUZm561vpYD+5eyWN8+UsceQbSrqqlc/6zDY2gtAE+uZLSdkkovhnGpmCThsvKBFakq4EdY/FF93E8XIw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.5.tgz", + "integrity": "sha512-703bOOmytCYAX9cXYqoikYIx6twmFCXsnzRQheBcTG3nzKYBR4P/+wkYeH+Mvj7qUz8zZDtdyzbxfnEi/kYzRQ==", "dev": true }, "collection-map": { @@ -3491,27 +3541,24 @@ } }, "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "requires": { "delayed-stream": "~1.0.0" } }, "comma-separated-tokens": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.5.tgz", - "integrity": "sha512-Cg90/fcK93n0ecgYTAz1jaA3zvnQ0ExlmKY1rdbyHqAx6BHxwoJc+J7HDu0iuQ7ixEs1qaa+WyQ6oeuBpYP1iA==", - "dev": true, - "requires": { - "trim": "0.0.1" - } + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.7.tgz", + "integrity": "sha512-Jrx3xsP4pPv4AwJUDWY9wOXGtwPXARej6Xd99h4TUGotmf8APuquKMpK+dnD3UgyxK7OEWaisjZz+3b5jtL6xQ==", + "dev": true }, "commander": { - "version": "2.15.1", - "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", "dev": true }, "commondir": { @@ -3527,9 +3574,9 @@ "dev": true }, "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, "component-inherit": { @@ -3597,14 +3644,14 @@ } }, "connect": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", - "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", "dev": true, "requires": { "debug": "2.6.9", - "finalhandler": "1.1.0", - "parseurl": "~1.3.2", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", "utils-merge": "1.0.1" }, "dependencies": { @@ -3711,40 +3758,33 @@ } }, "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" }, "core-js-compat": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.0.1.tgz", - "integrity": "sha512-2pC3e+Ht/1/gD7Sim/sqzvRplMiRnFQVlPpDVaHtY9l7zZP7knamr3VRD6NyGfHd84MrDC0tAM9ulNxYMW0T3g==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.1.4.tgz", + "integrity": "sha512-Z5zbO9f1d0YrJdoaQhphVAnKPimX92D6z8lCGphH89MNRxlL1prI9ExJPqVwP0/kgkQCv8c4GJGT8X16yUncOg==", "dev": true, "requires": { - "browserslist": "^4.5.4", - "core-js": "3.0.1", - "core-js-pure": "3.0.1", - "semver": "^6.0.0" + "browserslist": "^4.6.2", + "core-js-pure": "3.1.4", + "semver": "^6.1.1" }, "dependencies": { - "core-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.0.1.tgz", - "integrity": "sha512-sco40rF+2KlE0ROMvydjkrVMMG1vYilP2ALoRXcYR4obqbYIuV3Bg+51GEDW+HF8n7NRA+iaA4qD0nD9lo9mew==", - "dev": true - }, "semver": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz", - "integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", + "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==", "dev": true } } }, "core-js-pure": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.0.1.tgz", - "integrity": "sha512-mSxeQ6IghKW3MoyF4cz19GJ1cMm7761ON+WObSyLfTu/Jn3x7w4NwNFnrZxgl4MTSvYYepVLNuRtlB4loMwJ5g==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.1.4.tgz", + "integrity": "sha512-uJ4Z7iPNwiu1foygbcZYJsJs1jiXrTTCvxfLDXNhI/I+NHbSIEyr548y4fcsCEyWY0XgfAG/qqaunJ1SThHenA==", "dev": true }, "core-util-is": { @@ -3754,9 +3794,9 @@ "dev": true }, "coveralls": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.0.3.tgz", - "integrity": "sha512-viNfeGlda2zJr8Gj1zqXpDMRjw9uM54p7wzZdvLRyOgnAfCe974Dq4veZkjJdxQXbmdppu6flEajFYseHYaUhg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.0.4.tgz", + "integrity": "sha512-eyqUWA/7RT0JagiL0tThVhjbIjoiEUyWCjtUJoOPcWoeofP5WK/jb2OJYoBFrR6DvplR+AxOyuBqk4JHkk5ykA==", "dev": true, "requires": { "growl": "~> 1.10.0", @@ -3774,18 +3814,6 @@ "dev": true, "requires": { "buffer": "^5.1.0" - }, - "dependencies": { - "buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", - "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - } } }, "crc32-stream": { @@ -3836,12 +3864,14 @@ } }, "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "lru-cache": "^4.0.1", + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" } @@ -3921,12 +3951,13 @@ "dev": true }, "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", "dev": true, "requires": { - "es5-ext": "^0.10.9" + "es5-ext": "^0.10.50", + "type": "^1.0.1" } }, "dashdash": { @@ -4161,9 +4192,9 @@ "dev": true }, "detab": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detab/-/detab-2.0.1.tgz", - "integrity": "sha512-/hhdqdQc5thGrqzjyO/pz76lDZ5GSuAs6goxOaKTsvPk7HNnzAyFN5lyHgqpX4/s1i66K8qMGj+VhA9504x7DQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detab/-/detab-2.0.2.tgz", + "integrity": "sha512-Q57yPrxScy816TTE1P/uLRXLDKjXhvYTbfxS/e6lPD+YrqghbsMlGB9nQzj/zVtSPaF0DFPSdO916EWO4sQUyQ==", "dev": true, "requires": { "repeat-string": "^1.5.4" @@ -4324,12 +4355,62 @@ "yargs": "^9.0.1" }, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", "dev": true }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", @@ -4339,12 +4420,36 @@ "locate-path": "^2.0.0" } }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", "dev": true }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, "load-json-file": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", @@ -4375,6 +4480,32 @@ "path-exists": "^3.0.0" } }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -4442,24 +4573,17 @@ "path-type": "^2.0.0" } }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true }, "yargs": { "version": "9.0.1", @@ -4493,6 +4617,15 @@ } } } + }, + "yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } } } }, @@ -4580,15 +4713,15 @@ "dev": true }, "ejs": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", - "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==", + "version": "2.5.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.9.tgz", + "integrity": "sha512-GJCAeDBKfREgkBtgrYSf9hQy9kTb3helv0zGdzqhM7iAkW8FA/ZF97VQDbwFiwIT8MQLLOe5VlPZOEvZAqtUAQ==", "dev": true }, "electron-to-chromium": { - "version": "1.3.124", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.124.tgz", - "integrity": "sha512-glecGr/kFdfeXUHOHAWvGcXrxNU+1wSO/t5B23tT1dtlvYB26GY8aHzZSWD7HqhqC800Lr+w/hQul6C5AF542w==", + "version": "1.3.164", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.164.tgz", + "integrity": "sha512-VLlalqUeduN4+fayVtRZvGP2Hl1WrRxlwzh2XVVMJym3IFrQUS29BFQ1GP/BxOJXJI1OFCrJ5BnFEsAe8NHtOg==", "dev": true }, "elliptic": { @@ -4607,9 +4740,9 @@ } }, "emoji-regex": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", - "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, "emojis-list": { @@ -4636,7 +4769,7 @@ "engine.io": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", - "integrity": "sha1-tgKBw1SEpw7gNR6g6/+D7IyVIqI=", + "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", "dev": true, "requires": { "accepts": "~1.3.4", @@ -4650,7 +4783,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -4666,7 +4799,7 @@ }, "engine.io-client": { "version": "3.2.1", - "resolved": "http://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", "dev": true, "requires": { @@ -4683,10 +4816,16 @@ "yeast": "0.1.2" }, "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -4785,9 +4924,9 @@ } }, "es5-ext": { - "version": "0.10.49", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.49.tgz", - "integrity": "sha512-3NMEhi57E31qdzmYp2jwRArIUsj1HI/RxbQ4bgnSB+AIKIxsAmTiK83bYMifIcpWvEc3P1X30DhUKOqEtF/kvg==", + "version": "0.10.50", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.50.tgz", + "integrity": "sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==", "dev": true, "requires": { "es6-iterator": "~2.0.3", @@ -4827,9 +4966,9 @@ } }, "es6-promise": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz", - "integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", "dev": true }, "es6-promisify": { @@ -4865,14 +5004,14 @@ } }, "es6-weak-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", - "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", "dev": true, "requires": { "d": "1", - "es5-ext": "^0.10.14", - "es6-iterator": "^2.0.1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.1" } }, @@ -4989,6 +5128,17 @@ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", @@ -5052,9 +5202,9 @@ } }, "eslint-module-utils": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.3.0.tgz", - "integrity": "sha512-lmDJgeOOjk8hObTysjqH7wyMi+nsHwwvfBykwfhjR1LNdd7C2uFJBvx4OpWYpXOw4df1yE1cDEVd1yLHitk34w==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.0.tgz", + "integrity": "sha512-14tltLm38Eu3zS+mt0KvILC3q8jyIAH518MlG+HO0p+yK885Lb1UHTY/UgR91eOyGdmxAPb+OLoW4znqIT6Ndw==", "dev": true, "requires": { "debug": "^2.6.8", @@ -5131,21 +5281,22 @@ } }, "eslint-plugin-import": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.16.0.tgz", - "integrity": "sha512-z6oqWlf1x5GkHIFgrSvtmudnqM6Q60KM4KvpWi5ubonMjycLjndvd5+8VAZIsTlHC03djdgJuyKG6XO577px6A==", + "version": "2.17.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.17.3.tgz", + "integrity": "sha512-qeVf/UwXFJbeyLbxuY8RgqDyEKCkqV7YC+E5S5uOjAp4tOc8zj01JP3ucoBM8JcEqd1qRasJSg6LLlisirfy0Q==", "dev": true, "requires": { + "array-includes": "^3.0.3", "contains-path": "^0.1.0", "debug": "^2.6.9", "doctrine": "1.5.0", "eslint-import-resolver-node": "^0.3.2", - "eslint-module-utils": "^2.3.0", + "eslint-module-utils": "^2.4.0", "has": "^1.0.3", "lodash": "^4.17.11", "minimatch": "^3.0.4", "read-pkg-up": "^2.0.0", - "resolve": "^1.9.0" + "resolve": "^1.11.0" }, "dependencies": { "debug": { @@ -5377,7 +5528,7 @@ }, "event-stream": { "version": "3.3.4", - "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", "dev": true, "requires": { @@ -5399,9 +5550,9 @@ } }, "eventemitter3": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", "dev": true }, "events": { @@ -5435,19 +5586,6 @@ "strip-eof": "^1.0.0" }, "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -5456,16 +5594,6 @@ "requires": { "pump": "^3.0.0" } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } } } }, @@ -5624,40 +5752,6 @@ "vary": "~1.1.2" }, "dependencies": { - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "dev": true, - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "dev": true, - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - } - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "dev": true - }, "cookie": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", @@ -5673,21 +5767,6 @@ "ms": "2.0.0" } }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -5701,39 +5780,12 @@ "toidentifier": "1.0.0" } }, - "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", - "dev": true - }, - "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "dev": true, - "requires": { - "mime-db": "1.40.0" - } - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "dev": true - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true - }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -5746,24 +5798,6 @@ "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", "dev": true }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "dev": true, - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, "send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", @@ -5793,39 +5827,11 @@ } } }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, "setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", "dev": true - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } } } }, @@ -6057,17 +6063,17 @@ } }, "finalhandler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", - "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", "dev": true, "requires": { "debug": "2.6.9", - "encodeurl": "~1.0.1", + "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.3.1", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", "unpipe": "~1.0.0" }, "dependencies": { @@ -6121,9 +6127,9 @@ } }, "fined": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.1.tgz", - "integrity": "sha512-jQp949ZmEbiYHk3gkbdtpJ0G1+kgtLQBNdP5edFP7Fh+WAYceLQz6yO1SBj72Xkg8GVyTB3bBzAYrHJVh5Xd5g==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", "dev": true, "requires": { "expand-tilde": "^2.0.2", @@ -6139,6 +6145,23 @@ "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", "dev": true }, + "flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "dev": true, + "requires": { + "is-buffer": "~2.0.3" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", + "dev": true + } + } + }, "flat-cache": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", @@ -6165,7 +6188,7 @@ "flatted": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", - "integrity": "sha1-VRIrZTbqSWtLRIk+4mCBQdENmRY=", + "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", "dev": true }, "flush-write-stream": { @@ -6359,40 +6382,36 @@ "dev": true }, "fsevents": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", - "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", "dev": true, "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" }, "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "bundled": true, "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "bundled": true, "dev": true }, "aproba": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "bundled": true, "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6402,14 +6421,12 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "bundled": true, "dev": true }, "brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "bundled": true, "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -6418,71 +6435,61 @@ }, "chownr": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", - "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", + "bundled": true, "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "bundled": true, "dev": true }, "concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "bundled": true, "dev": true }, "console-control-strings": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "bundled": true, "dev": true }, "core-util-is": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "bundled": true, "dev": true, "optional": true }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "4.1.1", + "bundled": true, "dev": true, "optional": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "deep-extend": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "bundled": true, "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "bundled": true, "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "bundled": true, "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", - "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6491,15 +6498,13 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "bundled": true, "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6515,8 +6520,7 @@ }, "glob": { "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6530,15 +6534,13 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "bundled": true, "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6547,8 +6549,7 @@ }, "ignore-walk": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", - "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6557,8 +6558,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6568,21 +6568,18 @@ }, "inherits": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "bundled": true, "dev": true }, "ini": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "bundled": true, "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "bundled": true, "dev": true, "requires": { "number-is-nan": "^1.0.0" @@ -6590,15 +6587,13 @@ }, "isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "bundled": true, "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "bundled": true, "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -6606,14 +6601,12 @@ }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "bundled": true, "dev": true }, "minipass": { "version": "2.3.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", - "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "bundled": true, "dev": true, "requires": { "safe-buffer": "^5.1.2", @@ -6622,8 +6615,7 @@ }, "minizlib": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", - "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6632,36 +6624,32 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "bundled": true, "dev": true, "requires": { "minimist": "0.0.8" } }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.1", + "bundled": true, "dev": true, "optional": true }, "needle": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.4.tgz", - "integrity": "sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==", + "version": "2.3.0", + "bundled": true, "dev": true, "optional": true, "requires": { - "debug": "^2.1.2", + "debug": "^4.1.0", "iconv-lite": "^0.4.4", "sax": "^1.2.4" } }, "node-pre-gyp": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz", - "integrity": "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A==", + "version": "0.12.0", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6679,8 +6667,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6689,16 +6676,14 @@ } }, "npm-bundled": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz", - "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==", + "version": "1.0.6", + "bundled": true, "dev": true, "optional": true }, "npm-packlist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.2.0.tgz", - "integrity": "sha512-7Mni4Z8Xkx0/oegoqlcao/JpPCPEMtUvsmB0q7mgvlMinykJLSRTYuFqoQLYgGY8biuxIeiHO+QNJKbCfljewQ==", + "version": "1.4.1", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6708,8 +6693,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6721,21 +6705,18 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "bundled": true, "dev": true }, "object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "bundled": true, "dev": true, "optional": true }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "bundled": true, "dev": true, "requires": { "wrappy": "1" @@ -6743,22 +6724,19 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "bundled": true, "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "bundled": true, "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6768,22 +6746,19 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "bundled": true, "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "bundled": true, "dev": true, "optional": true }, "rc": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6795,8 +6770,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "bundled": true, "dev": true, "optional": true } @@ -6804,8 +6778,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6820,8 +6793,7 @@ }, "rimraf": { "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6830,49 +6802,42 @@ }, "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "bundled": true, "dev": true }, "safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "bundled": true, "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "bundled": true, "dev": true, "optional": true }, "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "version": "5.7.0", + "bundled": true, "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "bundled": true, "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "bundled": true, "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "bundled": true, "dev": true, "requires": { "code-point-at": "^1.0.0", @@ -6882,8 +6847,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6892,8 +6856,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "bundled": true, "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -6901,15 +6864,13 @@ }, "strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "bundled": true, "dev": true, "optional": true }, "tar": { "version": "4.4.8", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", - "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6924,15 +6885,13 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "bundled": true, "dev": true, "optional": true }, "wide-align": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -6941,22 +6900,20 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "bundled": true, "dev": true }, "yallist": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "bundled": true, "dev": true } } }, "fun-hooks": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/fun-hooks/-/fun-hooks-0.9.2.tgz", - "integrity": "sha512-Bbhqg3zj/joiHsmU9z/DBPofMN8yN4P7m2cE4sqZqaL+C6YcAXKjwa7Cu8rUs3roBiAhgWwQOAALZZodpmBglw==" + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/fun-hooks/-/fun-hooks-0.9.3.tgz", + "integrity": "sha512-MC/zsGf+duq8lI6xym+H8HuL6DE1fLyE90FRzU/j2lTDmjDJ//+KC7M8vLzG9y/mhkLOH5u9wK4QEf3lBqIo4w==" }, "function-bind": { "version": "1.1.1", @@ -6980,9 +6937,9 @@ } }, "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, "get-func-name": { @@ -7051,12 +7008,20 @@ "dev": true, "requires": { "emoji-regex": ">=6.0.0 <=6.1.1" + }, + "dependencies": { + "emoji-regex": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", + "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=", + "dev": true + } } }, "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -7181,9 +7146,9 @@ } }, "globals": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", - "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, "globals-docs": { @@ -7264,23 +7229,43 @@ "dev": true }, "gulp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.0.tgz", - "integrity": "sha1-lXZsYB2t5Kd+0+eyttwDiBtZY2Y=", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", + "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", "dev": true, "requires": { - "glob-watcher": "^5.0.0", - "gulp-cli": "^2.0.0", - "undertaker": "^1.0.0", + "glob-watcher": "^5.0.3", + "gulp-cli": "^2.2.0", + "undertaker": "^1.2.1", "vinyl-fs": "^3.0.0" }, "dependencies": { + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "requires": { + "ansi-wrap": "^0.1.0" + } + }, "camelcase": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", "dev": true }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, "find-up": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", @@ -7291,10 +7276,16 @@ "pinkie-promise": "^2.0.0" } }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, "gulp-cli": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.1.0.tgz", - "integrity": "sha512-txzgdFVlEPShBZus6JJyGyKJoBVDq6Do0ZQgIgx5RAsmhNVTDjymmOxpQvo3c20m66FldilS68ZXj2Q9w5dKbA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.2.0.tgz", + "integrity": "sha512-rGs3bVYHdyJpLqR0TUBnlcZ1O5O++Zs4bA0ajm+zr3WFCfiSLjGwoCBqFs18wzN+ZxahT9DkOK5nDf26iDsWjA==", "dev": true, "requires": { "ansi-colors": "^1.0.1", @@ -7317,6 +7308,30 @@ "yargs": "^7.1.0" } }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, "load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", @@ -7395,6 +7410,23 @@ "read-pkg": "^1.0.0" } }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, "strip-bom": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", @@ -7410,6 +7442,12 @@ "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", "dev": true }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, "yargs": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", @@ -7624,7 +7662,7 @@ "gulp-connect": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/gulp-connect/-/gulp-connect-5.7.0.tgz", - "integrity": "sha1-fpJfXkw06/7fnzGFdpZuj+iEDVo=", + "integrity": "sha512-8tRcC6wgXMLakpPw9M7GRJIhxkYdgZsXwn7n56BA2bQYGLR9NOPhMzx7js+qYDy6vhNkbApGKURjAw1FjY4pNA==", "dev": true, "requires": { "ansi-colors": "^2.0.5", @@ -7641,7 +7679,7 @@ "ansi-colors": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-2.0.5.tgz", - "integrity": "sha1-XaN4Jf7z51872kf3YNZL/RDhXhA=", + "integrity": "sha512-yAdfUZ+c2wetVNIFsNRn44THW+Lty6S5TwMpUfLA/UaGhiXbBv/F8E60/1hMLd0cnF/CDoWH8vzVaI5bAcHCjw==", "dev": true } } @@ -8062,9 +8100,9 @@ } }, "handlebars": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.1.tgz", - "integrity": "sha512-3Zhi6C0euYZL5sM0Zcy7lInLXKQ+YLcF/olbN010mzGQ4XVm50JeyBnMqofHh696GrciGruC7kCcApPDJvVgwA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", + "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", "dev": true, "requires": { "neo-async": "^2.6.0", @@ -8253,15 +8291,15 @@ } }, "hast-util-is-element": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.0.2.tgz", - "integrity": "sha512-4MEtyofNi3ZunPFrp9NpTQdNPN24xvLX3M+Lr/RGgPX6TLi+wR4/DqeoyQ7lwWcfUp4aevdt4RR0r7ZQPFbHxw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.0.3.tgz", + "integrity": "sha512-C62CVn7jbjp89yOhhy7vrkSaB7Vk906Gtcw/Ihd+Iufnq+2pwOZjdPmpzpKLWJXPJBMDX3wXg4FqmdOayPcewA==", "dev": true }, "hast-util-sanitize": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-1.3.0.tgz", - "integrity": "sha512-rQeetoD08jHmDOUYN6h9vTuE0hQN4wymhtkQZ6whHtcjaLpjw5RYAbcdxx9cMgMWERDsSs79UpqHuBLlUHKeOw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-1.3.1.tgz", + "integrity": "sha512-AIeKHuHx0Wk45nSkGVa2/ujQYTksnDl8gmmKo/mwQi7ag7IBZ8cM3nJ2G86SajbjGP/HRpud6kMkPtcM2i0Tlw==", "dev": true, "requires": { "xtend": "^4.0.1" @@ -8284,24 +8322,32 @@ "stringify-entities": "^1.0.1", "unist-util-is": "^2.0.0", "xtend": "^4.0.1" + }, + "dependencies": { + "unist-util-is": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.3.tgz", + "integrity": "sha512-4WbQX2iwfr/+PfM4U3zd2VNXY+dWtZsN1fLnWEi2QQXA4qyDYAZcDMfXUX0Cu6XZUHHAO9q4nyxxLT4Awk1qUA==", + "dev": true + } } }, "hast-util-whitespace": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-1.0.2.tgz", - "integrity": "sha512-4JT8B0HKPHBMFZdDQzexjxwhKx9TrpV/+uelvmqlPu8RqqDrnNIEHDtDZCmgE+4YmcFAtKVPLmnY3dQGRaN53A==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-1.0.3.tgz", + "integrity": "sha512-AlkYiLTTwPOyxZ8axq2/bCwRUPjIPBfrHkXuCR92B38b3lSdU22R5F/Z4DL6a2kxWpekWq1w6Nj48tWat6GeRA==", "dev": true }, "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, "highlight.js": { - "version": "9.15.6", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.6.tgz", - "integrity": "sha512-zozTAWM1D6sozHo8kqhfYgsac+B+q0PmsjXeyDrYIHHcBN0zTVT66+s2GW1GZv7DbyaROdLXKdabwS/WqPyIdQ==", + "version": "9.15.8", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.8.tgz", + "integrity": "sha512-RrapkKQWwE+wKdF73VsOa2RQdIoO3mxwJ4P8mhbI6KYJUraUHRKM5w5zQQKXNk0xNL4UVRdulV9SBJcmzJNzVA==", "dev": true }, "hmac-drbg": { @@ -8347,9 +8393,9 @@ "dev": true }, "html-void-elements": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.3.tgz", - "integrity": "sha512-SaGhCDPXJVNrQyKMtKy24q6IMdXg5FCPN3z+xizxw9l+oXQw5fOoaj/ERU5KqWhSYhXtW5bWthlDbTDLBhJQrA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.4.tgz", + "integrity": "sha512-yMk3naGPLrfvUV9TdDbuYXngh/TpHbA6TrOw3HL9kS8yhwx7i309BReNg7CbAJXGE+UMJ6je5OqJ7lC63o6YuQ==", "dev": true }, "http-cache-semantics": { @@ -8360,7 +8406,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -8368,20 +8414,12 @@ "inherits": "2.0.3", "setprototypeof": "1.1.0", "statuses": ">= 1.4.0 < 2" - }, - "dependencies": { - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true - } } }, "http-parser-js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz", - "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==", + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", + "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=", "dev": true }, "http-proxy": { @@ -8531,22 +8569,6 @@ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -8584,9 +8606,9 @@ } }, "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", "dev": true }, "ipaddr.js": { @@ -8626,9 +8648,9 @@ } }, "is-alphabetical": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.2.tgz", - "integrity": "sha512-V0xN4BYezDHcBSKb1QHUFMlR4as/XEuCZBzMJUU4n7+Cbt33SmUnSol+pnXFvLxSHNq2CemUXNdaXV6Flg7+xg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.3.tgz", + "integrity": "sha512-eEMa6MKpHFzw38eKm56iNNi6GJ7lf6aLLio7Kr23sJPAECscgRtZvOBYybejWDQ2bM949Y++61PY+udzj5QMLA==", "dev": true }, "is-alphanumeric": { @@ -8638,15 +8660,21 @@ "dev": true }, "is-alphanumerical": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.2.tgz", - "integrity": "sha512-pyfU/0kHdISIgslFfZN9nfY1Gk3MquQgUm1mJTjdkEPpkAKNWuBTSqFwewOpR7N351VkErCiyV71zX7mlQQqsg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.3.tgz", + "integrity": "sha512-A1IGAPO5AW9vSh7omxIlOGwIqEvpW/TA+DksVOPM5ODuxKlZS09+TEM1E3275lJqO2oJ38vDpeAL3DCIiHE6eA==", "dev": true, "requires": { "is-alphabetical": "^1.0.0", "is-decimal": "^1.0.0" } }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -8701,9 +8729,9 @@ "dev": true }, "is-decimal": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.2.tgz", - "integrity": "sha512-TRzl7mOCchnhchN+f3ICUCzYvL9ul7R+TYOsZ8xia++knyZAJfv/uA1FvQXsAnYIl1T3B2X5E/J7Wb1QXiIBXg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.3.tgz", + "integrity": "sha512-bvLSwoDg2q6Gf+E2LEPiklHZxxiSi3XAh4Mav65mKqTfCO1HM3uBs24TjEH8iJX3bbDdLXKJXBTmGzuTUuAEjQ==", "dev": true }, "is-descriptor": { @@ -8768,13 +8796,16 @@ } }, "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-generator-function": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.7.tgz", + "integrity": "sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==", + "dev": true }, "is-glob": { "version": "4.0.1", @@ -8786,9 +8817,9 @@ } }, "is-hexadecimal": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.2.tgz", - "integrity": "sha512-but/G3sapV3MNyqiDBLrOi4x8uCIw0RY3o/Vb5GT0sMFHrVV7731wFSVy41T5FO1og7G0gXLJh0MkgPRouko/A==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.3.tgz", + "integrity": "sha512-zxQ9//Q3D/34poZf8fiy3m3XVpbQc7ren15iKqrTtLPwkPD/t3Scy9Imp63FujULGxuK0ZlCwoo5xNpktFgbOA==", "dev": true }, "is-negated-glob": { @@ -8944,9 +8975,9 @@ "dev": true }, "is-whitespace-character": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.2.tgz", - "integrity": "sha512-SzM+T5GKUCtLhlHFKt2SDAX2RFzfS6joT91F2/WSi9LxgFdsnhfPK/UIA+JhRR2xuyLdrCys2PiFDrtn1fU5hQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.3.tgz", + "integrity": "sha512-SNPgMLz9JzPccD3nPctcj8sZlX9DAMJSKH8bP7Z6bohCwuNgX8xbWr1eTAYXX9Vpi/aSn8Y1akL9WgM3t43YNQ==", "dev": true }, "is-windows": { @@ -8956,9 +8987,9 @@ "dev": true }, "is-word-character": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.2.tgz", - "integrity": "sha512-T3FlsX8rCHAH8e7RE7PfOPZVFQlcV3XRF9eOOBQ1uf70OxO7CjjSOjeImMPCADBdYWcStAbVbYvJ1m2D3tb+EA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.3.tgz", + "integrity": "sha512-0wfcrFgOOOBdgRNT9H33xe6Zi6yhX/uoc4U8NBZGeQQB0ctU1dnlNTyL9JM2646bHDTpsDm1Brb3VPoCIMrd/A==", "dev": true }, "is-wsl": { @@ -9324,6 +9355,12 @@ "integrity": "sha1-6l7+QLg2kLmGZ2FKc5L8YOhCwN0=", "dev": true }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -9362,7 +9399,7 @@ "karma": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/karma/-/karma-3.1.4.tgz", - "integrity": "sha1-OJDKlyKxDR0UtybhM1kxRVeISZ4=", + "integrity": "sha512-31Vo8Qr5glN+dZEVIpnPCxEGleqE0EY6CtC2X9TagRV3rRQ3SNrvfhddICkJgUK3AgqpeKSZau03QumTGhGoSw==", "dev": true, "requires": { "bluebird": "^3.3.0", @@ -9396,9 +9433,9 @@ }, "dependencies": { "mime": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.2.tgz", - "integrity": "sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", "dev": true }, "rimraf": { @@ -9639,12 +9676,12 @@ } }, "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", "dev": true, "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "^2.0.0" } }, "lcov-parse": { @@ -9864,6 +9901,12 @@ "lodash._objecttypes": "~2.4.1" } }, + "lodash.clone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", + "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=", + "dev": true + }, "lodash.defaults": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", @@ -9987,7 +10030,7 @@ "log4js": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.6.tgz", - "integrity": "sha1-5srO2Uln7uuc45n5+GgqSysoyP8=", + "integrity": "sha512-ezXZk6oPJCWL483zj64pNkMuY/NcRX5MPiB0zE6tjZM137aeusrOnW1ecxgF9cmwMWkBMhjteQxBPoZBh9FDxQ==", "dev": true, "requires": { "circular-json": "^0.5.5", @@ -10000,7 +10043,7 @@ "circular-json": { "version": "0.5.9", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", - "integrity": "sha1-kydjroj0996teg0JyKUaR0OlOx0=", + "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==", "dev": true }, "debug": { @@ -10037,9 +10080,9 @@ "dev": true }, "longest-streak": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.2.tgz", - "integrity": "sha512-TmYTeEYxiAmSVdpbnQDXGtvYOIRsCMg89CVZzwzc2o7GFL1CjoiRPjH5ec0NFAVlAx3fVof9dX/t6KKRAo2OWA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.3.tgz", + "integrity": "sha512-9lz5IVdpwsKLMzQi0MQ+oD9EA0mIGcWYP7jXMTZVXP8D42PwuAk+M/HBFYQoxt1G5OR8m7aSIgb1UymfWGBWEw==", "dev": true }, "loose-envify": { @@ -10120,6 +10163,15 @@ "kind-of": "^6.0.2" } }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -10148,15 +10200,15 @@ } }, "markdown-escapes": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.2.tgz", - "integrity": "sha512-lbRZ2mE3Q9RtLjxZBZ9+IMl68DKIXaVAhwvwn9pmjnPLS0h/6kyBMgNhqi1xFJ/2yv6cSyv0jbiZavZv93JkkA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.3.tgz", + "integrity": "sha512-XUi5HJhhV5R74k8/0H2oCbCiYf/u4cO/rX8tnGkRvrqhsr5BRNU6Mg0yt/8UIx1iIS8220BNJsDb7XnILhLepw==", "dev": true }, "markdown-table": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.2.tgz", - "integrity": "sha512-NcWuJFHDA8V3wkDgR/j4+gZx+YQwstPgfQDV8ndUeWWzta3dnDTBxpVzqS9lkmJAuV5YX35lmyojl6HO5JXAgw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz", + "integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==", "dev": true }, "matchdep": { @@ -10212,18 +10264,18 @@ } }, "mdast-util-compact": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.2.tgz", - "integrity": "sha512-d2WS98JSDVbpSsBfVvD9TaDMlqPRz7ohM/11G0rp5jOBb5q96RJ6YLszQ/09AAixyzh23FeIpCGqfaamEADtWg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.3.tgz", + "integrity": "sha512-nRiU5GpNy62rZppDKbLwhhtw5DXoFMqw9UNZFmlPsNaQCZ//WLjGKUwWMdJrUH+Se7UvtO2gXtAMe0g/N+eI5w==", "dev": true, "requires": { "unist-util-visit": "^1.1.0" } }, "mdast-util-definitions": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-1.2.3.tgz", - "integrity": "sha512-P6wpRO8YVQ1iv30maMc93NLh7COvufglBE8/ldcOyYmk5EbfF0YeqlLgtqP/FOBU501Kqar1x5wYWwB3Nga74g==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-1.2.4.tgz", + "integrity": "sha512-HfUArPog1j4Z78Xlzy9Q4aHLnrF/7fb57cooTHypyGoe2XFNbcx/kWZDoOz+ra8CkUzvg3+VHV434yqEd1DRmA==", "dev": true, "requires": { "unist-util-visit": "^1.0.0" @@ -10258,9 +10310,9 @@ } }, "mdast-util-to-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.0.5.tgz", - "integrity": "sha512-2qLt/DEOo5F6nc2VFScQiHPzQ0XXcabquRJxKMhKte8nt42o08HUxNDPk7tt0YPxnWjAT11I1SYi0X0iPnfI5A==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.0.6.tgz", + "integrity": "sha512-868pp48gUPmZIhfKrLbaDneuzGiw3OTDjHc5M1kAepR2CWBJ+HpEsm252K4aXdiP5coVZaJPOqGtVU6Po8xnXg==", "dev": true }, "mdast-util-toc": { @@ -10275,6 +10327,12 @@ "unist-util-visit": "^1.1.0" }, "dependencies": { + "emoji-regex": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", + "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=", + "dev": true + }, "github-slugger": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.2.1.tgz", @@ -10283,6 +10341,12 @@ "requires": { "emoji-regex": ">=6.0.0 <=6.1.1" } + }, + "unist-util-is": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.3.tgz", + "integrity": "sha512-4WbQX2iwfr/+PfM4U3zd2VNXY+dWtZsN1fLnWEi2QQXA4qyDYAZcDMfXUX0Cu6XZUHHAO9q4nyxxLT4Awk1qUA==", + "dev": true } } }, @@ -10299,12 +10363,22 @@ "dev": true }, "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + }, + "dependencies": { + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true + } } }, "memoizee": { @@ -10333,6 +10407,12 @@ "readable-stream": "^2.0.1" } }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true + }, "meow": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", @@ -10500,24 +10580,24 @@ "dev": true }, "mime-db": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", - "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==", + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", "dev": true }, "mime-types": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", - "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", "dev": true, "requires": { - "mime-db": "~1.38.0" + "mime-db": "1.40.0" } }, "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, "mimic-response": { @@ -10549,7 +10629,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -10585,7 +10665,7 @@ "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true } @@ -10610,6 +10690,12 @@ "supports-color": "5.4.0" }, "dependencies": { + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -10639,6 +10725,12 @@ "path-is-absolute": "^1.0.0" } }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -10720,9 +10812,9 @@ } }, "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "multipipe": { @@ -10782,9 +10874,9 @@ "dev": true }, "nan": { - "version": "2.13.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", - "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", "dev": true, "optional": true }, @@ -10820,15 +10912,15 @@ "dev": true }, "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", "dev": true }, "neo-async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", - "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", "dev": true }, "next-tick": { @@ -10844,15 +10936,15 @@ "dev": true }, "nise": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.4.10.tgz", - "integrity": "sha512-sa0RRbj53dovjc7wombHmVli9ZihXbXCQ2uH3TNm03DyvOSIQbxg+pbqDKrk2oxMK1rtLGVlKxcB9rrc6X5YjA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.0.tgz", + "integrity": "sha512-Z3sfYEkLFzFmL8KY6xnSJLRxwQwYBjOXi/24lb62ZnZiGA0JUzGGTI6TBIgfCSMIDl9Jlu8SRmHNACLTemDHww==", "dev": true, "requires": { "@sinonjs/formatio": "^3.1.0", "@sinonjs/text-encoding": "^0.7.1", "just-extend": "^4.0.2", - "lolex": "^2.3.2", + "lolex": "^4.1.0", "path-to-regexp": "^1.7.0" }, "dependencies": { @@ -10867,17 +10959,27 @@ } }, "lolex": { - "version": "2.7.5", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz", - "integrity": "sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.1.0.tgz", + "integrity": "sha512-BYxIEXiVq5lGIXeVHnsFzqa1TxN5acnKnPCdlZSpzm8viNEOhiigupA4vTQ9HEFQ6nLTQ9wQOgBknJgzUYQ9Aw==", "dev": true } } }, + "node-environment-flags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", + "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", + "dev": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + } + }, "node-libs-browser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz", - "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", "dev": true, "requires": { "assert": "^1.1.1", @@ -10890,7 +10992,7 @@ "events": "^3.0.0", "https-browserify": "^1.0.0", "os-browserify": "^0.3.0", - "path-browserify": "0.0.0", + "path-browserify": "0.0.1", "process": "^0.11.10", "punycode": "^1.2.4", "querystring-es3": "^0.2.0", @@ -10902,21 +11004,41 @@ "tty-browserify": "0.0.0", "url": "^0.11.0", "util": "^0.11.0", - "vm-browserify": "0.0.4" + "vm-browserify": "^1.0.1" }, "dependencies": { + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", "dev": true + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + } } } }, "node-releases": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.14.tgz", - "integrity": "sha512-d58EpVZRhQE60kWiWUaaPlK9dyC4zg3ZoMcHcky2d4hDksyQj0rUozwInOl0C66mBsqo01Tuns8AvxnL5S7PKg==", + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.23.tgz", + "integrity": "sha512-uq1iL79YjfYC0WXoHbC/z28q/9pOl8kSHaXdWmAAc8No+bDwqkZbzIJz55g/MUsPgSGm9LZ7QSUbzTcH5tz47w==", "dev": true, "requires": { "semver": "^5.3.0" @@ -10976,6 +11098,23 @@ "integrity": "sha1-1+/jz816sAYUuJbqUxGdyaslkSU=", "dev": true }, + "npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + } + }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -11085,6 +11224,18 @@ "isobject": "^3.0.0" } }, + "object.entries": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", + "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, "object.getownpropertydescriptors": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", @@ -11170,6 +11321,14 @@ "dev": true, "requires": { "mimic-fn": "^1.0.0" + }, + "dependencies": { + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + } } }, "opener": { @@ -11199,7 +11358,7 @@ "dependencies": { "minimist": { "version": "0.0.10", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", "dev": true }, @@ -11247,31 +11406,14 @@ "dev": true }, "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", "dev": true, "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - }, - "dependencies": { - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - } + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" } }, "os-tmpdir": { @@ -11286,6 +11428,12 @@ "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==", "dev": true }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -11361,21 +11509,144 @@ } }, "parse-domain": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/parse-domain/-/parse-domain-2.1.7.tgz", - "integrity": "sha512-yb0VWRwDCe96ML49b3xg+4wScbocpIrFSAdkml8eKq/deH3FiFPBpsC6RTC9ZUtnDhInmXPfNIHsN/v62+TAMA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/parse-domain/-/parse-domain-2.3.1.tgz", + "integrity": "sha512-k/tkc7tfcoGfaUOCG5DuPNX+dt6UBqRWU9EtR0rA9esi5GpOY0OGEgprfylmYx8pykQbdBTYHLaM/UwFHXuZKA==", "dev": true, "requires": { "chai": "^4.2.0", "got": "^8.3.2", "mkdirp": "^0.5.1", - "mocha": "^5.2.0" + "mocha": "^6.1.4", + "npm-run-all": "^4.1.5" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "mocha": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.1.4.tgz", + "integrity": "sha512-PN8CIy4RXsIoxoFJzS4QNnCH4psUCPWc4/rPrst/ecSJJbLBkubMiyGCP2Kj/9YnWbotFqAoeXyXMucj7gwCFg==", + "dev": true, + "requires": { + "ansi-colors": "3.2.3", + "browser-stdout": "1.3.1", + "debug": "3.2.6", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "find-up": "3.0.0", + "glob": "7.1.3", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "2.2.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "ms": "2.1.1", + "node-environment-flags": "1.0.5", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.2.2", + "yargs-parser": "13.0.0", + "yargs-unparser": "1.5.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "yargs": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", + "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.0.0" + } + } } }, "parse-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.1.tgz", - "integrity": "sha512-NBWYLQm1KSoDKk7GAHyioLTvCZ5QjdH/ASBBQTD3iLiAWJXS5bg1jEWI8nIJ+vgVvsceBVBcDGRWSo0KVQBvvg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.2.tgz", + "integrity": "sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg==", "dev": true, "requires": { "character-entities": "^1.0.0", @@ -11498,9 +11769,9 @@ } }, "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true }, "pascalcase": { @@ -11510,9 +11781,9 @@ "dev": true }, "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", "dev": true }, "path-dirname": { @@ -11646,6 +11917,12 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "pidtree": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.0.tgz", + "integrity": "sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==", + "dev": true + }, "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -11686,6 +11963,17 @@ "arr-diff": "^4.0.0", "arr-union": "^3.1.0", "extend-shallow": "^3.0.2" + }, + "dependencies": { + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "requires": { + "ansi-wrap": "^0.1.0" + } + } } }, "pluralize": { @@ -11792,9 +12080,9 @@ "dev": true }, "psl": { - "version": "1.1.31", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", + "version": "1.1.32", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.32.tgz", + "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g==", "dev": true }, "public-encrypt": { @@ -11812,9 +12100,9 @@ } }, "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -11830,6 +12118,18 @@ "duplexify": "^3.6.0", "inherits": "^2.0.3", "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } } }, "punycode": { @@ -11923,9 +12223,9 @@ } }, "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true }, "raw-body": { @@ -12064,9 +12364,9 @@ "dev": true }, "regenerate-unicode-properties": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.0.2.tgz", - "integrity": "sha512-SbA/iNrBUf6Pv2zU8Ekv1Qbhv92yxL4hiDa2siuxs4KKn4oOoMDHXjAf7+Nz9qinUQ46B1LcWEi/PhJfPWpZWQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", + "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", "dev": true, "requires": { "regenerate": "^1.4.0" @@ -12078,9 +12378,9 @@ "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" }, "regenerator-transform": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.13.4.tgz", - "integrity": "sha512-T0QMBjK3J0MtxjPmdIMXm72Wvj2Abb0Bd4HADdfijwMdoIsyQZ6fWC7kDFhk2YinBBEMZDL7Y7wh0J1sGx3S4A==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.0.tgz", + "integrity": "sha512-rtOelq4Cawlbmq9xuMR5gdFmv7ku/sFoB7sRiywx7aq53bc52b4j6zvH7Te1Vt/X2YveDKnCGUbioieU7FEL3w==", "dev": true, "requires": { "private": "^0.1.6" @@ -12106,9 +12406,9 @@ } }, "regexp-tree": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.5.tgz", - "integrity": "sha512-nUmxvfJyAODw+0B13hj8CFVAxhe7fDEAgJgaotBu3nnR+IgGgZq59YedJP5VYTlkEfqjuK6TuRpnymKdatLZfQ==", + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.10.tgz", + "integrity": "sha512-K1qVSbcedffwuIslMwpe6vGlj+ZXRnGkvjAtFHfDZZZuEdA/h0dxljAPu9vhUo6Rrx2U2AwJ+nSQ6hK+lrP5MQ==", "dev": true }, "regexpp": { @@ -12201,18 +12501,18 @@ } }, "remark-reference-links": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/remark-reference-links/-/remark-reference-links-4.0.3.tgz", - "integrity": "sha512-Q9d7JaK5r0JDBo3TInfrodBuI3xulI8htCr8jlX+0oXosF3GaebJbo5y228VYFoV6xJ+syDukkUGMKNlwSJWjQ==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/remark-reference-links/-/remark-reference-links-4.0.4.tgz", + "integrity": "sha512-+2X8hwSQqxG4tvjYZNrTcEC+bXp8shQvwRGG6J/rnFTvBoU4G0BBviZoqKGZizLh/DG+0gSYhiDDWCqyxXW1iQ==", "dev": true, "requires": { "unist-util-visit": "^1.0.0" } }, "remark-slug": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/remark-slug/-/remark-slug-5.1.1.tgz", - "integrity": "sha512-r591rdoDPJkSSAVvEaTVUkqbMp7c7AyZfif14V0Dp66GQkOHzaPAS6wyhawSbqpS0ZdTnfJS+TltFoxzi6bdIA==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/remark-slug/-/remark-slug-5.1.2.tgz", + "integrity": "sha512-DWX+Kd9iKycqyD+/B+gEFO3jjnt7Yg1O05lygYSNTe5i5PIxxxPjp5qPBDxPIzp5wreF7+1ROCwRgjEcqmzr3A==", "dev": true, "requires": { "github-slugger": "^1.0.0", @@ -12393,9 +12693,9 @@ "dev": true }, "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, "require-uncached": { @@ -12415,9 +12715,9 @@ "dev": true }, "resolve": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", - "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", + "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -12480,9 +12780,9 @@ "dev": true }, "rfdc": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.2.tgz", - "integrity": "sha1-5uctdPXcOd6PU49l4Aw2wYAY40k=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz", + "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==", "dev": true }, "rgb2hex": { @@ -12680,16 +12980,81 @@ } }, "serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", "dev": true, "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.2" - } + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + } + } }, "set-blocking": { "version": "2.0.0", @@ -12757,6 +13122,18 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "shell-quote": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", + "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", + "dev": true, + "requires": { + "array-filter": "~0.0.0", + "array-map": "~0.0.0", + "array-reduce": "~0.0.0", + "jsonify": "~0.0.0" + } + }, "shelljs": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz", @@ -12816,14 +13193,6 @@ "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - } } }, "snapdragon": { @@ -12951,7 +13320,7 @@ "socket.io": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", - "integrity": "sha1-oGnF/qvuPmshSnW0DOBlLhz7mYA=", + "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", "dev": true, "requires": { "debug": "~3.1.0", @@ -12965,7 +13334,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -12988,7 +13357,7 @@ "socket.io-client": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", - "integrity": "sha1-3LOBA0NqtFeN2wJmOK4vIbYjZx8=", + "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", "dev": true, "requires": { "backo2": "1.0.2", @@ -13007,10 +13376,16 @@ "to-array": "0.1.4" }, "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -13026,7 +13401,7 @@ }, "socket.io-parser": { "version": "3.2.0", - "resolved": "http://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", "dev": true, "requires": { @@ -13035,10 +13410,16 @@ "isarray": "2.0.1" }, "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -13108,13 +13489,10 @@ "dev": true }, "space-separated-tokens": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.2.tgz", - "integrity": "sha512-G3jprCEw+xFEs0ORweLmblJ3XLymGGr6hxZYTYZjIlvDti9vOBUjRQa1Rzjt012aRrocKstHwdNi+F7HguPsEA==", - "dev": true, - "requires": { - "trim": "0.0.1" - } + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.4.tgz", + "integrity": "sha512-UyhMSmeIqZrQn2UdjYpxEkwY9JUrn8pP+7L4f91zRzOQuI8MF1FGLfYU9DKCYeLdo7LXMxwrX5zKFy7eeeVHuA==", + "dev": true }, "sparkles": { "version": "1.0.1", @@ -13156,7 +13534,7 @@ }, "split": { "version": "0.3.3", - "resolved": "http://registry.npmjs.org/split/-/split-0.3.3.tgz", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", "dev": true, "requires": { @@ -13202,9 +13580,9 @@ "dev": true }, "state-toggle": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.1.tgz", - "integrity": "sha512-Qe8QntFrrpWTnHwvwj2FZTgv+PKIsp0B9VxLzLLbSpPXWOgRgc5LVj/aTiSfK1RqIeF9jeC1UeOH8Q8y60A7og==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.2.tgz", + "integrity": "sha512-8LpelPGR0qQM4PnfLiplOQNJcIN1/r2Gy0xKB2zKnIW2YzPMt2sR4I/+gtPjhN7Svh9kw+zqEg2SFwpBO9iNiw==", "dev": true }, "static-extend": { @@ -13229,9 +13607,9 @@ } }, "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", "dev": true }, "stealthy-require": { @@ -13290,7 +13668,7 @@ }, "stream-combiner": { "version": "0.0.4", - "resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", "dev": true, "requires": { @@ -13368,14 +13746,41 @@ "dev": true }, "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string.prototype.padend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz", + "integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "define-properties": "^1.1.2", + "es-abstract": "^1.4.3", + "function-bind": "^1.0.2" } }, "string_decoder": { @@ -13481,39 +13886,6 @@ "lodash": "^4.17.4", "slice-ansi": "1.0.0", "string-width": "^2.1.1" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } } }, "tapable": { @@ -13788,9 +14160,9 @@ "dev": true }, "trim-lines": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-1.1.1.tgz", - "integrity": "sha512-X+eloHbgJGxczUk1WSjIvn7aC9oN3jVE3rQfRVKcgpavi3jxtCn0VVKtjOBj64Yop96UYn/ujJRpTbCdAF1vyg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-1.1.2.tgz", + "integrity": "sha512-3GOuyNeTqk3FAqc3jOJtw7FTjYl94XBR5aD9QnDbK/T4CA9sW/J0l9RoaRPE9wyPP7NF331qnHnvJFBJ+IDkmQ==", "dev": true }, "trim-newlines": { @@ -13806,15 +14178,15 @@ "dev": true }, "trim-trailing-lines": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.1.tgz", - "integrity": "sha512-bWLv9BbWbbd7mlqqs2oQYnLD/U/ZqeJeJwbO0FG2zA1aTq+HTvxfHNKFa/HGCVyJpDiioUYaBhfiT6rgk+l4mg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.2.tgz", + "integrity": "sha512-MUjYItdrqqj2zpcHFTkMa9WAv4JHTI6gnRQGPFLrt5L9a6tRMiDnIqYl8JBvu2d2Tc3lWJKQwlGCp0K8AvCM+Q==", "dev": true }, "trough": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.3.tgz", - "integrity": "sha512-fwkLWH+DimvA4YCy+/nvJd61nWQQ2liO/nF/RjkTpiOGi+zxZzVkhb1mvbHIIW4b/8nDsYI8uTmAlc0nNkRMOw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.4.tgz", + "integrity": "sha512-tdzBRDGWcI1OpPVmChbdSKhvSVurznZ8X36AYURAcl+0o2ldlCY2XPzyXNNxwJwwyIU+rIglTCG4kxtNKBQH7Q==", "dev": true }, "tryer": { @@ -13844,6 +14216,12 @@ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "dev": true }, + "type": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/type/-/type-1.0.1.tgz", + "integrity": "sha512-MAM5dBMJCJNKs9E7JXo4CXRAansRfG0nlJxW7Wf6GZzSOvH31zClSaHdIMWLehe/EGMBkqeC55rrkaOr5Oo7Nw==", + "dev": true + }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -13860,13 +14238,13 @@ "dev": true }, "type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dev": true, "requires": { "media-typer": "0.3.0", - "mime-types": "~2.1.18" + "mime-types": "~2.1.24" } }, "typedarray": { @@ -13876,21 +14254,15 @@ "dev": true }, "uglify-js": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.4.tgz", - "integrity": "sha512-GpKo28q/7Bm5BcX9vOu4S46FwisbPbAmkkqPnGIpKvKTM96I85N6XHQV+k4I6FA2wxgLhcsSyHoNhzucwCflvA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", + "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", "dev": true, "requires": { "commander": "~2.20.0", "source-map": "~0.6.1" }, "dependencies": { - "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -13944,12 +14316,6 @@ "yargs": "~3.10.0" } }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true - }, "wordwrap": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", @@ -14006,9 +14372,9 @@ "dev": true }, "unherit": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.1.tgz", - "integrity": "sha512-+XZuV691Cn4zHsK0vkKYwBEwB74T3IZIcxrgn2E4rKwTfFyI1zCh7X7grwh9Re08fdPlarIdyWgI8aVB3F5A5g==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.2.tgz", + "integrity": "sha512-W3tMnpaMG7ZY6xe/moK04U9fBhi6wEiCYHUW5Mop/wQHf12+79EQGwxYejNdhEz2mkqkBlGwm7pxmgBKMVUj0w==", "dev": true, "requires": { "inherits": "^2.0.1", @@ -14103,36 +14469,36 @@ } }, "unist-builder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-1.0.3.tgz", - "integrity": "sha512-/KB8GEaoeHRyIqClL+Kam+Y5NWJ6yEiPsAfv1M+O1p+aKGgjR89WwoEHKTyOj17L6kAlqtKpAgv2nWvdbQDEig==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-1.0.4.tgz", + "integrity": "sha512-v6xbUPP7ILrT15fHGrNyHc1Xda8H3xVhP7/HAIotHOhVPjH5dCXA097C3Rry1Q2O+HbOLCao4hfPB+EYEjHgVg==", "dev": true, "requires": { "object-assign": "^4.1.0" } }, "unist-util-generated": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.3.tgz", - "integrity": "sha512-qlPeDqnQnd84KIqwphzOR+l02cxjDzvEYEBl84EjmKRrX4eUmjyAo8xJv1SCDhJqNjyHRnBMZWNKAiBtXE6hBg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.4.tgz", + "integrity": "sha512-SA7Sys3h3X4AlVnxHdvN/qYdr4R38HzihoEVY2Q2BZu8NHWDnw5OGcC/tXWjQfd4iG+M6qRFNIRGqJmp2ez4Ww==", "dev": true }, "unist-util-is": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.2.tgz", - "integrity": "sha512-YkXBK/H9raAmG7KXck+UUpnKiNmUdB+aBGrknfQ4EreE1banuzrKABx3jP6Z5Z3fMSPMQQmeXBlKpCbMwBkxVw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", + "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", "dev": true }, "unist-util-position": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.0.2.tgz", - "integrity": "sha512-npmFu92l/+b1Ao6uGP4I1WFz9hsKv7qleZ4aliw6x0RVu6A9A3tAf57NMpFfzQ02jxRtJZuRn+C8xWT7GWnH0g==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.0.3.tgz", + "integrity": "sha512-28EpCBYFvnMeq9y/4w6pbnFmCUfzlsc41NJui5c51hOFjBA1fejcwc+5W4z2+0ECVbScG3dURS3JTVqwenzqZw==", "dev": true }, "unist-util-remove-position": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.2.tgz", - "integrity": "sha512-XxoNOBvq1WXRKXxgnSYbtCF76TJrRoe5++pD4cCBsssSiWSnPEktyFrFLE8LTk3JW5mt9hB0Sk5zn4x/JeWY7Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.3.tgz", + "integrity": "sha512-CtszTlOjP2sBGYc2zcKA/CvNdTdEs3ozbiJ63IPBxh8iZg42SCCb8m04f8z2+V1aSk5a7BxbZKEdoDjadmBkWA==", "dev": true, "requires": { "unist-util-visit": "^1.1.0" @@ -14145,21 +14511,21 @@ "dev": true }, "unist-util-visit": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.0.tgz", - "integrity": "sha512-FiGu34ziNsZA3ZUteZxSFaczIjGmksfSgdKqBfOejrrfzyUy5b7YrlzT1Bcvi+djkYDituJDy2XB7tGTeBieKw==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", + "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", "dev": true, "requires": { "unist-util-visit-parents": "^2.0.0" } }, "unist-util-visit-parents": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.0.1.tgz", - "integrity": "sha512-6B0UTiMfdWql4cQ03gDTCSns+64Zkfo2OCbK31Ov0uMizEz+CJeAp0cgZVb5Fhmcd7Bct2iRNywejT0orpbqUA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", + "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", "dev": true, "requires": { - "unist-util-is": "^2.1.2" + "unist-util-is": "^3.0.0" } }, "unpipe": { @@ -14254,12 +14620,12 @@ "dev": true }, "url-parse": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz", - "integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", "dev": true, "requires": { - "querystringify": "^2.0.0", + "querystringify": "^2.1.1", "requires-port": "^1.0.0" }, "dependencies": { @@ -14303,7 +14669,7 @@ "useragent": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", - "integrity": "sha1-IX+UOtVAyyEoZYqyP8lg9qiMmXI=", + "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", "dev": true, "requires": { "lru-cache": "4.1.x", @@ -14311,12 +14677,16 @@ } }, "util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.0.tgz", + "integrity": "sha512-pPSOFl7VLhZ7LO/SFABPraZEEurkJUWSMn3MuA/r3WQZc+Z1fqou2JqLSOZbCLl73EUIxuUVX8X4jkX2vfJeAA==", "dev": true, "requires": { - "inherits": "2.0.3" + "inherits": "2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "object.entries": "^1.1.0", + "safe-buffer": "^5.1.2" } }, "util-deprecate": { @@ -14325,16 +14695,6 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" - } - }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -14348,9 +14708,9 @@ "dev": true }, "v8flags": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.2.tgz", - "integrity": "sha512-MtivA7GF24yMPte9Rp/BWGCYQNaUj86zeYxV/x2RRJMKagImbbv3u8iJC57lNhWLPcGLJmHcHmFWkNsplbbLWw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz", + "integrity": "sha512-amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w==", "dev": true, "requires": { "homedir-polyfill": "^1.0.1" @@ -14402,9 +14762,9 @@ } }, "vfile-location": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.4.tgz", - "integrity": "sha512-KRL5uXQPoUKu+NGvQVL4XLORw45W62v4U4gxJ3vRlDfI9QsT4ZN1PNXn/zQpKUulqGDpYuT0XDfp5q9O87/y/w==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.5.tgz", + "integrity": "sha512-Pa1ey0OzYBkLPxPZI3d9E+S4BmvfVwNAAXrrqGbwTVXWaX2p9kM1zZ+n35UtVM06shmWKH4RPRN8KI80qE3wNQ==", "dev": true }, "vfile-message": { @@ -14435,6 +14795,26 @@ "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", "dev": true }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, "supports-color": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", @@ -14447,15 +14827,15 @@ } }, "vfile-sort": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/vfile-sort/-/vfile-sort-2.2.0.tgz", - "integrity": "sha512-RgxLXVWrJBWb2GuP8FsSkqK7HmbjXjnI8qx3nD6NTWhsWaelaKvJuxfh1F1d1lkCPD7imo4zzi8cf6IOMgaTnQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vfile-sort/-/vfile-sort-2.2.1.tgz", + "integrity": "sha512-5dt7xEhC44h0uRQKhbM2JAe0z/naHphIZlMOygtMBM9Nn0pZdaX5fshhwWit9wvsuP8t/wp43nTDRRErO1WK8g==", "dev": true }, "vfile-statistics": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vfile-statistics/-/vfile-statistics-1.1.2.tgz", - "integrity": "sha512-16wAC9eEGXdsD35LX9m/iXCRIZyX5LIrDgDtAF92rbATSqsBRbC4n05e0Rj5vt3XRpcKu0UJeWnTxWsSyvNZ+w==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vfile-statistics/-/vfile-statistics-1.1.3.tgz", + "integrity": "sha512-CstaK/ebTz1W3Qp41Bt9Lj/2DmumFsCwC2sKahDNSPh0mPh7/UyMLCoU8ZBX34CRU0d61B4W41yIFsV0NKMZeA==", "dev": true }, "vinyl": { @@ -14533,13 +14913,10 @@ } }, "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz", + "integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==", + "dev": true }, "void-elements": { "version": "2.0.1", @@ -14653,12 +15030,6 @@ "wgxpath": "~1.0.0" }, "dependencies": { - "ejs": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.9.tgz", - "integrity": "sha512-GJCAeDBKfREgkBtgrYSf9hQy9kTb3helv0zGdzqhM7iAkW8FA/ZF97VQDbwFiwIT8MQLLOe5VlPZOEvZAqtUAQ==", - "dev": true - }, "has-flag": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", @@ -14724,12 +15095,6 @@ "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==", "dev": true }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, "async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", @@ -14739,6 +15104,62 @@ "lodash": "^4.17.11" } }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", @@ -14754,18 +15175,33 @@ "locate-path": "^2.0.0" } }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, "has-flag": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", "dev": true }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", "dev": true }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -14778,6 +15214,15 @@ "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", "dev": true }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, "load-json-file": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", @@ -14800,6 +15245,32 @@ "path-exists": "^3.0.0" } }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -14869,24 +15340,11 @@ "read-pkg": "^2.0.0" } }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true }, "supports-color": { "version": "4.5.0", @@ -14897,6 +15355,12 @@ "has-flag": "^2.0.0" } }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, "yargs": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", @@ -14917,6 +15381,15 @@ "y18n": "^3.2.1", "yargs-parser": "^7.0.0" } + }, + "yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } } } }, @@ -14947,10 +15420,10 @@ "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", "dev": true }, - "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "ejs": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.2.tgz", + "integrity": "sha512-PcW2a0tyTuPHz3tWyYqtK6r1fZ3gp+3Sop8Ph+ZYN81Ob5rwmbHEzaqs10N3BEsaGTkh/ooniXK+WwszGlc2+Q==", "dev": true }, "ws": { @@ -14993,7 +15466,7 @@ }, "webpack-dev-middleware": { "version": "2.0.6", - "resolved": "http://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz", "integrity": "sha512-tj5LLD9r4tDuRIDa5Mu9lnY2qBBehAITv6A9irqXhw/HQquZgTx3BCd57zYbU2gMDnncA49ufK2qVQSbaKJwOw==", "dev": true, "requires": { @@ -15007,9 +15480,9 @@ }, "dependencies": { "mime": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.2.tgz", - "integrity": "sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", "dev": true } } @@ -15125,6 +15598,17 @@ "pako": "~0.2.0" } }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, "camelcase": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", @@ -15335,12 +15819,6 @@ "object-assign": "^4.0.1" } }, - "lodash.clone": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", - "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=", - "dev": true - }, "memory-fs": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.3.0.tgz", @@ -15424,6 +15902,12 @@ "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=", "dev": true }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -15509,6 +15993,15 @@ "replace-ext": "0.0.1" } }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "requires": { + "indexof": "0.0.1" + } + }, "watchpack": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-0.2.9.tgz", @@ -15551,12 +16044,6 @@ "webpack-core": "~0.6.9" } }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true - }, "wordwrap": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", @@ -15578,12 +16065,13 @@ } }, "websocket-driver": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", - "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", + "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==", "dev": true, "requires": { - "http-parser-js": ">=0.4.0", + "http-parser-js": ">=0.4.0 <0.4.11", + "safe-buffer": ">=5.1.0", "websocket-extensions": ">=0.1.1" } }, @@ -15614,6 +16102,21 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -15628,6 +16131,28 @@ "requires": { "string-width": "^1.0.1", "strip-ansi": "^3.0.1" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } } }, "wrappy": { @@ -15675,9 +16200,9 @@ "dev": true }, "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, "yallist": { @@ -15693,12 +16218,68 @@ "dev": true }, "yargs-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", + "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yargs-unparser": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", + "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", "dev": true, "requires": { - "camelcase": "^4.1.0" + "flat": "^4.1.0", + "lodash": "^4.17.11", + "yargs": "^12.0.5" + }, + "dependencies": { + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } } }, "yeast": { diff --git a/package.json b/package.json index 11d5ee5901f..0a68cb6d5d9 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.20.0-pre", + "version": "2.20.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 7e5bda28964b461d4b539478637b0d8a8df57664 Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Tue, 18 Jun 2019 15:58:57 -0400 Subject: [PATCH 133/146] increment pre version --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4c03303a050..1d224cebc76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.20.0", + "version": "2.21.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 0a68cb6d5d9..a4e2985e1bd 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.20.0", + "version": "2.21.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 92fb453b1962f2ccf057d9cf965dc74e7306ac17 Mon Sep 17 00:00:00 2001 From: Harshad Mane Date: Tue, 18 Jun 2019 13:12:06 -0700 Subject: [PATCH 134/146] always secure (#3922) changed serevr end-point to HTTPS changed user-sync end-point to HTTPS changed imp.secure to 1 hard-coded Also corrected test case --- modules/pubmaticBidAdapter.js | 6 +++--- test/spec/modules/pubmaticBidAdapter_spec.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index 1f9ea06cad2..245ca3e60c9 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -5,8 +5,8 @@ import {config} from '../src/config'; const BIDDER_CODE = 'pubmatic'; const LOG_WARN_PREFIX = 'PubMatic: '; -const ENDPOINT = '//hbopenbid.pubmatic.com/translator?source=prebid-client'; -const USYNCURL = '//ads.pubmatic.com/AdServer/js/showad.js#PIX&kdntuid=1&p='; +const ENDPOINT = 'https://hbopenbid.pubmatic.com/translator?source=prebid-client'; +const USYNCURL = 'https://ads.pubmatic.com/AdServer/js/showad.js#PIX&kdntuid=1&p='; const DEFAULT_CURRENCY = 'USD'; const AUCTION_TYPE = 1; const PUBMATIC_DIGITRUST_KEY = 'nFIn8aLzbd'; @@ -513,7 +513,7 @@ function _createImpressionObject(bid, conf) { id: bid.bidId, tagid: bid.params.adUnit || undefined, bidfloor: _parseSlotParam('kadfloor', bid.params.kadfloor), - secure: window.location.protocol === 'https:' ? 1 : 0, + secure: 1, ext: { pmZoneId: _parseSlotParam('pmzoneid', bid.params.pmzoneid) }, diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 6126c0f9fd8..289e0f461ec 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -695,7 +695,7 @@ describe('PubMatic adapter', function () { it('Endpoint checking', function () { let request = spec.buildRequests(bidRequests); - expect(request.url).to.equal('//hbopenbid.pubmatic.com/translator?source=prebid-client'); + expect(request.url).to.equal('https://hbopenbid.pubmatic.com/translator?source=prebid-client'); expect(request.method).to.equal('POST'); }); From ce095e02e90ca58be59d9bcd5daa435ee65d1faf Mon Sep 17 00:00:00 2001 From: susyt Date: Thu, 20 Jun 2019 08:15:40 -0700 Subject: [PATCH 135/146] GumGum: adds tradedesk id param (#3896) * adds tradedesk id param * adds more tests * removes only from test spec * linting fix * updates one of the unit tests --- modules/gumgumBidAdapter.js | 10 +++- test/spec/modules/gumgumBidAdapter_spec.js | 54 ++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index 8c2b6415505..496941e3c1f 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -75,6 +75,14 @@ function getWrapperCode(wrapper, data) { return wrapper.replace('AD_JSON', window.btoa(JSON.stringify(data))) } +function _getTradeDeskIDParam(bidRequest) { + const unifiedIdObj = {}; + if (bidRequest.userId && bidRequest.userId.tdid) { + unifiedIdObj.tdid = bidRequest.userId.tdid; + } + return unifiedIdObj; +} + // TODO: use getConfig() function _getDigiTrustQueryParams() { function getDigiTrustId () { @@ -170,7 +178,7 @@ function buildRequests (validBidRequests, bidderRequest) { sizes: bidRequest.sizes, url: BID_ENDPOINT, method: 'GET', - data: Object.assign(data, _getBrowserParams(), _getDigiTrustQueryParams()) + data: Object.assign(data, _getBrowserParams(), _getDigiTrustQueryParams(), _getTradeDeskIDParam(bidRequest)) }) }); return bids; diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index c067f50fa56..a7a588afd13 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -80,6 +80,35 @@ describe('gumgumAdapter', function () { expect(request.method).to.equal('GET'); expect(request.id).to.equal('30b31c1838de1e'); }); + it('should correctly set the request paramters depending on params field', function () { + const request = Object.assign({}, bidRequests[0]); + delete request.params; + request.params = { + 'inScreen': '10433394', + 'bidfloor': 0.05 + }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.pi).to.equal(2); + expect(bidRequest.data).to.include.any.keys('t'); + expect(bidRequest.data).to.include.any.keys('fp'); + }); + it('should correctly set the request paramters depending on params field', function () { + const request = Object.assign({}, bidRequests[0]); + delete request.params; + request.params = { + 'ICV': '10433395' + }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.pi).to.equal(5); + expect(bidRequest.data).to.include.any.keys('ni'); + }); + it('should not add additional parameters depending on params field', function () { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.data).to.not.include.any.keys('ni'); + expect(request.data).to.not.include.any.keys('t'); + expect(request.data).to.not.include.any.keys('eAdBuyId'); + expect(request.data).to.not.include.any.keys('adBuyId'); + }); it('should add consent parameters if gdprConsent is present', function () { const gdprConsent = { consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', gdprApplies: true }; const fakeBidRequest = { gdprConsent: gdprConsent }; @@ -93,6 +122,31 @@ describe('gumgumAdapter', function () { const bidRequest = spec.buildRequests(bidRequests, fakeBidRequest)[0]; expect(bidRequest.data).to.not.include.any.keys('gdprConsent') }); + it('should add a tdid parameter if request contains unified id from TradeDesk', function () { + const unifiedId = { + 'userId': { + 'tdid': 'tradedesk-id' + } + } + const request = Object.assign(unifiedId, bidRequests[0]); + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.tdid).to.eq(unifiedId.userId.tdid); + }); + it('should not add a tdid parameter if unified id is not found', function () { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.data).to.not.include.any.keys('tdid'); + }); + it('should send ns parameter if browser contains navigator.connection property', function () { + const bidRequest = spec.buildRequests(bidRequests)[0]; + const connection = window.navigator && window.navigator.connection; + if (connection) { + const downlink = connection.downlink || connection.bandwidth; + expect(bidRequest.data).to.include.any.keys('ns'); + expect(bidRequest.data.ns).to.eq(Math.round(downlink * 1024)); + } else { + expect(bidRequest.data).to.not.include.any.keys('ns'); + } + }); }) describe('interpretResponse', function () { From bbd73ce0689949877a0c37c4279a55e944aa92f9 Mon Sep 17 00:00:00 2001 From: msm0504 <51493331+msm0504@users.noreply.github.com> Date: Fri, 21 Jun 2019 17:23:57 -0400 Subject: [PATCH 136/146] Digitrust support in PBS bid adapter and Rubicon bid adapter (#3935) * Add microadBidAdapter * Remove unnecessary encodeURIComponent from microadBidAdapter * Submit Advangelists Prebid Adapter * Submit Advangelists Prebid Adapter 1.1 * Correct procudtion endpoint for prebid * - Get digitrust data from bid request - Send UnifiedID and PubCommon data to OpenRTB * - Replace lodash with Prebid util functions - Updated pubcommon id location when sending to OpenRTB * Remove pref property when sending DigiTrust to OpenRTB * Updated tests to check new user external id locations in request * Remove use of array find from unit test --- modules/prebidServerBidAdapter/index.js | 64 ++++++----- modules/rubiconBidAdapter.js | 107 +++++++++++------- src/utils.js | 17 +++ .../modules/prebidServerBidAdapter_spec.js | 58 +++++----- 4 files changed, 146 insertions(+), 100 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 586d78ed2ca..028f02d9662 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -232,20 +232,24 @@ function doClientSideSyncs(bidders) { }); } -function _getDigiTrustQueryParams() { - function getDigiTrustId() { - let digiTrustUser = window.DigiTrust && (config.getConfig('digiTrustId') || window.DigiTrust.getUser({member: 'T9QSFKPDN9'})); +function _getDigiTrustQueryParams(bidRequest = {}) { + function getDigiTrustId(bidRequest) { + const bidRequestDigitrust = utils.deepAccess(bidRequest, 'bids.0.userId.digitrustid.data'); + if (bidRequestDigitrust) { + return bidRequestDigitrust; + } + + const digiTrustUser = config.getConfig('digiTrustId'); return (digiTrustUser && digiTrustUser.success && digiTrustUser.identity) || null; } - let digiTrustId = getDigiTrustId(); + let digiTrustId = getDigiTrustId(bidRequest); // Verify there is an ID and this user has not opted out if (!digiTrustId || (digiTrustId.privacy && digiTrustId.privacy.optout)) { return null; } return { id: digiTrustId.id, - keyv: digiTrustId.keyv, - pref: 0 + keyv: digiTrustId.keyv }; } @@ -334,7 +338,7 @@ const LEGACY_PROTOCOL = { _appendSiteAppDevice(request); - let digiTrust = _getDigiTrustQueryParams(); + let digiTrust = _getDigiTrustQueryParams(bidRequests && bidRequests[0]); if (digiTrust) { request.digiTrust = digiTrust; } @@ -521,26 +525,39 @@ const OPEN_RTB_PROTOCOL = { _appendSiteAppDevice(request); - const digiTrust = _getDigiTrustQueryParams(); + const digiTrust = _getDigiTrustQueryParams(bidRequests && bidRequests[0]); if (digiTrust) { - request.user = { ext: { digitrust: digiTrust } }; + utils.deepSetValue(request, 'user.ext.digitrust', digiTrust); } if (!utils.isEmpty(aliases)) { request.ext.prebid.aliases = aliases; } - if (bidRequests && bidRequests[0].userId && typeof bidRequests[0].userId === 'object') { - if (!request.user) { - request.user = {}; - } - if (!request.user.ext) { - request.user.ext = {} + const bidUserId = utils.deepAccess(bidRequests, '0.bids.0.userId'); + if (bidUserId && typeof bidUserId === 'object' && (bidUserId.tdid || bidUserId.pubcid)) { + utils.deepSetValue(request, 'user.ext.eids', []); + + if (bidUserId.tdid) { + request.user.ext.eids.push({ + source: 'adserver.org', + uids: [{ + id: bidUserId.tdid, + ext: { + rtiPartner: 'TDID' + } + }] + }); } - if (!request.user.ext.tpid) { - request.user.ext.tpid = {} + + if (bidUserId.pubcid) { + request.user.ext.eids.push({ + source: 'pubcommon', + uids: [{ + id: bidUserId.pubcid, + }] + }); } - Object.assign(request.user.ext.tpid, bidRequests[0].userId); } if (bidRequests && bidRequests[0].gdprConsent) { @@ -560,16 +577,7 @@ const OPEN_RTB_PROTOCOL = { request.regs = { ext: { gdpr: gdprApplies } }; } - let consentString = bidRequests[0].gdprConsent.consentString; - if (request.user) { - if (request.user.ext) { - request.user.ext.consent = consentString; - } else { - request.user.ext = { consent: consentString }; - } - } else { - request.user = { ext: { consent: consentString } }; - } + utils.deepSetValue(request, 'user.ext.consent', bidRequests[0].gdprConsent.consentString); } return request; diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index a0e9c89a221..abeebd2e1b2 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -14,6 +14,18 @@ export const FASTLANE_ENDPOINT = '//fastlane.rubiconproject.com/a/api/fastlane.j export const VIDEO_ENDPOINT = '//prebid-server.rubiconproject.com/openrtb2/auction'; export const SYNC_ENDPOINT = 'https://eus.rubiconproject.com/usync.html'; +const DIGITRUST_PROP_NAMES = { + FASTLANE: { + id: 'dt.id', + keyv: 'dt.keyv', + pref: 'dt.pref' + }, + PREBID_SERVER: { + id: 'id', + keyv: 'keyv' + } +}; + var sizeMap = { 1: '468x60', 2: '728x90', @@ -170,13 +182,9 @@ export const spec = { addVideoParameters(data, bidRequest); - const digiTrust = getDigiTrustQueryParams(); + const digiTrust = _getDigiTrustQueryParams(bidRequest, 'PREBID_SERVER'); if (digiTrust) { - data.user = { - ext: { - digitrust: digiTrust - } - }; + utils.deepSetValue(data, 'user.ext.digitrust', digiTrust); } if (bidderRequest.gdprConsent) { @@ -196,15 +204,32 @@ export const spec = { data.regs = {ext: {gdpr: gdprApplies}}; } - const consentString = bidderRequest.gdprConsent.consentString; - if (data.user) { - if (data.user.ext) { - data.user.ext.consent = consentString; - } else { - data.user.ext = {consent: consentString}; - } - } else { - data.user = {ext: {consent: consentString}}; + utils.deepSetValue(data, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + } + + if (bidRequest.userId && typeof bidRequest.userId === 'object' && + (bidRequest.userId.tdid || bidRequest.userId.pubcid)) { + utils.deepSetValue(data, 'user.ext.eids', []); + + if (bidRequest.userId.tdid) { + data.user.ext.eids.push({ + source: 'adserver.org', + uids: [{ + id: bidRequest.userId.tdid, + ext: { + rtiPartner: 'TDID' + } + }] + }); + } + + if (bidRequest.userId.pubcid) { + data.user.ext.eids.push({ + source: 'pubcommon', + uids: [{ + id: bidRequest.userId.pubcid, + }] + }); } } @@ -406,10 +431,8 @@ export const spec = { } // digitrust properties - const digitrustParams = _getDigiTrustQueryParams(); - Object.keys(digitrustParams).forEach(paramKey => { - data[paramKey] = digitrustParams[paramKey]; - }); + const digitrustParams = _getDigiTrustQueryParams(bidRequest, 'FASTLANE'); + Object.assign(data, digitrustParams); return data; }, @@ -600,22 +623,36 @@ function _getScreenResolution() { return [window.screen.width, window.screen.height].join('x'); } -function _getDigiTrustQueryParams() { +function _getDigiTrustQueryParams(bidRequest = {}, endpointName) { + if (!endpointName || !DIGITRUST_PROP_NAMES[endpointName]) { + return null; + } + const propNames = DIGITRUST_PROP_NAMES[endpointName]; + function getDigiTrustId() { - let digiTrustUser = window.DigiTrust && (config.getConfig('digiTrustId') || window.DigiTrust.getUser({member: 'T9QSFKPDN9'})); + const bidRequestDigitrust = utils.deepAccess(bidRequest, 'userId.digitrustid.data'); + if (bidRequestDigitrust) { + return bidRequestDigitrust; + } + + let digiTrustUser = (window.DigiTrust && (config.getConfig('digiTrustId') || window.DigiTrust.getUser({member: 'T9QSFKPDN9'}))); return (digiTrustUser && digiTrustUser.success && digiTrustUser.identity) || null; } let digiTrustId = getDigiTrustId(); // Verify there is an ID and this user has not opted out if (!digiTrustId || (digiTrustId.privacy && digiTrustId.privacy.optout)) { - return []; + return null; } - return { - 'dt.id': digiTrustId.id, - 'dt.keyv': digiTrustId.keyv, - 'dt.pref': 0 + + const digiTrustQueryParams = { + [propNames.id]: digiTrustId.id, + [propNames.keyv]: digiTrustId.keyv }; + if (propNames.pref) { + digiTrustQueryParams[propNames.pref] = 0; + } + return digiTrustQueryParams; } /** @@ -677,24 +714,6 @@ function parseSizes(bid, mediaType) { return masSizeOrdering(sizes); } -function getDigiTrustQueryParams() { - function getDigiTrustId() { - let digiTrustUser = window.DigiTrust && (config.getConfig('digiTrustId') || window.DigiTrust.getUser({member: 'T9QSFKPDN9'})); - return (digiTrustUser && digiTrustUser.success && digiTrustUser.identity) || null; - } - - let digiTrustId = getDigiTrustId(); - // Verify there is an ID and this user has not opted out - if (!digiTrustId || (digiTrustId.privacy && digiTrustId.privacy.optout)) { - return null; - } - return { - id: digiTrustId.id, - keyv: digiTrustId.keyv, - pref: 0 - }; -} - /** * @param {Object} data * @param bidRequest diff --git a/src/utils.js b/src/utils.js index ea80e970786..b5a46d174f4 100644 --- a/src/utils.js +++ b/src/utils.js @@ -986,6 +986,23 @@ export function deepAccess(obj, path) { return obj; } +/** + * @param {Object} obj The object to set a deep property value in + * @param {(string|Array.)} path Object path to the value you would like ot set. + * @param {*} value The value you would like to set + */ +export function deepSetValue(obj, path, value) { + let i; + path = path.split('.'); + for (i = 0; i < path.length - 1; i++) { + if (i !== path.length - 1 && typeof obj[path[i]] === 'undefined') { + obj[path[i]] = {}; + } + obj = obj[path[i]]; + } + obj[path[i]] = value; +} + /** * Returns content for a friendly iframe to execute a URL in script tag * @param {string} url URL to be executed in a script tag in a friendly iframe diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 47d0084d86a..e2a3a5b111a 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -513,38 +513,37 @@ describe('S2S Adapter', function () { }); it('adds digitrust id is present and user is not optout', function () { + let ortb2Config = utils.deepClone(CONFIG); + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; + + let consentConfig = { s2sConfig: ortb2Config }; + config.setConfig(consentConfig); + let digiTrustObj = { - success: true, - identity: { - privacy: { - optout: false - }, - id: 'testId', - keyv: 'testKeyV' - } + privacy: { + optout: false + }, + id: 'testId', + keyv: 'testKeyV' }; - window.DigiTrust = { - getUser: () => digiTrustObj - }; + let digiTrustBidRequest = utils.deepClone(BID_REQUESTS); + digiTrustBidRequest[0].bids[0].userId = { digitrustid: { data: digiTrustObj } }; - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + adapter.callBids(REQUEST, digiTrustBidRequest, addBidResponse, done, ajax); let requestBid = JSON.parse(requests[0].requestBody); - expect(requestBid.digiTrust).to.deep.equal({ - id: digiTrustObj.identity.id, - keyv: digiTrustObj.identity.keyv, - pref: 0 + expect(requestBid.user.ext.digitrust).to.deep.equal({ + id: digiTrustObj.id, + keyv: digiTrustObj.keyv }); - digiTrustObj.identity.privacy.optout = true; + digiTrustObj.privacy.optout = true; - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); + adapter.callBids(REQUEST, digiTrustBidRequest, addBidResponse, done, ajax); requestBid = JSON.parse(requests[1].requestBody); - expect(requestBid.digiTrust).to.not.exist; - - delete window.DigiTrust; + expect(requestBid.user && request.user.ext && requestBid.user.ext.digitrust).to.not.exist; }); it('adds device and app objects to request', function () { @@ -813,22 +812,25 @@ describe('S2S Adapter', function () { it('when userId is defined on bids, it\'s properties should be copied to user.ext.tpid properties', function () { let ortb2Config = utils.deepClone(CONFIG); - ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' + ortb2Config.endpoint = 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction'; let consentConfig = { s2sConfig: ortb2Config }; config.setConfig(consentConfig); let userIdBidRequest = utils.deepClone(BID_REQUESTS); - userIdBidRequest[0].userId = { - foo: 'abc123', - unifiedid: '1234' + userIdBidRequest[0].bids[0].userId = { + tdid: 'abc123', + pubcid: '1234' }; adapter.callBids(REQUEST, userIdBidRequest, addBidResponse, done, ajax); let requestBid = JSON.parse(requests[0].requestBody); - expect(typeof requestBid.user.ext.tpid).is.equal('object'); - expect(requestBid.user.ext.tpid.foo).is.equal('abc123'); - expect(requestBid.user.ext.tpid.unifiedid).is.equal('1234'); + expect(typeof requestBid.user.ext.eids).is.equal('object'); + expect(Array.isArray(requestBid.user.ext.eids)).to.be.true; + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'adserver.org')).is.not.empty; + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'adserver.org')[0].uids[0].id).is.equal('abc123'); + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'pubcommon')).is.not.empty; ; + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'pubcommon')[0].uids[0].id).is.equal('1234'); }) it('always add ext.prebid.targeting.includebidderkeys: false for ORTB', function () { From de8381f3552bee10c2eec80564dec3a26b433e65 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Durgeat Date: Mon, 24 Jun 2019 19:05:50 +0200 Subject: [PATCH 137/146] ID5 userId submodule (#3798) * Extract GDPRApplies in userId.js to make it available to cookie-sync requiring it * Adding id5 userId submodule, tests and documentation * Fixed typo in test name for unifiedid * Adding test to userId for ID5 * Follow correct naming convention for GDPRApplies: renamed to isGDPRApplicable * Refactoring of id5 module following refactoring of userId module. * Moved init of id5 user module at the end of the id5 module itself * regroup import to avoid a CircleCI Bug --- integrationExamples/gpt/userId_example.html | 12 ++- modules/id5IdSystem.js | 60 +++++++++++++ modules/userId.js | 13 ++- modules/userId.md | 24 +++++- test/spec/modules/userId_spec.js | 95 +++++++++++++++------ 5 files changed, 175 insertions(+), 29 deletions(-) create mode 100644 modules/id5IdSystem.js diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index d64e22e44c7..febe61628fe 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -140,7 +140,17 @@ name: "unifiedid", expires: 30 }, - + }, { + name: "id5Id", + params: { + partner: 173 // @TODO: Set your real ID5 partner ID here for production, please ask for one contact@id5.io + }, + storage: { + type: "cookie", + name: "id5id", + expires: 90 + }, + }, { name: "pubCommonId", storage: { diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js new file mode 100644 index 00000000000..39ab256a81e --- /dev/null +++ b/modules/id5IdSystem.js @@ -0,0 +1,60 @@ +/** + * This module adds ID5 to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/unifiedIdSystem + * @requires module:modules/userId + */ + +import * as utils from '../src/utils.js' +import {ajax} from '../src/ajax.js'; +import {isGDPRApplicable, attachIdSystem} from './userId.js'; + +/** @type {Submodule} */ +export const id5IdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: 'id5Id', + /** + * decode the stored id value for passing to bid requests + * @function + * @param {{ID5ID:Object}} value + * @returns {{id5id:String}} + */ + decode(value) { + return (value && typeof value['ID5ID'] === 'string') ? { 'id5id': value['ID5ID'] } : undefined; + }, + /** + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleParams} [configParams] + * @param {ConsentData} [consentData] + * @returns {function(callback:function)} + */ + getId(configParams, consentData) { + if (!configParams || typeof configParams.partner !== 'number') { + utils.logError(`User ID - ID5 submodule requires partner to be defined as a number`); + return; + } + const hasGdpr = isGDPRApplicable(consentData) ? 1 : 0; + const gdprConsentString = hasGdpr ? consentData.consentString : ''; + const url = `https://id5-sync.com/g/v1/${configParams.partner}.json?gdpr=${hasGdpr}&gdpr_consent=${gdprConsentString}`; + + return function (callback) { + ajax(url, response => { + let responseObj; + if (response) { + try { + responseObj = JSON.parse(response); + } catch (error) { + utils.logError(error); + } + } + callback(responseObj); + }, undefined, { method: 'GET' }); + } + } +}; + +attachIdSystem(id5IdSubmodule); diff --git a/modules/userId.js b/modules/userId.js index ae06dfc4027..c80ea21a0a0 100644 --- a/modules/userId.js +++ b/modules/userId.js @@ -158,13 +158,22 @@ function getStoredValue(storage) { return storedValue; } +/** + * test if consent module is present, and if GDPR applies + * @param {ConsentData} consentData + * @returns {boolean} + */ +export function isGDPRApplicable(consentData) { + return consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies; +} + /** * test if consent module is present, applies, and is valid for local storage or cookies (purpose 1) * @param {ConsentData} consentData * @returns {boolean} */ -function hasGDPRConsent(consentData) { - if (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) { +export function hasGDPRConsent(consentData) { + if (isGDPRApplicable(consentData)) { if (!consentData.consentString) { return false; } diff --git a/modules/userId.md b/modules/userId.md index 782e7782554..b36b9b1007c 100644 --- a/modules/userId.md +++ b/modules/userId.md @@ -3,12 +3,12 @@ Example showing `cookie` storage for user id data for both submodules ``` pbjs.setConfig({ - userSync: { + usersync: { userIds: [{ name: "unifiedId", params: { partner: "prebid", - url: "//match.adsrvr.org/track/rid?ttd_pid=prebid&fmt=json" + url: "http://match.adsrvr.org/track/rid?ttd_pid=prebid&fmt=json" }, storage: { type: "cookie", @@ -28,6 +28,26 @@ pbjs.setConfig({ }); ``` +Example showing `cookie` storage for user id data for id5 submodule +``` +pbjs.setConfig({ + usersync: { + userIds: [{ + name: "id5Id", + params: { + partner: 173 // @TODO: Set your real ID5 partner ID here for production, please ask for one contact@id5.io + }, + storage: { + type: "cookie", + name: "id5id", + expires: 90 + } + }], + syncDelay: 5000 + } +}); +``` + Example showing `localStorage` for user id data for both submodules ``` pbjs.setConfig({ diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index d0f5e06cdad..60df468a903 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -9,18 +9,20 @@ import {config} from 'src/config'; import * as utils from 'src/utils'; import {unifiedIdSubmodule} from 'modules/unifiedIdSystem'; import {pubCommonIdSubmodule} from 'modules/pubCommonIdSystem'; +import {id5IdSubmodule} from 'modules/id5IdSystem'; let assert = require('chai').assert; let expect = require('chai').expect; const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; describe('User ID', function() { - function getConfigMock(configArr1, configArr2) { + function getConfigMock(configArr1, configArr2, configArr3) { return { userSync: { syncDelay: 0, userIds: [ (configArr1 && configArr1.length === 3) ? getStorageMock.apply(null, configArr1) : null, - (configArr2 && configArr2.length === 3) ? getStorageMock.apply(null, configArr2) : null + (configArr2 && configArr2.length === 3) ? getStorageMock.apply(null, configArr2) : null, + (configArr3 && configArr3.length === 3) ? getStorageMock.apply(null, configArr3) : null ].filter(i => i)} } } @@ -68,7 +70,7 @@ describe('User ID', function() { let pubcid = utils.getCookie('pubcid'); expect(pubcid).to.be.null; // there should be no cookie initially - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); @@ -94,7 +96,7 @@ describe('User ID', function() { let pubcid1; let pubcid2; - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); requestBidsHook((config) => { innerAdUnits1 = config.adUnits }, {adUnits: adUnits1}); @@ -108,7 +110,7 @@ describe('User ID', function() { }); }); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); requestBidsHook((config) => { innerAdUnits2 = config.adUnits }, {adUnits: adUnits2}); @@ -129,7 +131,7 @@ describe('User ID', function() { let adUnits = [getAdUnitMock()]; let innerAdUnits; - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid_alt', 'cookie'])); requestBidsHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); @@ -164,14 +166,14 @@ describe('User ID', function() { }); it('fails initialization if opt out cookie exists', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - opt-out cookie found, exit module'); }); it('initializes if no opt out cookie exists', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 1 submodules'); @@ -190,7 +192,7 @@ describe('User ID', function() { }); it('handles config with no usersync object', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig({}); // usersync is undefined, and no logInfo message for 'User ID - usersync config updated' @@ -198,14 +200,14 @@ describe('User ID', function() { }); it('handles config with empty usersync object', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig({ usersync: {} }); expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with usersync and userIds that are empty objs', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig({ usersync: { @@ -216,7 +218,7 @@ describe('User ID', function() { }); it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig({ usersync: { @@ -233,15 +235,15 @@ describe('User ID', function() { }); it('config with 1 configurations should create 1 submodules', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig(getConfigMock(['unifiedId', 'unifiedid', 'cookie'])); expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 1 submodules'); }); - it('config with 2 configurations should result in 2 submodules add', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + it('config with 3 configurations should result in 3 submodules add', function () { + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig({ usersync: { @@ -251,14 +253,17 @@ describe('User ID', function() { }, { name: 'unifiedId', storage: { name: 'unifiedid', type: 'cookie' } + }, { + name: 'id5Id', + storage: { name: 'id5id', type: 'cookie' } }] } }); - expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 2 submodules'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 3 submodules'); }); it('config syncDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig({ usersync: { @@ -322,7 +327,7 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from pubcommonid html5', function(done) { + it('test hook from unifiedid html5', function(done) { // simulate existing browser local storage values localStorage.setItem('unifiedid_alt', JSON.stringify({'TDID': 'testunifiedid_alt'})); localStorage.setItem('unifiedid_alt_exp', ''); @@ -344,13 +349,36 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook when both pubCommonId and unifiedId have data to pass', function(done) { + it('test hook from id5id cookies', function(done) { + // simulate existing browser local storage values + utils.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); + + setSubmoduleRegistry([id5IdSubmodule]); + init(config); + config.setConfig(getConfigMock(['id5Id', 'id5id', 'cookie'])); + + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.id5id'); + expect(bid.userId.id5id).to.equal('testid5id'); + }); + }); + utils.setCookie('id5id', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + + it('test hook when pubCommonId, unifiedId and id5Id have data to pass', function(done) { utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); utils.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); + utils.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); - config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'])); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], + ['unifiedId', 'unifiedid', 'cookie'], + ['id5Id', 'id5id', 'cookie'])); requestBidsHook(function() { adUnits.forEach(unit => { @@ -361,17 +389,22 @@ describe('User ID', function() { // also check that UnifiedId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.tdid'); expect(bid.userId.tdid).to.equal('testunifiedid'); + // also check that Id5Id id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.id5id'); + expect(bid.userId.id5id).to.equal('testid5id'); }); }); utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('id5id', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); - it('test hook when pubCommonId and unifiedId have their modules added before and after init', function(done) { + it('test hook when pubCommonId, unifiedId and id5Id have their modules added before and after init', function(done) { utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); utils.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); + utils.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([]); @@ -382,8 +415,11 @@ describe('User ID', function() { // attaching after init attachIdSystem(unifiedIdSubmodule); + attachIdSystem(id5IdSubmodule); - config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'])); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], + ['unifiedId', 'unifiedid', 'cookie'], + ['id5Id', 'id5id', 'cookie'])); requestBidsHook(function() { adUnits.forEach(unit => { @@ -394,10 +430,14 @@ describe('User ID', function() { // also check that UnifiedId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.tdid'); expect(bid.userId.tdid).to.equal('cookie-value-add-module-variations'); + // also check that Id5Id id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.id5id'); + expect(bid.userId.id5id).to.equal('testid5id'); }); }); utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('id5id', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); @@ -405,9 +445,10 @@ describe('User ID', function() { it('should add new id system ', function(done) { utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); utils.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); + utils.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); utils.setCookie('MOCKID', JSON.stringify({'MOCKID': '123456778'}), new Date(Date.now() + 5000).toUTCString()); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig({ @@ -417,6 +458,8 @@ describe('User ID', function() { name: 'pubCommonId', storage: { name: 'pubcid', type: 'cookie' } }, { name: 'unifiedId', storage: { name: 'unifiedid', type: 'cookie' } + }, { + name: 'id5Id', storage: { name: 'id5id', type: 'cookie' } }, { name: 'mockId', storage: { name: 'MOCKID', type: 'cookie' } }] @@ -445,6 +488,9 @@ describe('User ID', function() { // check UnifiedId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.tdid'); expect(bid.userId.tdid).to.equal('cookie-value-add-module-variations'); + // also check that Id5Id id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.id5id'); + expect(bid.userId.id5id).to.equal('testid5id'); // check MockId data was copied to bid expect(bid).to.have.deep.nested.property('userId.mid'); expect(bid.userId.mid).to.equal('123456778'); @@ -452,6 +498,7 @@ describe('User ID', function() { }); utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('id5id', '', EXPIRED_COOKIE_DATE); utils.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); From 37232c79d9e606324b673bb65d51e9a4a3c93311 Mon Sep 17 00:00:00 2001 From: Dan Bogdan <43830380+EMXDigital@users.noreply.github.com> Date: Mon, 24 Jun 2019 15:39:48 -0400 Subject: [PATCH 138/146] EMX Digital: Device info and Video parameter updates (#3929) * EMX Digital - device info and video parameter updates * update timeout value to grab from bidderRequest arg * removing unused import * removing object spread use. quote fix for lint * remove space --- modules/emx_digitalBidAdapter.js | 85 +++++++++++++------ .../modules/emx_digitalBidAdapter_spec.js | 7 +- 2 files changed, 59 insertions(+), 33 deletions(-) diff --git a/modules/emx_digitalBidAdapter.js b/modules/emx_digitalBidAdapter.js index 69e02d5c860..2ca595151f9 100644 --- a/modules/emx_digitalBidAdapter.js +++ b/modules/emx_digitalBidAdapter.js @@ -1,13 +1,14 @@ import * as utils from '../src/utils'; import { registerBidder } from '../src/adapters/bidderFactory'; import { BANNER, VIDEO } from '../src/mediaTypes'; -import { config } from '../src/config'; import { Renderer } from '../src/Renderer'; import includes from 'core-js/library/fn/array/includes'; const BIDDER_CODE = 'emx_digital'; const ENDPOINT = 'hb.emxdgt.com'; const RENDERER_URL = '//js.brealtime.com/outstream/1.30.0/bundle.js'; +const ADAPTER_VERSION = '1.40.2'; +const DEFAULT_CUR = 'USD'; export const emxAdapter = { validateSizes: (sizes) => { @@ -48,6 +49,23 @@ export const emxAdapter = { } return bidResponse; }, + isMobile: () => { + return (/(ios|ipod|ipad|iphone|android)/i).test(navigator.userAgent); + }, + isConnectedTV: () => { + return (/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(navigator.userAgent); + }, + getDevice: () => { + return { + ua: navigator.userAgent, + js: 1, + dnt: (navigator.doNotTrack === 'yes' || navigator.doNotTrack === '1' || navigator.msDoNotTrack === '1') ? 1 : 0, + h: screen.height, + w: screen.width, + devicetype: emxAdapter.isMobile() ? 1 : emxAdapter.isConnectedTV() ? 3 : 2, + language: (navigator.language || navigator.browserLanguage || navigator.userLanguage || navigator.systemLanguage), + }; + }, cleanProtocols: (video) => { if (video.protocols && includes(video.protocols, 7)) { // not supporting VAST protocol 7 (VAST 4.0); @@ -85,10 +103,22 @@ export const emxAdapter = { return renderer; }, buildVideo: (bid) => { - bid.params.video = bid.params.video || {}; - bid.params.video.h = bid.mediaTypes.video.playerSize[0][0]; - bid.params.video.w = bid.mediaTypes.video.playerSize[0][1]; - return emxAdapter.cleanProtocols(bid.params.video); + let videoObj = Object.assign(bid.mediaTypes.video, bid.params.video) + return emxAdapter.cleanProtocols(videoObj); + }, + parseResponse: (bidResponseAdm) => { + try { + return decodeURIComponent(bidResponseAdm); + } catch (err) { + utils.logError('emx_digitalBidAdapter', 'error', err); + } + }, + getReferrer: () => { + try { + return window.top.document.referrer; + } catch (err) { + return document.referrer; + } }, getGdpr: (bidRequests, emxData) => { if (bidRequests.gdprConsent) { @@ -151,40 +181,44 @@ export const spec = { return true; }, buildRequests: function (validBidRequests, bidderRequest) { - const page = bidderRequest.refererInfo.referer; - let emxImps = []; - const timeout = config.getConfig('bidderTimeout'); + const emxImps = []; + const timeout = bidderRequest.timeout || ''; const timestamp = Date.now(); - const url = location.protocol + '//' + ENDPOINT + ('?t=' + timeout + '&ts=' + timestamp); - const networkProtocol = location.protocol.indexOf('https') > -1 ? 1 : 0; + const url = location.protocol + '//' + ENDPOINT + ('?t=' + timeout + '&ts=' + timestamp + '&src=pbjs'); + const secure = location.protocol.indexOf('https') > -1 ? 1 : 0; + const domain = utils.getTopWindowLocation().hostname; + const page = bidderRequest.refererInfo.referer; + const device = emxAdapter.getDevice(); + const ref = emxAdapter.getReferrer(); utils._each(validBidRequests, function (bid) { - let tagId = utils.getBidIdParameter('tagid', bid.params); - let bidFloor = parseFloat(utils.getBidIdParameter('bidfloor', bid.params)) || 0; + let tagid = utils.getBidIdParameter('tagid', bid.params); + let bidfloor = parseFloat(utils.getBidIdParameter('bidfloor', bid.params)) || 0; let isVideo = !!bid.mediaTypes.video; let data = { id: bid.bidId, tid: bid.transactionId, - tagid: tagId, - secure: networkProtocol + tagid, + secure }; let typeSpecifics = isVideo ? { video: emxAdapter.buildVideo(bid) } : { banner: emxAdapter.buildBanner(bid) }; - let emxBid = Object.assign(data, typeSpecifics); + let bidfloorObj = bidfloor > 0 ? { bidfloor, bidfloorcur: DEFAULT_CUR } : {}; + let emxBid = Object.assign(data, typeSpecifics, bidfloorObj); - if (bidFloor > 0) { - emxBid.bidfloor = bidFloor - } emxImps.push(emxBid); }); let emxData = { id: bidderRequest.auctionId, imp: emxImps, + device, site: { - domain: window.top.document.location.host, - page: page + domain, + page, + ref }, - version: '1.30.0' + cur: DEFAULT_CUR, + version: ADAPTER_VERSION }; emxData = emxAdapter.getGdpr(bidderRequest, Object.assign({}, emxData)); @@ -204,6 +238,7 @@ export const spec = { response.seatbid.forEach(function (emxBid) { emxBid = emxBid.bid[0]; let isVideo = false; + let adm = emxAdapter.parseResponse(emxBid.adm) || ''; let bidResponse = { requestId: emxBid.id, cpm: emxBid.price, @@ -214,7 +249,7 @@ export const spec = { currency: 'USD', netRevenue: true, ttl: emxBid.ttl, - ad: decodeURIComponent(emxBid.adm) + ad: adm }; if (emxBid.adm && emxBid.adm.indexOf(' -1) { isVideo = true; @@ -234,12 +269,6 @@ export const spec = { url: '//biddr.brealtime.com/check.html' }); } - if (syncOptions.pixelEnabled) { - syncs.push({ - type: 'image', - url: '//edba.brealtime.com/' - }); - } return syncs; } }; diff --git a/test/spec/modules/emx_digitalBidAdapter_spec.js b/test/spec/modules/emx_digitalBidAdapter_spec.js index 170e5676f43..10d0d74c49c 100644 --- a/test/spec/modules/emx_digitalBidAdapter_spec.js +++ b/test/spec/modules/emx_digitalBidAdapter_spec.js @@ -276,8 +276,9 @@ describe('emx_digital Adapter', function () { it('properly sends site information and protocol', function () { request = spec.buildRequests(bidderRequest.bids, bidderRequest); request = JSON.parse(request.data); - expect(request.site.domain).to.equal(window.top.document.location.host); + expect(request.site.domain).to.equal(utils.getTopWindowLocation().hostname); expect(decodeURIComponent(request.site.page)).to.equal(bidderRequest.refererInfo.referer); + expect(request.site.ref).to.equal(window.top.document.referrer); }); it('builds correctly formatted request banner object', function () { @@ -511,10 +512,6 @@ describe('emx_digital Adapter', function () { let iframeSync = spec.getUserSyncs(syncOptionsIframe); expect(iframeSync.length).to.equal(1); expect(iframeSync[0].type).to.equal('iframe'); - - let pixelSync = spec.getUserSyncs(syncOptionsPixel); - expect(pixelSync.length).to.equal(1); - expect(pixelSync[0].type).to.equal('image'); }); }); }); From 75aca43bc2c9f05db2483bcd4661c32682fa0dcc Mon Sep 17 00:00:00 2001 From: Rich Snapp Date: Mon, 24 Jun 2019 13:40:40 -0600 Subject: [PATCH 139/146] update placementId to be number instead of string (#3941) --- integrationExamples/gpt/prebidServer_example.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrationExamples/gpt/prebidServer_example.html b/integrationExamples/gpt/prebidServer_example.html index f13c93963c6..db61a6a46d6 100644 --- a/integrationExamples/gpt/prebidServer_example.html +++ b/integrationExamples/gpt/prebidServer_example.html @@ -41,7 +41,7 @@ { bidder: 'appnexus', params: { - placementId: '13144370' + placementId: 13144370 } } ] From b6aaf644b2239da3f85b73737a5a06c6fa327d01 Mon Sep 17 00:00:00 2001 From: Gleb Glushtsov Date: Mon, 24 Jun 2019 20:21:30 -0400 Subject: [PATCH 140/146] [33Across adapter] Map ad unit path to element id (#3920) * fix return of _mapAdUnitPathToElementId() * improve logging of _mapAdUnitPathToElementId() * do not use Array.find() * return id once element is found * return id once element is found * let -> const --- modules/33acrossBidAdapter.js | 48 ++++++++++++++------ modules/33acrossBidAdapter.md | 2 +- test/spec/modules/33acrossBidAdapter_spec.js | 4 +- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/modules/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js index 1cf885ed6e4..801a5d564a3 100644 --- a/modules/33acrossBidAdapter.js +++ b/modules/33acrossBidAdapter.js @@ -1,8 +1,7 @@ +import { registerBidder } from '../src/adapters/bidderFactory'; +import { config } from '../src/config'; import * as utils from '../src/utils'; -const { registerBidder } = require('../src/adapters/bidderFactory'); -const { config } = require('../src/config'); - const BIDDER_CODE = '33across'; const END_POINT = 'https://ssc.33across.com/api/v1/hb'; const SYNC_ENDPOINT = 'https://de.tynt.com/deb/v2?m=xch&rt=html'; @@ -32,17 +31,43 @@ function _isViewabilityMeasurable(element) { } function _getViewability(element, topWin, { w, h } = {}) { - return utils.getWindowTop().document.visibilityState === 'visible' + return topWin.document.visibilityState === 'visible' ? _getPercentInView(element, topWin, { w, h }) : 0; } +function _mapAdUnitPathToElementId(adUnitCode) { + if (utils.isGptPubadsDefined()) { + const adSlots = googletag.pubads().getSlots(); + const isMatchingAdSlot = utils.isSlotMatchingAdUnitCode(adUnitCode); + + for (let i = 0; i < adSlots.length; i++) { + if (isMatchingAdSlot(adSlots[i])) { + const id = adSlots[i].getSlotElementId(); + + utils.logInfo(`[33Across Adapter] Map ad unit path to HTML element id: '${adUnitCode}' -> ${id}`); + + return id; + } + } + } + + utils.logWarn(`[33Across Adapter] Unable to locate element for ad unit code: '${adUnitCode}'`); + + return null; +} + +function _getAdSlotHTMLElement(adUnitCode) { + return document.getElementById(adUnitCode) || + document.getElementById(_mapAdUnitPathToElementId(adUnitCode)); +} + // Infer the necessary data from valid bid for a minimal ttxRequest and create HTTP request // NOTE: At this point, TTX only accepts request for a single impression function _createServerRequest(bidRequest, gdprConsent) { const ttxRequest = {}; const params = bidRequest.params; - const element = document.getElementById(bidRequest.adUnitCode); + const element = _getAdSlotHTMLElement(bidRequest.adUnitCode); const sizes = _transformSizes(bidRequest.sizes); const minSize = _getMinSize(sizes); @@ -52,10 +77,6 @@ function _createServerRequest(bidRequest, gdprConsent) { const contributeViewability = ViewabilityContributor(viewabilityAmount); - if (element === null) { - utils.logWarn(`Unable to locate element with id: '${bidRequest.adUnitCode}'`); - } - /* * Infer data for the request payload */ @@ -264,13 +285,14 @@ function isBidRequestValid(bid) { // - the server, at this point, also doesn't need the consent string to handle gdpr compliance. So passing // value whether set or not, for the sake of future dev. function buildRequests(bidRequests, bidderRequest) { - const gdprConsent = Object.assign({ consentString: undefined, gdprApplies: false }, bidderRequest && bidderRequest.gdprConsent); + const gdprConsent = Object.assign({ + consentString: undefined, + gdprApplies: false + }, bidderRequest && bidderRequest.gdprConsent); adapterState.uniqueSiteIds = bidRequests.map(req => req.params.siteId).filter(utils.uniques); - return bidRequests.map((req) => { - return _createServerRequest(req, gdprConsent); - }); + return bidRequests.map(req => _createServerRequest(req, gdprConsent)); } // NOTE: At this point, the response from 33exchange will only ever contain one bid i.e. the highest bid diff --git a/modules/33acrossBidAdapter.md b/modules/33acrossBidAdapter.md index bdb2b944861..58e3b2b273a 100644 --- a/modules/33acrossBidAdapter.md +++ b/modules/33acrossBidAdapter.md @@ -16,7 +16,7 @@ Connects to 33Across's exchange for bids. ``` var adUnits = [ { - code: '33across-hb-ad-123456-1', + code: '33across-hb-ad-123456-1', // ad slot HTML element ID sizes: [ [300, 250], [728, 90] diff --git a/test/spec/modules/33acrossBidAdapter_spec.js b/test/spec/modules/33acrossBidAdapter_spec.js index 1fc77a7e14d..7e1a8619c63 100644 --- a/test/spec/modules/33acrossBidAdapter_spec.js +++ b/test/spec/modules/33acrossBidAdapter_spec.js @@ -337,8 +337,8 @@ describe('33acrossBidAdapter:', function () { utils.getWindowTop.restore(); utils.getWindowSelf.restore(); - sandbox.stub(utils, 'getWindowTop').returns(win); - sandbox.stub(utils, 'getWindowSelf').returns({}); + sandbox.stub(utils, 'getWindowTop').returns({}); + sandbox.stub(utils, 'getWindowSelf').returns(win); expect(spec.buildRequests(bidRequests)).to.deep.equal([ serverRequest ]); }); From 9d2f06c79e0a84207fb72e36329a894ee221c5b1 Mon Sep 17 00:00:00 2001 From: Daniel Liebner Date: Tue, 25 Jun 2019 13:36:02 -0400 Subject: [PATCH 141/146] New Adapter: bidglass (#3861) * Added bidglass adapter + test * PR Review Updates: - Added formal params to getUserSyncs function definition - getUserSyncs now always returns an array - Improved unit test coverage * PR Review Updates: - Removed unused methods: getUserSyncs, onTimeout, onBidWon, onSetTargeting - Removed getUserSyncs unit test - Removed "dead code" - Removed some unnecessary comments - Fixed usage of parseInt --- modules/bidglassBidAdapter.js | 134 ++++++++++++++++++++++ modules/bidglassBidAdapter.md | 34 ++++++ test/spec/modules/bidglassAdapter_spec.js | 103 +++++++++++++++++ 3 files changed, 271 insertions(+) create mode 100644 modules/bidglassBidAdapter.js create mode 100644 modules/bidglassBidAdapter.md create mode 100644 test/spec/modules/bidglassAdapter_spec.js diff --git a/modules/bidglassBidAdapter.js b/modules/bidglassBidAdapter.js new file mode 100644 index 00000000000..1898d1220fa --- /dev/null +++ b/modules/bidglassBidAdapter.js @@ -0,0 +1,134 @@ +import * as utils from 'src/utils'; +// import {config} from 'src/config'; +import {registerBidder} from 'src/adapters/bidderFactory'; + +const BIDDER_CODE = 'bidglass'; + +export const spec = { + code: BIDDER_CODE, + aliases: ['bg'], // short 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.adUnitId && !isNaN(parseFloat(bid.params.adUnitId)) && isFinite(bid.params.adUnitId)); + }, + /** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests, bidderRequest) { + /* + Sample array entry for validBidRequests[]: + [{ + "bidder": "bidglass", + "bidId": "51ef8751f9aead", + "params": { + "adUnitId": 11, + ... + }, + "adUnitCode": "div-gpt-ad-1460505748561-0", + "transactionId": "d7b773de-ceaa-484d-89ca-d9f51b8d61ec", + "sizes": [[320,50],[300,250],[300,600]], + "bidderRequestId": "418b37f85e772c", + "auctionId": "18fd8b8b0bd757", + "bidRequestsCount": 1 + }] + */ + + let imps = []; + let getReferer = function() { + return window === window.top ? window.location.href : window.parent === window.top ? document.referrer : null; + }; + let getOrigins = function() { + var ori = [window.location.protocol + '//' + window.location.hostname]; + + if (window.location.ancestorOrigins) { + for (var i = 0; i < window.location.ancestorOrigins.length; i++) { + ori.push(window.location.ancestorOrigins[i]); + } + } else if (window !== window.top) { + // Derive the parent origin + var parts = document.referrer.split('/'); + + ori.push(parts[0] + '//' + parts[2]); + + if (window.parent !== window.top) { + // Additional unknown origins exist + ori.push('null'); + } + } + + return ori; + }; + + utils._each(validBidRequests, function(bid) { + bid.sizes = ((utils.isArray(bid.sizes) && utils.isArray(bid.sizes[0])) ? bid.sizes : [bid.sizes]); + bid.sizes = bid.sizes.filter(size => utils.isArray(size)); + + // Stuff to send: [bid id, sizes, adUnitId] + imps.push({ + bidId: bid.bidId, + sizes: bid.sizes, + adUnitId: utils.getBidIdParameter('adUnitId', bid.params) + }); + }); + + // Stuff to send: page URL + const bidReq = { + reqId: utils.getUniqueIdentifierStr(), + imps: imps, + ref: getReferer(), + ori: getOrigins() + }; + + let url = 'https://bid.glass/ad/hb.php?' + + `src=$$REPO_AND_VERSION$$`; + + return { + method: 'POST', + url: url, + data: JSON.stringify(bidReq), + options: { + contentType: 'text/plain', + withCredentials: false + } + } + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse) { + const bidResponses = []; + + utils._each(serverResponse.body.bidResponses, function(bid) { + bidResponses.push({ + requestId: bid.requestId, + cpm: parseFloat(bid.cpm), + width: parseInt(bid.width, 10), + height: parseInt(bid.height, 10), + creativeId: bid.creativeId, + dealId: bid.dealId || null, + currency: bid.currency || 'USD', + mediaType: bid.mediaType || 'banner', + netRevenue: true, + ttl: bid.ttl || 10, + ad: bid.ad + }); + }); + + return bidResponses; + } + +} + +registerBidder(spec); diff --git a/modules/bidglassBidAdapter.md b/modules/bidglassBidAdapter.md new file mode 100644 index 00000000000..5384a095314 --- /dev/null +++ b/modules/bidglassBidAdapter.md @@ -0,0 +1,34 @@ +# Overview + +``` +Module Name: Bid Glass Bid Adapter +Module Type: Bidder Adapter +Maintainer: dliebner@gmail.com +``` + +# Description + +Connects to Bid Glass and allows bids on ad units to compete within prebid. + +# Sample Ad Unit: For Publishers +``` +var adUnits = [{ + code: 'bg-test-rectangle', + sizes: [[300, 250]], + bids: [{ + bidder: 'bidglass', + params: { + adUnitId: '-1' + } + }] +},{ + code: 'bg-test-leaderboard', + sizes: [[728, 90]], + bids: [{ + bidder: 'bidglass', + params: { + adUnitId: '-1' + } + }] +}] +``` \ No newline at end of file diff --git a/test/spec/modules/bidglassAdapter_spec.js b/test/spec/modules/bidglassAdapter_spec.js new file mode 100644 index 00000000000..00a47fc997a --- /dev/null +++ b/test/spec/modules/bidglassAdapter_spec.js @@ -0,0 +1,103 @@ +import { expect } from 'chai'; +import { spec } from 'modules/bidglassBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +describe('Bid Glass Adapter', function () { + const adapter = newBidder(spec); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'bidglass', + 'params': { + 'adUnitId': '3' + }, + 'adUnitCode': 'bidglass-testunit', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + const bidRequests = [{ + 'bidder': 'bidglass', + 'params': { + 'adUnitId': '3' + }, + 'adUnitCode': 'bidglass-testunit', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }]; + + const request = spec.buildRequests(bidRequests); + + it('sends bid request to our endpoint via POST', function () { + expect(request.method).to.equal('POST'); + }); + + it('sets withCredentials to false', function () { + expect(request.options.withCredentials).to.equal(false); + }); + }); + + describe('interpretResponse', function () { + let response; + beforeEach(function () { + response = { + body: { + 'bidResponses': [{ + 'ad': '', + 'cpm': '0.01', + 'creativeId': '-1', + 'width': '300', + 'height': '250', + 'requestId': '30b31c1838de1e' + }] + } + }; + }); + + it('should get the correct bid response', function () { + let expectedResponse = [{ + 'requestId': '30b31c1838de1e', + 'cpm': 0.01, + 'width': 300, + 'height': 250, + 'creativeId': '-1', + 'dealId': null, + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 10, + 'ad': '' + }]; + + let result = spec.interpretResponse(response); + expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])); + }); + + it('handles empty bid response', function () { + let response = { + body: { + 'bidResponses': [] + } + }; + let result = spec.interpretResponse(response); + expect(result.length).to.equal(0); + }); + }); +}); From e64360ccdd3613dbffebd29212367ffc6b72c3e6 Mon Sep 17 00:00:00 2001 From: Index Exchange 3 Prebid Team Date: Tue, 25 Jun 2019 13:37:06 -0400 Subject: [PATCH 142/146] Prebid.js Video (#3901) * Implemented changes required to provide support for video in the IX bidding adapter for Instream and Outstream contexts. * Implemented changes required to provide support for video in the IX bidding adapter for Instream and Outstream contexts. * fix find module --- modules/ixBidAdapter.js | 366 ++++++++++++++++--------- modules/ixBidAdapter.md | 128 ++++++++- test/spec/modules/ixBidAdapter_spec.js | 344 ++++++++++++++++++----- 3 files changed, 628 insertions(+), 210 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index c63b920dc93..f26c5e413c5 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -1,38 +1,75 @@ import * as utils from '../src/utils'; -import { BANNER } from '../src/mediaTypes'; +import { BANNER, VIDEO } from '../src/mediaTypes'; +import find from 'core-js/library/fn/array/find'; import { config } from '../src/config'; import isArray from 'core-js/library/fn/array/is-array'; import isInteger from 'core-js/library/fn/number/is-integer'; import { registerBidder } from '../src/adapters/bidderFactory'; const BIDDER_CODE = 'ix'; -const BANNER_SECURE_BID_URL = 'https://as-sec.casalemedia.com/cygnus'; -const BANNER_INSECURE_BID_URL = 'http://as.casalemedia.com/cygnus'; -const SUPPORTED_AD_TYPES = [BANNER]; -const ENDPOINT_VERSION = 7.2; +const SECURE_BID_URL = 'https://as-sec.casalemedia.com/cygnus'; +const INSECURE_BID_URL = 'http://as.casalemedia.com/cygnus'; +const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; +const BANNER_ENDPOINT_VERSION = 7.2; +const VIDEO_ENDPOINT_VERSION = 8.1; + const CENT_TO_DOLLAR_FACTOR = 100; -const TIME_TO_LIVE = 35; +const BANNER_TIME_TO_LIVE = 35; +const VIDEO_TIME_TO_LIVE = 3600; // 1hr const NET_REVENUE = true; const PRICE_TO_DOLLAR_FACTOR = { JPY: 1 }; /** - * Transform valid bid request config object to impression object that will be sent to ad server. + * Transform valid bid request config object to banner impression object that will be sent to ad server. * * @param {object} bid A valid bid request config object. * @return {object} A impression object that will be sent to ad server. */ function bidToBannerImp(bid) { - const imp = {}; - - imp.id = bid.bidId; + const imp = bidToImp(bid); imp.banner = {}; imp.banner.w = bid.params.size[0]; imp.banner.h = bid.params.size[1]; imp.banner.topframe = utils.inIframe() ? 0 : 1; + return imp; +} + +/** + * Transform valid bid request config object to video impression object that will be sent to ad server. + * + * @param {object} bid A valid bid request config object. + * @return {object} A impression object that will be sent to ad server. + */ +function bidToVideoImp(bid) { + const imp = bidToImp(bid); + + imp.video = utils.deepClone(bid.params.video) + imp.video.w = bid.params.size[0]; + imp.video.h = bid.params.size[1]; + + const context = utils.deepAccess(bid, 'mediaTypes.video.context'); + if (context) { + if (context === 'instream') { + imp.video.placement = 1; + } else if (context === 'outstream') { + imp.video.placement = 4; + } else { + utils.logWarn(`ix bidder params: video context '${context}' is not supported`); + } + } + + return imp; +} + +function bidToImp(bid) { + const imp = {}; + + imp.id = bid.bidId; + imp.ext = {}; imp.ext.siteID = bid.params.siteId; @@ -58,7 +95,7 @@ function bidToBannerImp(bid) { * @param {string} currency Global currency in bid response. * @return {object} bid The parsed bid. */ -function parseBid(rawBid, currency) { +function parseBid(rawBid, currency, bidRequest) { const bid = {}; if (PRICE_TO_DOLLAR_FACTOR.hasOwnProperty(currency)) { @@ -68,15 +105,27 @@ function parseBid(rawBid, currency) { } bid.requestId = rawBid.impid; - bid.width = rawBid.w; - bid.height = rawBid.h; - bid.ad = rawBid.adm; + bid.dealId = utils.deepAccess(rawBid, 'ext.dealid'); - bid.ttl = TIME_TO_LIVE; bid.netRevenue = NET_REVENUE; bid.currency = currency; bid.creativeId = rawBid.hasOwnProperty('crid') ? rawBid.crid : '-'; + // in the event of a video + if (utils.deepAccess(rawBid, 'ext.vasturl')) { + bid.vastUrl = rawBid.ext.vasturl + bid.width = bidRequest.video.w; + bid.height = bidRequest.video.h; + bid.mediaType = VIDEO; + bid.ttl = VIDEO_TIME_TO_LIVE; + } else { + bid.ad = rawBid.adm; + bid.width = rawBid.w; + bid.height = rawBid.h; + bid.mediaType = BANNER; + bid.ttl = BANNER_TIME_TO_LIVE; + } + bid.meta = {}; bid.meta.networkId = utils.deepAccess(rawBid, 'ext.dspid'); bid.meta.brandId = utils.deepAccess(rawBid, 'ext.advbrandid'); @@ -133,6 +182,143 @@ function isValidBidFloorParams(bidFloor, bidFloorCur) { bidFloorCur.match(curRegex)); } +/** + * Finds the impression with the associated id. + * + * @param {*} id Id of the impression. + * @param {array} impressions List of impressions sent in the request. + * @return {object} The impression with the associated id. + */ +function getBidRequest(id, impressions) { + if (!id) { + return; + } + return find(impressions, imp => imp.id === id); +} + +/** + * Builds a request object to be sent to the ad server based on bid requests. + * + * @param {array} validBidRequests A list of valid bid request config objects. + * @param {object} bidderRequest An object containing other info like gdprConsent. + * @param {array} impressions List of impression objects describing the bids. + * @param {array} version Endpoint version denoting banner or video. + * @return {object} Info describing the request to the server. + * + */ +function buildRequest(validBidRequests, bidderRequest, impressions, version) { + const userEids = []; + + // Always start by assuming the protocol is HTTPS. This way, it will work + // whether the page protocol is HTTP or HTTPS. Then check if the page is + // actually HTTP.If we can guarantee it is, then, and only then, set protocol to + // HTTP. + let baseUrl = SECURE_BID_URL; + + // RTI ids will be included in the bid request if the function getIdentityInfo() is loaded + // and if the data for the partner exist + if (window.headertag && typeof window.headertag.getIdentityInfo === 'function') { + let identityInfo = window.headertag.getIdentityInfo(); + if (identityInfo && typeof identityInfo === 'object') { + for (const partnerName in identityInfo) { + if (identityInfo.hasOwnProperty(partnerName)) { + let response = identityInfo[partnerName]; + if (!response.responsePending && response.data && typeof response.data === 'object' && Object.keys(response.data).length) { + userEids.push(response.data); + } + } + } + } + } + const r = {}; + + // Since bidderRequestId are the same for different bid request, just use the first one. + r.id = validBidRequests[0].bidderRequestId; + + r.imp = impressions; + + r.site = {}; + r.ext = {}; + r.ext.source = 'prebid'; + if (userEids.length > 0) { + r.user = {}; + r.user.eids = userEids; + } + + if (document.referrer && document.referrer !== '') { + r.site.ref = document.referrer; + } + + // Apply GDPR information to the request if GDPR is enabled. + if (bidderRequest) { + if (bidderRequest.gdprConsent) { + const gdprConsent = bidderRequest.gdprConsent; + + if (gdprConsent.hasOwnProperty('gdprApplies')) { + r.regs = { + ext: { + gdpr: gdprConsent.gdprApplies ? 1 : 0 + } + }; + } + + if (gdprConsent.hasOwnProperty('consentString')) { + r.user = r.user || {}; + r.user.ext = { + consent: gdprConsent.consentString || '' + }; + } + } + + if (bidderRequest.refererInfo) { + r.site.page = bidderRequest.refererInfo.referer; + + if (bidderRequest.refererInfo.referer && bidderRequest.refererInfo.referer.indexOf('https') !== 0) { + baseUrl = INSECURE_BID_URL; + } + } + } + + const payload = {}; + + // Parse additional runtime configs. + const otherIxConfig = config.getConfig('ix'); + if (otherIxConfig) { + // Append firstPartyData to r.site.page if firstPartyData exists. + if (typeof otherIxConfig.firstPartyData === 'object') { + const firstPartyData = otherIxConfig.firstPartyData; + let firstPartyString = '?'; + for (const key in firstPartyData) { + if (firstPartyData.hasOwnProperty(key)) { + firstPartyString += `${encodeURIComponent(key)}=${encodeURIComponent(firstPartyData[key])}&`; + } + } + firstPartyString = firstPartyString.slice(0, -1); + + r.site.page += firstPartyString; + } + + // Create t in payload if timeout is configured. + if (typeof otherIxConfig.timeout === 'number') { + payload.t = otherIxConfig.timeout; + } + } + + // Use the siteId in the first bid request as the main siteId. + payload.s = validBidRequests[0].params.siteId; + payload.v = version; + payload.r = JSON.stringify(r); + payload.ac = 'j'; + payload.sd = 1; + payload.nf = 1; + + return { + method: 'GET', + url: baseUrl, + data: payload + }; +} + export const spec = { code: BIDDER_CODE, @@ -146,22 +332,25 @@ export const spec = { */ isBidRequestValid: function (bid) { if (!isValidSize(bid.params.size)) { + utils.logError('ix bidder params: bid size has invalid format.'); return false; } if (!includesSize(bid.sizes, bid.params.size)) { + utils.logError('ix bidder params: bid size is not included in ad unit sizes.'); return false; } - if (bid.hasOwnProperty('mediaType') && bid.mediaType !== 'banner') { + if (bid.hasOwnProperty('mediaType') && !(utils.contains(SUPPORTED_AD_TYPES, bid.mediaType))) { return false; } - if (bid.hasOwnProperty('mediaTypes') && !utils.deepAccess(bid, 'mediaTypes.banner.sizes')) { + if (bid.hasOwnProperty('mediaTypes') && !(utils.deepAccess(bid, 'mediaTypes.banner.sizes') || utils.deepAccess(bid, 'mediaTypes.video.playerSize'))) { return false; } if (typeof bid.params.siteId !== 'string' && typeof bid.params.siteId !== 'number') { + utils.logError('ix bidder params: siteId must be string or number value.'); return false; } @@ -169,8 +358,10 @@ export const spec = { const hasBidFloorCur = bid.params.hasOwnProperty('bidFloorCur'); if (hasBidFloor || hasBidFloorCur) { - return hasBidFloor && hasBidFloorCur && - isValidBidFloorParams(bid.params.bidFloor, bid.params.bidFloorCur); + if (!(hasBidFloor && hasBidFloorCur && isValidBidFloorParams(bid.params.bidFloor, bid.params.bidFloorCur))) { + utils.logError('ix bidder params: bidFloor / bidFloorCur parameter has invalid format.'); + return false; + } } return true; @@ -180,139 +371,49 @@ export const spec = { * Make a server request from the list of BidRequests. * * @param {array} validBidRequests A list of valid bid request config objects. - * @param {object} options A object contains bids and other info like gdprConsent. + * @param {object} bidderRequest A object contains bids and other info like gdprConsent. * @return {object} Info describing the request to the server. */ - buildRequests: function (validBidRequests, options) { - const bannerImps = []; - const userEids = []; + buildRequests: function (validBidRequests, bidderRequest) { + let reqs = []; + let bannerImps = []; + let videoImps = []; let validBidRequest = null; - let bannerImp = null; - - // Always start by assuming the protocol is HTTPS. This way, it will work - // whether the page protocol is HTTP or HTTPS. Then check if the page is - // actually HTTP.If we can guarantee it is, then, and only then, set protocol to - // HTTP. - let baseUrl = BANNER_SECURE_BID_URL; for (let i = 0; i < validBidRequests.length; i++) { validBidRequest = validBidRequests[i]; - // Transform the bid request based on the banner format. - bannerImp = bidToBannerImp(validBidRequest); - bannerImps.push(bannerImp); - } - - // RTI ids will be included in the bid request if the function getIdentityInfo() is loaded - // and if the data for the partner exist - if (window.headertag && typeof window.headertag.getIdentityInfo === 'function') { - let identityInfo = window.headertag.getIdentityInfo(); - if (identityInfo && typeof identityInfo === 'object') { - for (const partnerName in identityInfo) { - if (identityInfo.hasOwnProperty(partnerName)) { - let response = identityInfo[partnerName]; - if (!response.responsePending && response.data && typeof response.data === 'object' && Object.keys(response.data).length) { - userEids.push(response.data); - } - } - } - } - } - const r = {}; - - // Since bidderRequestId are the same for different bid request, just use the first one. - r.id = validBidRequests[0].bidderRequestId; - - r.imp = bannerImps; - r.site = {}; - r.ext = {}; - r.ext.source = 'prebid'; - if (userEids.length > 0) { - r.user = {}; - r.user.eids = userEids; - } - - if (document.referrer && document.referrer !== '') { - r.site.ref = document.referrer; - } - - // Apply GDPR information to the request if GDPR is enabled. - if (options) { - if (options.gdprConsent) { - const gdprConsent = options.gdprConsent; - - if (gdprConsent.hasOwnProperty('gdprApplies')) { - r.regs = { - ext: { - gdpr: gdprConsent.gdprApplies ? 1 : 0 - } - }; - } - - if (gdprConsent.hasOwnProperty('consentString')) { - r.user = r.user || {}; - r.user.ext = { - consent: gdprConsent.consentString || '' - }; + if (validBidRequest.mediaType === VIDEO || utils.deepAccess(validBidRequest, 'mediaTypes.video')) { + if (validBidRequest.mediaType === VIDEO || includesSize(validBidRequest.mediaTypes.video.playerSize, validBidRequest.params.size)) { + videoImps.push(bidToVideoImp(validBidRequest)); + } else { + utils.logError('Bid size is not included in video playerSize') } } - - if (options.refererInfo) { - r.site.page = options.refererInfo.referer; - - if (options.refererInfo.referer && options.refererInfo.referer.indexOf('https') !== 0) { - baseUrl = BANNER_INSECURE_BID_URL; - } + if (validBidRequest.mediaType === BANNER || utils.deepAccess(validBidRequest, 'mediaTypes.banner') || + (!validBidRequest.mediaType && !validBidRequest.mediaTypes)) { + bannerImps.push(bidToBannerImp(validBidRequest)); } } - const payload = {}; - - // Parse additional runtime configs. - const otherIxConfig = config.getConfig('ix'); - if (otherIxConfig) { - // Append firstPartyData to r.site.page if firstPartyData exists. - if (typeof otherIxConfig.firstPartyData === 'object') { - const firstPartyData = otherIxConfig.firstPartyData; - let firstPartyString = '?'; - for (const key in firstPartyData) { - if (firstPartyData.hasOwnProperty(key)) { - firstPartyString += `${encodeURIComponent(key)}=${encodeURIComponent(firstPartyData[key])}&`; - } - } - firstPartyString = firstPartyString.slice(0, -1); - - r.site.page += firstPartyString; - } - - // Create t in payload if timeout is configured. - if (typeof otherIxConfig.timeout === 'number') { - payload.t = otherIxConfig.timeout; - } + if (bannerImps.length > 0) { + reqs.push(buildRequest(validBidRequests, bidderRequest, bannerImps, BANNER_ENDPOINT_VERSION)); + } + if (videoImps.length > 0) { + reqs.push(buildRequest(validBidRequests, bidderRequest, videoImps, VIDEO_ENDPOINT_VERSION)); } - // Use the siteId in the first bid request as the main siteId. - payload.s = validBidRequests[0].params.siteId; - - payload.v = ENDPOINT_VERSION; - payload.r = JSON.stringify(r); - payload.ac = 'j'; - payload.sd = 1; - - return { - method: 'GET', - url: baseUrl, - data: payload - }; + return reqs; }, /** * Unpack the response from the server into a list of bids. * * @param {object} serverResponse A successful response from the server. + * @param {object} bidderRequest The bid request sent to the server. * @return {array} An array of bids which were nested inside the server. */ - interpretResponse: function (serverResponse) { + interpretResponse: function (serverResponse, bidderRequest) { const bids = []; let bid = null; @@ -329,8 +430,11 @@ export const spec = { // Transform rawBid in bid response to the format that will be accepted by prebid. const innerBids = seatbid[i].bid; + let requestBid = JSON.parse(bidderRequest.data.r); + for (let j = 0; j < innerBids.length; j++) { - bid = parseBid(innerBids[j], responseBody.cur); + const bidRequest = getBidRequest(innerBids[j].impid, requestBid.imp); + bid = parseBid(innerBids[j], responseBody.cur, bidRequest); bids.push(bid); } } diff --git a/modules/ixBidAdapter.md b/modules/ixBidAdapter.md index e99c42408f2..7bd60ac413b 100644 --- a/modules/ixBidAdapter.md +++ b/modules/ixBidAdapter.md @@ -42,16 +42,21 @@ var adUnits = [{ ```javascript var adUnits = [{ // ... - mediaTypes: { banner: { sizes: [ [300, 250], [300, 600] ] + }, + video: { + context: 'instream', + playerSize: [ + [300, 250], + [300, 600] + ] } - } - + }, // ... }]; ``` @@ -61,7 +66,7 @@ var adUnits = [{ | Type | Support | --- | --- | Banner | Fully supported for all IX approved sizes. -| Video | Not supported. +| Video | Fully supported for all IX approved sizes. | Native | Not supported. # Bid Parameters @@ -76,6 +81,17 @@ object are detailed here. | siteId | Required | String | An IX-specific identifier that is associated with a specific size on this ad unit. This is similar to a placement ID or an ad unit ID that some other modules have. Examples: `'3723'`, `'6482'`, `'3639'` | size | Required | Number[] | The single size associated with the site ID. It should be one of the sizes listed in the ad unit under `adUnits[].sizes` or `adUnits[].mediaTypes.banner.sizes`. Examples: `[300, 250]`, `[300, 600]`, `[728, 90]` +### Video + +| Key | Scope | Type | Description +| --- | --- | --- | --- +| siteId | Required | String | An IX-specific identifier that is associated with a specific size on this ad unit. This is similar to a placement ID or an ad unit ID that some other modules have. Examples: `'3723'`, `'6482'`, `'3639'` +| size | Required | Number[] | The single size associated with the site ID. It should be one of the sizes listed in the ad unit under `adUnits[].sizes` or `adUnits[].mediaTypes.video.playerSize`. Examples: `[300, 250]`, `[300, 600]` +| video | Required | Hash | The video object will serve as the properties of the video ad. You can create any field under the video object that is mentioned in the `OpenRTB Spec v2.5`. Some fields like `mimes, protocols, minduration, maxduration` are required. +| video.mimes | Required | String[] | Array list of content MIME types supported. Popular MIME types include, but are not limited to, `"video/x-ms- wmv"` for Windows Media and `"video/x-flv"` for Flash Video. +|video.minduration| Required | Integer | Minimum video ad duration in seconds. +|video.maxduration| Required | Integer | Maximum video ad duration in seconds. +|video.protocol / video.protocols| Required | Integer / Integer[] | Either a single protocol provided as an integer, or protocols provided as a list of integers. `2` - VAST 2.0, `3` - VAST 3.0, `5` - VAST 2.0 Wrapper, `6` - VAST 3.0 Wrapper Setup Guide @@ -84,7 +100,9 @@ Setup Guide Follow these steps to configure and add the IX module to your Prebid.js integration. -The examples in this guide assume the following starting configuration: +The examples in this guide assume the following starting configuration (you may remove banner or video, if either does not apply). + +In regards to video, `context` can either be `'instream'` or `'outstream'`. Note that `outstream` requires additional configuration on the adUnit. ```javascript var adUnits = [{ @@ -98,6 +116,19 @@ var adUnits = [{ } }, bids: [] +}, +{ + code: 'video-div-a', + mediaTypes: { + video: { + context: 'instream', + playerSize: [ + [300, 250], + [300, 600] + ] + } + }, + bids: [] }]; ``` @@ -119,7 +150,9 @@ bid objects under `adUnits[].bids`: Set `params.siteId` and `params.size` in each bid object to the values provided by your IX representative. -**Example** +**Examples** + +**Banner:** ```javascript var adUnits = [{ code: 'banner-div-a', @@ -146,18 +179,94 @@ var adUnits = [{ }] }]; ``` - +**Video (Instream):** +```javascript +var adUnits = [{ + code: 'video-div-a', + mediaTypes: { + video: { + context: 'instream', + playerSize: [ + [300, 250], + [300, 600] + ] + } + }, + bids: [{ + bidder: 'ix', + params: { + siteId: '12345', + size: [300, 250], + video: { + skippable: false, + mimes: [ + 'video/mp4', + 'video/webm' + ], + minduration: 0, + maxduration: 60, + protocols: [6] + } + } + }, { + bidder: 'ix', + params: { + siteId: '12345', + size: [300, 600], + video: { + // openrtb v2.5 compatible video obj + } + } + }] +}]; +``` Please note that you can re-use the existing `siteId` within the same flex position. +**Video (Outstream):** +Note that currently, outstream video rendering must be configured by the publisher. In the adUnit, a `renderer` object must be defined, which includes a `url` pointing to the video rendering script, and a `render` function for creating the video player. See http://prebid.org/dev-docs/show-outstream-video-ads.html for more information. +```javascript +var adUnits = [{ + code: 'video-div-a', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[300, 250]] + } + }, + renderer: { + url: 'https://test.com/my-video-player.js', + render: function (bid) { + ... + } + }, + bids: [{ + bidder: 'ix', + params: { + siteId: '12345', + size: [300, 250], + video: { + skippable: false, + mimes: [ + 'video/mp4', + 'video/webm' + ], + minduration: 0, + maxduration: 60, + protocols: [6] + } + } + }] +}]; +``` ##### 2. Include `ixBidAdapter` in your build process -When running the build command, include `ixBidAdapter` as a module. +When running the build command, include `ixBidAdapter` as a module, as well as `dfpAdServerVideo` if you require video support. ``` -gulp build --modules=ixBidAdapter,fooBidAdapter,bazBidAdapter +gulp build --modules=ixBidAdapter,dfpAdServerVideo,fooBidAdapter,bazBidAdapter ``` If a JSON file is being used to specify the bidder modules, add `"ixBidAdapter"` @@ -166,6 +275,7 @@ to the top-level array in that file. ```json [ "ixBidAdapter", + "dfpAdServerVideo", "fooBidAdapter", "bazBidAdapter" ] diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 38e64e8d338..634d5041e6e 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -7,7 +7,8 @@ import { spec } from 'modules/ixBidAdapter'; describe('IndexexchangeAdapter', function () { const IX_INSECURE_ENDPOINT = 'http://as.casalemedia.com/cygnus'; const IX_SECURE_ENDPOINT = 'https://as-sec.casalemedia.com/cygnus'; - const BIDDER_VERSION = 7.2; + const VIDEO_ENDPOINT_VERSION = 8.1; + const BANNER_ENDPOINT_VERSION = 7.2; const DEFAULT_BANNER_VALID_BID = [ { @@ -29,17 +30,37 @@ describe('IndexexchangeAdapter', function () { auctionId: '1aa2bb3cc4dd' } ]; - const DEFAULT_BANNER_OPTION = { - gdprConsent: { - gdprApplies: true, - consentString: '3huaa11=qu3198ae', - vendorData: {} - }, - refererInfo: { - referer: 'http://www.prebid.org', - canonicalUrl: 'http://www.prebid.org/the/link/to/the/page' + + const DEFAULT_VIDEO_VALID_BID = [ + { + bidder: 'ix', + params: { + siteId: '456', + video: { + skippable: false, + mimes: [ + 'video/mp4', + 'video/webm' + ], + minduration: 0 + }, + size: [400, 100] + }, + sizes: [[400, 100], [200, 400]], + mediaTypes: { + video: { + context: 'instream', + playerSize: [[400, 100], [200, 400]] + } + }, + adUnitCode: 'div-gpt-ad-1460505748562-0', + transactionId: '173f49a8-7549-4218-a23c-e7ba59b47230', + bidId: '1a2b3c4e', + bidderRequestId: '11a22b33c44e', + auctionId: '1aa2bb3cc4de' } - }; + ]; + const DEFAULT_BANNER_BID_RESPONSE = { cur: 'USD', id: '11a22b33c44d', @@ -69,6 +90,48 @@ describe('IndexexchangeAdapter', function () { } ] }; + + const DEFAULT_VIDEO_BID_RESPONSE = { + cur: 'USD', + id: '1aa2bb3cc4de', + seatbid: [ + { + bid: [ + { + crid: '12346', + adomain: ['www.abcd.com'], + adid: '14851456', + impid: '1a2b3c4e', + cid: '3051267', + price: 110, + id: '2', + ext: { + vasturl: 'www.abcd.com/vast', + errorurl: 'www.abcd.com/error', + dspid: 51, + pricelevel: '_110', + advbrandid: 303326, + advbrand: 'OECTB' + } + } + ], + seat: '3971' + } + ] + }; + + const DEFAULT_OPTION = { + gdprConsent: { + gdprApplies: true, + consentString: '3huaa11=qu3198ae', + vendorData: {} + }, + refererInfo: { + referer: 'http://www.prebid.org', + canonicalUrl: 'http://www.prebid.org/the/link/to/the/page' + } + }; + const DEFAULT_IDENTITY_RESPONSE = { IdentityIp: { responsePending: false, @@ -83,6 +146,31 @@ describe('IndexexchangeAdapter', function () { } }; + const DEFAULT_BIDDER_REQUEST_DATA = { + ac: 'j', + r: JSON.stringify({ + id: '345', + imp: [ + { + id: '1a2b3c4e', + video: { + w: 640, + h: 480, + placement: 1 + } + } + ], + site: { + ref: 'http://ref.com/ref.html', + page: 'http://page.com' + }, + }), + s: '21', + sd: 1, + t: 1000, + v: 8.1 + }; + describe('inherited functions', function () { it('should exists and is a function', function () { const adapter = newBidder(spec); @@ -91,11 +179,12 @@ describe('IndexexchangeAdapter', function () { }); describe('isBidRequestValid', function () { - it('should return true when required params found for a banner ad', function () { + it('should return true when required params found for a banner or video ad', function () { expect(spec.isBidRequestValid(DEFAULT_BANNER_VALID_BID[0])).to.equal(true); + expect(spec.isBidRequestValid(DEFAULT_VIDEO_VALID_BID[0])).to.equal(true); }); - it('should return true when optional params found for a banner ad', function () { + it('should return true when optional bidFloor params found for an ad', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.bidFloor = 50; bid.params.bidFloorCur = 'USD'; @@ -136,10 +225,10 @@ describe('IndexexchangeAdapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when mediaTypes is not banner', function () { + it('should return false when mediaTypes is not banner or video', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.mediaTypes = { - video: { + native: { sizes: [[300, 250]] } }; @@ -156,19 +245,13 @@ describe('IndexexchangeAdapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when mediaType is not banner', function () { - const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); - delete bid.params.mediaTypes; - bid.mediaType = 'banne'; - bid.sizes = [[300, 250]]; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return false when mediaType is video', function () { - const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); - delete bid.params.mediaTypes; - bid.mediaType = 'video'; - bid.sizes = [[300, 250]]; + it('should return false when mediaTypes.video does not have sizes', function () { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); + bid.mediaTypes = { + video: { + size: [[300, 250]] + } + }; expect(spec.isBidRequestValid(bid)).to.equal(false); }); @@ -195,6 +278,14 @@ describe('IndexexchangeAdapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); + it('should return true when mediaType is video', function () { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); + delete bid.mediaTypes; + bid.mediaType = 'video'; + bid.sizes = [[400, 100]]; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('should return false when there is only bidFloor', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.bidFloor = 50; @@ -232,7 +323,7 @@ describe('IndexexchangeAdapter', function () { window.headertag.getIdentityInfo = function() { return testCopy; }; - request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; query = request.data; }); afterEach(function() { @@ -343,7 +434,7 @@ describe('IndexexchangeAdapter', function () { window.headertag.getIdentityInfo = function() { return undefined; }; - request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; query = request.data; const payload = JSON.parse(query.r); @@ -356,7 +447,7 @@ describe('IndexexchangeAdapter', function () { responsePending: true, data: {} } - request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; query = request.data; const payload = JSON.parse(query.r); @@ -366,7 +457,7 @@ describe('IndexexchangeAdapter', function () { it('payload should not have any user eids if identity data is pending for all partners', function () { testCopy.IdentityIp.responsePending = true; - request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; query = request.data; const payload = JSON.parse(query.r); @@ -377,7 +468,7 @@ describe('IndexexchangeAdapter', function () { it('payload should not have any user eids if identity data is pending or not available for all partners', function () { testCopy.IdentityIp.responsePending = false; testCopy.IdentityIp.data = {}; - request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; query = request.data; const payload = JSON.parse(query.r); @@ -387,8 +478,8 @@ describe('IndexexchangeAdapter', function () { }); }); - describe('buildRequestsBanner', function () { - const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + describe('buildRequests', function () { + const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; const requestUrl = request.url; const requestMethod = request.method; const query = request.data; @@ -396,7 +487,7 @@ describe('IndexexchangeAdapter', function () { const bidWithoutMediaType = utils.deepClone(DEFAULT_BANNER_VALID_BID); delete bidWithoutMediaType[0].mediaTypes; bidWithoutMediaType[0].sizes = [[300, 250], [300, 600]]; - const requestWithoutMediaType = spec.buildRequests(bidWithoutMediaType, DEFAULT_BANNER_OPTION); + const requestWithoutMediaType = spec.buildRequests(bidWithoutMediaType, DEFAULT_OPTION)[0]; const queryWithoutMediaType = requestWithoutMediaType.data; it('request should be made to IX endpoint with GET method', function () { @@ -405,11 +496,12 @@ describe('IndexexchangeAdapter', function () { }); it('query object (version, siteID and request) should be correct', function () { - expect(query.v).to.equal(BIDDER_VERSION); + expect(query.v).to.equal(BANNER_ENDPOINT_VERSION); expect(query.s).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId); expect(query.r).to.exist; expect(query.ac).to.equal('j'); expect(query.sd).to.equal(1); + expect(query.nf).to.equal(1); }); it('payload should have correct format and value', function () { @@ -417,7 +509,7 @@ describe('IndexexchangeAdapter', function () { expect(payload.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidderRequestId); expect(payload.site).to.exist; - expect(payload.site.page).to.equal(DEFAULT_BANNER_OPTION.refererInfo.referer); + expect(payload.site.page).to.equal(DEFAULT_OPTION.refererInfo.referer); expect(payload.site.ref).to.equal(document.referrer); expect(payload.ext).to.exist; expect(payload.ext.source).to.equal('prebid'); @@ -445,7 +537,7 @@ describe('IndexexchangeAdapter', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.bidFloor = 50; bid.params.bidFloorCur = 'USD'; - const requestBidFloor = spec.buildRequests([bid]); + const requestBidFloor = spec.buildRequests([bid])[0]; const impression = JSON.parse(requestBidFloor.data.r).imp[0]; expect(impression.bidfloor).to.equal(bid.params.bidFloor); @@ -457,7 +549,7 @@ describe('IndexexchangeAdapter', function () { expect(payload.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidderRequestId); expect(payload.site).to.exist; - expect(payload.site.page).to.equal(DEFAULT_BANNER_OPTION.refererInfo.referer); + expect(payload.site.page).to.equal(DEFAULT_OPTION.refererInfo.referer); expect(payload.site.ref).to.equal(document.referrer); expect(payload.ext).to.exist; expect(payload.ext.source).to.equal('prebid'); @@ -484,7 +576,7 @@ describe('IndexexchangeAdapter', function () { it('impression should have sid if id is configured as number', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.id = 50; - const requestBidFloor = spec.buildRequests([bid]); + const requestBidFloor = spec.buildRequests([bid])[0]; const impression = JSON.parse(requestBidFloor.data.r).imp[0]; expect(impression.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); @@ -501,7 +593,7 @@ describe('IndexexchangeAdapter', function () { it('impression should have sid if id is configured as string', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.id = 'abc'; - const requestBidFloor = spec.buildRequests([bid]); + const requestBidFloor = spec.buildRequests([bid])[0]; const impression = JSON.parse(requestBidFloor.data.r).imp[0]; expect(impression.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); expect(impression.banner).to.exist; @@ -526,9 +618,9 @@ describe('IndexexchangeAdapter', function () { } }); - const requestWithFirstPartyData = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + const requestWithFirstPartyData = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; const pageUrl = JSON.parse(requestWithFirstPartyData.data.r).site.page; - const expectedPageUrl = DEFAULT_BANNER_OPTION.refererInfo.referer + '?ab=123&cd=123%23ab&e%2Ff=456&h%3Fg=456%23cd'; + const expectedPageUrl = DEFAULT_OPTION.refererInfo.referer + '?ab=123&cd=123%23ab&e%2Ff=456&h%3Fg=456%23cd'; expect(pageUrl).to.equal(expectedPageUrl); }); @@ -540,10 +632,10 @@ describe('IndexexchangeAdapter', function () { } }); - const requestFirstPartyDataNumber = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + const requestFirstPartyDataNumber = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; const pageUrl = JSON.parse(requestFirstPartyDataNumber.data.r).site.page; - expect(pageUrl).to.equal(DEFAULT_BANNER_OPTION.refererInfo.referer); + expect(pageUrl).to.equal(DEFAULT_OPTION.refererInfo.referer); }); it('should not set first party or timeout if it is not present', function () { @@ -551,18 +643,18 @@ describe('IndexexchangeAdapter', function () { ix: {} }); - const requestWithoutConfig = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + const requestWithoutConfig = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; const pageUrl = JSON.parse(requestWithoutConfig.data.r).site.page; - expect(pageUrl).to.equal(DEFAULT_BANNER_OPTION.refererInfo.referer); + expect(pageUrl).to.equal(DEFAULT_OPTION.refererInfo.referer); expect(requestWithoutConfig.data.t).to.be.undefined; }); it('should not set first party or timeout if it is setConfig is not called', function () { - const requestWithoutConfig = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); + const requestWithoutConfig = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0]; const pageUrl = JSON.parse(requestWithoutConfig.data.r).site.page; - expect(pageUrl).to.equal(DEFAULT_BANNER_OPTION.refererInfo.referer); + expect(pageUrl).to.equal(DEFAULT_OPTION.refererInfo.referer); expect(requestWithoutConfig.data.t).to.be.undefined; }); @@ -572,7 +664,7 @@ describe('IndexexchangeAdapter', function () { timeout: 500 } }); - const requestWithTimeout = spec.buildRequests(DEFAULT_BANNER_VALID_BID); + const requestWithTimeout = spec.buildRequests(DEFAULT_BANNER_VALID_BID)[0]; expect(requestWithTimeout.data.t).to.equal(500); }); @@ -583,14 +675,98 @@ describe('IndexexchangeAdapter', function () { timeout: '500' } }); - const requestStringTimeout = spec.buildRequests(DEFAULT_BANNER_VALID_BID); + const requestStringTimeout = spec.buildRequests(DEFAULT_BANNER_VALID_BID)[0]; expect(requestStringTimeout.data.t).to.be.undefined; }); + + it('request should contain both banner and video requests', function () { + const request = spec.buildRequests([DEFAULT_BANNER_VALID_BID[0], DEFAULT_VIDEO_VALID_BID[0]]); + + const bannerImp = JSON.parse(request[0].data.r).imp[0]; + expect(JSON.parse(request[0].data.v)).to.equal(BANNER_ENDPOINT_VERSION); + expect(bannerImp.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); + expect(bannerImp.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId); + expect(bannerImp.banner).to.exist; + expect(bannerImp.banner.w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]); + expect(bannerImp.banner.h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]); + + const videoImp = JSON.parse(request[1].data.r).imp[0]; + expect(JSON.parse(request[1].data.v)).to.equal(VIDEO_ENDPOINT_VERSION); + expect(videoImp.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); + expect(videoImp.video).to.exist; + expect(videoImp.video.w).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[0]); + expect(videoImp.video.h).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[1]); + }); }); - describe('interpretResponseBanner', function () { - it('should get correct bid response', function () { + describe('buildRequestVideo', function () { + const request = spec.buildRequests(DEFAULT_VIDEO_VALID_BID, DEFAULT_OPTION); + const query = request[0].data; + + it('query object (version, siteID and request) should be correct', function () { + expect(query.v).to.equal(VIDEO_ENDPOINT_VERSION); + expect(query.s).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.siteId); + expect(query.r).to.exist; + expect(query.ac).to.equal('j'); + expect(query.sd).to.equal(1); + }); + + it('impression should have correct format and value', function () { + const impression = JSON.parse(query.r).imp[0]; + const sidValue = `${DEFAULT_VIDEO_VALID_BID[0].params.size[0].toString()}x${DEFAULT_VIDEO_VALID_BID[0].params.size[1].toString()}`; + + expect(impression.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); + expect(impression.video).to.exist; + expect(impression.video.w).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[0]); + expect(impression.video.h).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[1]); + expect(impression.video.placement).to.exist; + expect(impression.video.placement).to.equal(1); + expect(impression.video.minduration).to.exist; + expect(impression.video.minduration).to.equal(0); + expect(impression.video.mimes).to.exist; + expect(impression.video.mimes[0]).to.equal('video/mp4'); + expect(impression.video.mimes[1]).to.equal('video/webm'); + + expect(impression.video.skippable).to.equal(false); + expect(impression.ext).to.exist; + expect(impression.ext.siteID).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.siteId.toString()); + expect(impression.ext.sid).to.equal(sidValue); + }); + + it('impression should have correct format when mediaType is specified.', function () { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); + delete bid.mediaTypes; + bid.mediaType = 'video'; + const requestBidFloor = spec.buildRequests([bid])[0]; + const impression = JSON.parse(requestBidFloor.data.r).imp[0]; + const sidValue = `${DEFAULT_VIDEO_VALID_BID[0].params.size[0].toString()}x${DEFAULT_VIDEO_VALID_BID[0].params.size[1].toString()}`; + + expect(impression.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); + expect(impression.video).to.exist; + expect(impression.video.w).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[0]); + expect(impression.video.h).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.size[1]); + expect(impression.video.placement).to.not.exist; + expect(impression.ext).to.exist; + expect(impression.ext.siteID).to.equal(DEFAULT_VIDEO_VALID_BID[0].params.siteId.toString()); + expect(impression.ext.sid).to.equal(sidValue); + }); + + it('should set correct placement if context is outstream', function () { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); + bid.mediaTypes.video.context = 'outstream'; + const request = spec.buildRequests([bid])[0]; + const impression = JSON.parse(request.data.r).imp[0]; + + expect(impression.id).to.equal(DEFAULT_VIDEO_VALID_BID[0].bidId); + expect(impression.video).to.exist; + expect(impression.video.placement).to.exist; + expect(impression.video.placement).to.equal(4); + }); + }); + + describe('interpretResponse', function () { + it('should get correct bid response for banner ad', function () { const expectedParse = [ { requestId: '1a2b3c4d', @@ -598,6 +774,7 @@ describe('IndexexchangeAdapter', function () { creativeId: '12345', width: 300, height: 250, + mediaType: 'banner', ad: '
    ', currency: 'USD', ttl: 35, @@ -610,7 +787,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE }); + const result = spec.interpretResponse({ body: DEFAULT_BANNER_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA }); expect(result[0]).to.deep.equal(expectedParse[0]); }); @@ -624,6 +801,7 @@ describe('IndexexchangeAdapter', function () { creativeId: '-', width: 300, height: 250, + mediaType: 'banner', ad: '', currency: 'USD', ttl: 35, @@ -636,8 +814,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: bidResponse }); - expect(result[0]).to.deep.equal(expectedParse[0]); + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); }); it('should set Japanese price correctly', function () { @@ -650,6 +827,7 @@ describe('IndexexchangeAdapter', function () { creativeId: '12345', width: 300, height: 250, + mediaType: 'banner', ad: '', currency: 'JPY', ttl: 35, @@ -662,7 +840,7 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: bidResponse }); + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); expect(result[0]).to.deep.equal(expectedParse[0]); }); @@ -676,6 +854,7 @@ describe('IndexexchangeAdapter', function () { creativeId: '12345', width: 300, height: 250, + mediaType: 'banner', ad: '', currency: 'USD', ttl: 35, @@ -688,13 +867,38 @@ describe('IndexexchangeAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: bidResponse }); + const result = spec.interpretResponse({ body: bidResponse }, { data: DEFAULT_BIDDER_REQUEST_DATA }); + expect(result[0]).to.deep.equal(expectedParse[0]); + }); + + it('should get correct bid response for video ad', function () { + const expectedParse = [ + { + requestId: '1a2b3c4e', + cpm: 1.1, + creativeId: '12346', + mediaType: 'video', + width: 640, + height: 480, + currency: 'USD', + ttl: 3600, + netRevenue: true, + dealId: undefined, + vastUrl: 'www.abcd.com/vast', + meta: { + networkId: 51, + brandId: 303326, + brandName: 'OECTB' + } + } + ]; + const result = spec.interpretResponse({ body: DEFAULT_VIDEO_BID_RESPONSE }, { data: DEFAULT_BIDDER_REQUEST_DATA }); expect(result[0]).to.deep.equal(expectedParse[0]); }); it('bidrequest should have consent info if gdprApplies and consentString exist', function () { - const validBidWithConsent = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_BANNER_OPTION); - const requestWithConsent = JSON.parse(validBidWithConsent.data.r); + const validBidWithConsent = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION); + const requestWithConsent = JSON.parse(validBidWithConsent[0].data.r); expect(requestWithConsent.regs.ext.gdpr).to.equal(1); expect(requestWithConsent.user.ext.consent).to.equal('3huaa11=qu3198ae'); @@ -708,7 +912,7 @@ describe('IndexexchangeAdapter', function () { } }; const validBidWithConsent = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); - const requestWithConsent = JSON.parse(validBidWithConsent.data.r); + const requestWithConsent = JSON.parse(validBidWithConsent[0].data.r); expect(requestWithConsent.regs.ext.gdpr).to.equal(1); expect(requestWithConsent.user).to.be.undefined; @@ -722,7 +926,7 @@ describe('IndexexchangeAdapter', function () { } }; const validBidWithConsent = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); - const requestWithConsent = JSON.parse(validBidWithConsent.data.r); + const requestWithConsent = JSON.parse(validBidWithConsent[0].data.r); expect(requestWithConsent.regs).to.be.undefined; expect(requestWithConsent.user.ext.consent).to.equal('3huaa11=qu3198ae'); @@ -731,7 +935,7 @@ describe('IndexexchangeAdapter', function () { it('bidrequest should not have consent info if options.gdprConsent is undefined', function () { const options = {}; const validBidWithConsent = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); - const requestWithConsent = JSON.parse(validBidWithConsent.data.r); + const requestWithConsent = JSON.parse(validBidWithConsent[0].data.r); expect(requestWithConsent.regs).to.be.undefined; expect(requestWithConsent.user).to.be.undefined; @@ -740,10 +944,10 @@ describe('IndexexchangeAdapter', function () { it('bidrequest should not have page if options is undefined', function () { const options = {}; const validBidWithoutreferInfo = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); - const requestWithoutreferInfo = JSON.parse(validBidWithoutreferInfo.data.r); + const requestWithoutreferInfo = JSON.parse(validBidWithoutreferInfo[0].data.r); expect(requestWithoutreferInfo.site.page).to.be.undefined; - expect(validBidWithoutreferInfo.url).to.equal(IX_SECURE_ENDPOINT); + expect(validBidWithoutreferInfo[0].url).to.equal(IX_SECURE_ENDPOINT); }); it('bidrequest should not have page if options.refererInfo is an empty object', function () { @@ -751,10 +955,10 @@ describe('IndexexchangeAdapter', function () { refererInfo: {} }; const validBidWithoutreferInfo = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); - const requestWithoutreferInfo = JSON.parse(validBidWithoutreferInfo.data.r); + const requestWithoutreferInfo = JSON.parse(validBidWithoutreferInfo[0].data.r); expect(requestWithoutreferInfo.site.page).to.be.undefined; - expect(validBidWithoutreferInfo.url).to.equal(IX_SECURE_ENDPOINT); + expect(validBidWithoutreferInfo[0].url).to.equal(IX_SECURE_ENDPOINT); }); it('bidrequest should sent to secure endpoint if page url is secure', function () { @@ -764,10 +968,10 @@ describe('IndexexchangeAdapter', function () { } }; const validBidWithoutreferInfo = spec.buildRequests(DEFAULT_BANNER_VALID_BID, options); - const requestWithoutreferInfo = JSON.parse(validBidWithoutreferInfo.data.r); + const requestWithoutreferInfo = JSON.parse(validBidWithoutreferInfo[0].data.r); expect(requestWithoutreferInfo.site.page).to.equal(options.refererInfo.referer); - expect(validBidWithoutreferInfo.url).to.equal(IX_SECURE_ENDPOINT); + expect(validBidWithoutreferInfo[0].url).to.equal(IX_SECURE_ENDPOINT); }); }); }); From 8e198bb41e3e10aaa5b8ec5b3e63a50e5704651d Mon Sep 17 00:00:00 2001 From: Mirko Feddern Date: Tue, 25 Jun 2019 20:14:11 +0200 Subject: [PATCH 143/146] Add Outstream Renderer for Yieldlab Adapter (#3910) * Add Outstream Renderer * Fix playerSize overwrite Prebid is translating the playerSize to an array of arrays, so we have to return accordingly --- modules/yieldlabBidAdapter.js | 53 +++++++++++++++++++- modules/yieldlabBidAdapter.md | 2 +- test/spec/modules/yieldlabBidAdapter_spec.js | 17 +++++++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index 1bbb3f11a2e..116f1aae0a8 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -2,11 +2,13 @@ import * as utils from '../src/utils' import { registerBidder } from '../src/adapters/bidderFactory' import find from 'core-js/library/fn/array/find' import { VIDEO, BANNER } from '../src/mediaTypes' +import { Renderer } from 'src/Renderer' const ENDPOINT = 'https://ad.yieldlab.net' const BIDDER_CODE = 'yieldlab' const BID_RESPONSE_TTL_SEC = 300 const CURRENCY_CODE = 'EUR' +const OUTSTREAMPLAYER_URL = 'https://ad2.movad.net/dynamic.ad?a=o193092&ma_loadEvent=ma-start-event' export const spec = { code: BIDDER_CODE, @@ -93,8 +95,23 @@ export const spec = { } if (isVideo(bidRequest)) { + const playersize = getPlayerSize(bidRequest) + if (playersize) { + bidResponse.width = playersize[0] + bidResponse.height = playersize[1] + } bidResponse.mediaType = VIDEO bidResponse.vastUrl = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/${customsize[0]}x${customsize[1]}?ts=${timestamp}${extId}` + + if (isOutstream(bidRequest)) { + const renderer = Renderer.install({ + id: bidRequest.bidId, + url: OUTSTREAMPLAYER_URL, + loaded: false + }) + renderer.setRender(outstreamRender) + bidResponse.renderer = renderer + } } bidResponses.push(bidResponse) @@ -106,13 +123,33 @@ export const spec = { /** * Is this a video format? - * @param {String} format + * @param {Object} format * @returns {Boolean} */ function isVideo (format) { return utils.deepAccess(format, 'mediaTypes.video') } +/** + * Is this an outstream context? + * @param {Object} format + * @returns {Boolean} + */ +function isOutstream (format) { + let context = utils.deepAccess(format, 'mediaTypes.video.context') + return (context === 'outstream') +} + +/** + * Gets optional player size + * @param {Object} format + * @returns {Array} + */ +function getPlayerSize (format) { + let playerSize = utils.deepAccess(format, 'mediaTypes.video.playerSize') + return (playerSize && utils.isArray(playerSize[0])) ? playerSize[0] : playerSize +} + /** * Expands a 'WxH' string as a 2-element [W, H] array * @param {String} size @@ -137,4 +174,18 @@ function createQueryString (obj) { return str.join('&') } +/** + * Handles an outstream response after the library is loaded + * @param {Object} bid + */ +function outstreamRender(bid) { + bid.renderer.push(() => { + window.ma_width = bid.width + window.ma_height = bid.height + window.ma_vastUrl = bid.vastUrl + window.ma_container = bid.adUnitCode + window.document.dispatchEvent(new Event('ma-start-event')) + }); +} + registerBidder(spec) diff --git a/modules/yieldlabBidAdapter.md b/modules/yieldlabBidAdapter.md index de93baf42ae..37897b83f12 100644 --- a/modules/yieldlabBidAdapter.md +++ b/modules/yieldlabBidAdapter.md @@ -34,7 +34,7 @@ Module that connects to Yieldlab's demand sources sizes: [[640, 480]], mediaTypes: { video: { - context: "instream" + context: "instream" // or "outstream" } }, bids: [{ diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index c2e12408cdd..c8709969e00 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -148,5 +148,22 @@ describe('yieldlabBidAdapter', function () { expect(result[0].vastUrl).to.include('https://ad.yieldlab.net/d/1111/2222/728x90?ts=') expect(result[0].vastUrl).to.include('&id=abc') }) + + it('should add renderer if outstream context', function () { + const OUTSTREAM_REQUEST = Object.assign({}, REQUEST, { + 'mediaTypes': { + 'video': { + 'playerSize': [[640, 480]], + 'context': 'outstream' + } + } + }) + const result = spec.interpretResponse({body: [RESPONSE]}, {validBidRequests: [OUTSTREAM_REQUEST]}) + + expect(result[0].renderer.id).to.equal('2d925f27f5079f') + expect(result[0].renderer.url).to.equal('https://ad2.movad.net/dynamic.ad?a=o193092&ma_loadEvent=ma-start-event') + expect(result[0].width).to.equal(640) + expect(result[0].height).to.equal(480) + }) }) }) From 64a258a7d2b29198cd16b946c780ad0c82670552 Mon Sep 17 00:00:00 2001 From: msm0504 <51493331+msm0504@users.noreply.github.com> Date: Tue, 25 Jun 2019 14:44:32 -0400 Subject: [PATCH 144/146] Standardized COPPA support (#3936) * Add microadBidAdapter * Remove unnecessary encodeURIComponent from microadBidAdapter * Submit Advangelists Prebid Adapter * Submit Advangelists Prebid Adapter 1.1 * Correct procudtion endpoint for prebid * Send coppa flag on requests to OpenRTB from Prebid server * Support coppa flag being set in Prebid config * Add unit tests for deepSetValue util function --- modules/arteebeeBidAdapter.js | 2 +- modules/openxBidAdapter.js | 2 +- modules/openxoutstreamBidAdapter.js | 2 +- modules/prebidServerBidAdapter/index.js | 4 ++++ modules/rubiconBidAdapter.js | 8 ++++++++ test/spec/utils_spec.js | 23 +++++++++++++++++++++++ 6 files changed, 38 insertions(+), 3 deletions(-) diff --git a/modules/arteebeeBidAdapter.js b/modules/arteebeeBidAdapter.js index ddf728a143e..74d5d5d3d52 100644 --- a/modules/arteebeeBidAdapter.js +++ b/modules/arteebeeBidAdapter.js @@ -96,7 +96,7 @@ function makeRtbRequest(req, bidderRequest) { 'tmax': config.getConfig('bidderTimeout') }; - if (req.params.coppa) { + if (config.getConfig('coppa') === true || req.params.coppa) { rtbReq.regs = {coppa: 1}; } diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index 8236be8c2e5..ef60d6e1856 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -262,7 +262,7 @@ function buildOXBannerRequest(bids, bidderRequest) { queryParams.ns = 1; } - if (bids.some(bid => bid.params.coppa)) { + if (config.getConfig('coppa') === true || bids.some(bid => bid.params.coppa)) { queryParams.tfcd = 1; } diff --git a/modules/openxoutstreamBidAdapter.js b/modules/openxoutstreamBidAdapter.js index 42c7a3fff32..9011a949e7b 100644 --- a/modules/openxoutstreamBidAdapter.js +++ b/modules/openxoutstreamBidAdapter.js @@ -114,7 +114,7 @@ function buildOXBannerRequest(bid, bidderRequest) { queryParams.ns = 1; } - if (bid.params.coppa) { + if (config.getConfig('coppa') === true || bid.params.coppa) { queryParams.tfcd = 1; } diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 028f02d9662..4ac1bccaeda 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -580,6 +580,10 @@ const OPEN_RTB_PROTOCOL = { utils.deepSetValue(request, 'user.ext.consent', bidRequests[0].gdprConsent.consentString); } + if (getConfig('coppa') === true) { + utils.deepSetValue(request, 'regs.coppa', 1); + } + return request; }, diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index abeebd2e1b2..c3d0b48f14b 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -233,6 +233,10 @@ export const spec = { } } + if (config.getConfig('coppa') === true) { + utils.deepSetValue(request, 'regs.coppa', 1); + } + return { method: 'POST', url: VIDEO_ENDPOINT, @@ -434,6 +438,10 @@ export const spec = { const digitrustParams = _getDigiTrustQueryParams(bidRequest, 'FASTLANE'); Object.assign(data, digitrustParams); + if (config.getConfig('coppa') === true) { + data['coppa'] = 1; + } + return data; }, diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js index df1c9b66b28..ff9b6ec2371 100755 --- a/test/spec/utils_spec.js +++ b/test/spec/utils_spec.js @@ -626,6 +626,29 @@ describe('Utils', function () { }); }); + describe('deepSetValue', function() { + it('should set existing properties at various depths', function() { + const testObj = { + prop: 'value', + nestedObj: { + nestedProp: 'nestedValue' + } + }; + utils.deepSetValue(testObj, 'prop', 'newValue'); + assert.equal(testObj.prop, 'newValue'); + utils.deepSetValue(testObj, 'nestedObj.nestedProp', 'newNestedValue'); + assert.equal(testObj.nestedObj.nestedProp, 'newNestedValue'); + }); + + it('should create object levels between top and bottom of given path if they do not exist', function() { + const testObj = {}; + utils.deepSetValue(testObj, 'level1.level2', 'value'); + assert.notEqual(testObj.level1, undefined); + assert.notEqual(testObj.level1.level2, undefined); + assert.equal(testObj.level1.level2, 'value'); + }); + }); + describe('createContentToExecuteExtScriptInFriendlyFrame', function () { it('should return empty string if url is not passed', function () { var output = utils.createContentToExecuteExtScriptInFriendlyFrame(); From e55684b0d7cf61dfb817022978d3ca5561d4b4da Mon Sep 17 00:00:00 2001 From: Jaimin Panchal <7393273+jaiminpanchal27@users.noreply.github.com> Date: Tue, 25 Jun 2019 15:12:04 -0400 Subject: [PATCH 145/146] Adding privacy_supported flag (#3943) --- modules/appnexusBidAdapter.js | 4 ++++ test/spec/modules/appnexusBidAdapter_spec.js | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index e0ae19187b2..50c5a0e6f04 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -686,6 +686,10 @@ function buildNativeRequest(params) { request[requestKey].sizes = transformSizes(request[requestKey].sizes); } } + + if (requestKey === NATIVE_MAPPING.privacyLink) { + request.privacy_supported = true; + } }); return request; diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 9e37f6cbffb..e55e3e32029 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -432,7 +432,8 @@ describe('AppNexusAdapter', function () { likes: {required: true}, phone: {required: true}, price: {required: true}, - saleprice: {required: true} + saleprice: {required: true}, + privacy_supported: true }); }); From 5e1d88986acdc902a0b0a919ad33db2d61fac30f Mon Sep 17 00:00:00 2001 From: Eric Harper Date: Tue, 25 Jun 2019 15:47:00 -0400 Subject: [PATCH 146/146] Prebid 2.21.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a4e2985e1bd..6c0d2ace797 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "2.21.0-pre", + "version": "2.21.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": {