From 66b0eef4cc915cd0bf96506be067aa1e86985c2a Mon Sep 17 00:00:00 2001 From: jrosendahl Date: Fri, 5 Oct 2018 15:56:46 -0600 Subject: [PATCH 1/5] Updated Bid Adaptor for MultiSized and added Error Call Home --- modules/sovrnBidAdapter.js | 202 +++++++++++++--------- test/spec/modules/sovrnBidAdapter_spec.js | 39 ++++- 2 files changed, 160 insertions(+), 81 deletions(-) diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index 4f1eb298794..5d6227dd69d 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -1,7 +1,9 @@ -import * as utils from 'src/utils'; -import { registerBidder } from 'src/adapters/bidderFactory'; -import { BANNER } from 'src/mediaTypes'; -import { REPO_AND_VERSION } from 'src/constants'; +import * as utils from 'src/utils' +import { registerBidder } from 'src/adapters/bidderFactory' +import { BANNER } from 'src/mediaTypes' +import { REPO_AND_VERSION } from 'src/constants' +import { ajax } from 'src/ajax' +const errorUrl = 'https://pcb.aws.lijit.com/collect' export const spec = { code: 'sovrn', @@ -22,48 +24,59 @@ export const spec = { * @return object of parameters for Prebid AJAX request */ buildRequests: function(bidReqs, bidderRequest) { - const loc = utils.getTopWindowLocation(); - let sovrnImps = []; - let iv; - utils._each(bidReqs, function (bid) { - iv = iv || utils.getBidIdParameter('iv', bid.params); - sovrnImps.push({ - id: bid.bidId, - banner: { w: 1, h: 1 }, - tagid: String(utils.getBidIdParameter('tagid', bid.params)), - bidfloor: utils.getBidIdParameter('bidfloor', bid.params) + try { + const loc = utils.getTopWindowLocation(); + let sovrnImps = []; + let iv; + utils._each(bidReqs, function (bid) { + iv = iv || utils.getBidIdParameter('iv', bid.params); + 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)})) + sovrnImps.push({ + id: bid.bidId, + banner: { + format: processedSizes, + w: 1, + h: 1, + }, + tagid: String(utils.getBidIdParameter('tagid', bid.params)), + bidfloor: utils.getBidIdParameter('bidfloor', bid.params) + }); }); - }); - const sovrnBidReq = { - id: utils.getUniqueIdentifierStr(), - imp: sovrnImps, - site: { - domain: loc.host, - page: loc.host + loc.pathname + loc.search + loc.hash - } - }; + const sovrnBidReq = { + id: utils.getUniqueIdentifierStr(), + imp: sovrnImps, + site: { + domain: loc.host, + page: loc.host + loc.pathname + loc.search + loc.hash + } + }; - if (bidderRequest && bidderRequest.gdprConsent) { - sovrnBidReq.regs = { - ext: { - gdpr: +bidderRequest.gdprConsent.gdprApplies - }}; - sovrnBidReq.user = { - ext: { - consent: bidderRequest.gdprConsent.consentString - }}; - } + if (bidderRequest && bidderRequest.gdprConsent) { + sovrnBidReq.regs = { + ext: { + gdpr: +bidderRequest.gdprConsent.gdprApplies + }}; + sovrnBidReq.user = { + ext: { + consent: bidderRequest.gdprConsent.consentString + }}; + } - let url = `//ap.lijit.com/rtb/bid?` + - `src=${REPO_AND_VERSION}`; - if (iv) url += `&iv=${iv}`; + let url = `//ap.lijit.com/rtb/bid?` + + `src=${REPO_AND_VERSION}`; + if (iv) url += `&iv=${iv}`; - return { - method: 'POST', - url: url, - data: JSON.stringify(sovrnBidReq), - options: {contentType: 'text/plain'} - }; + return { + method: 'POST', + url: url, + data: JSON.stringify(sovrnBidReq), + options: {contentType: 'text/plain'} + } + } catch (e) { + new LogError(e, {bidReqs, bidderRequest}).send() + } }, /** @@ -72,48 +85,83 @@ export const spec = { * @return {Bid[]} An array of formatted bids. */ interpretResponse: function({ body: {id, seatbid} }) { - let sovrnBidResponses = []; - if (id && - seatbid && - seatbid.length > 0 && - seatbid[0].bid && - seatbid[0].bid.length > 0) { - seatbid[0].bid.map(sovrnBid => { - sovrnBidResponses.push({ - requestId: sovrnBid.impid, - cpm: parseFloat(sovrnBid.price), - width: parseInt(sovrnBid.w), - height: parseInt(sovrnBid.h), - creativeId: sovrnBid.crid || sovrnBid.id, - dealId: sovrnBid.dealid || null, - currency: 'USD', - netRevenue: true, - mediaType: BANNER, - ad: decodeURIComponent(`${sovrnBid.adm}`), - ttl: 60 + try { + let sovrnBidResponses = []; + if (id && + seatbid && + seatbid.length > 0 && + seatbid[0].bid && + seatbid[0].bid.length > 0) { + seatbid[0].bid.map(sovrnBid => { + sovrnBidResponses.push({ + requestId: sovrnBid.impid, + cpm: parseFloat(sovrnBid.price), + width: parseInt(sovrnBid.w), + height: parseInt(sovrnBid.h), + creativeId: sovrnBid.crid || sovrnBid.id, + dealId: sovrnBid.dealid || null, + currency: 'USD', + netRevenue: true, + mediaType: BANNER, + ad: decodeURIComponent(`${sovrnBid.adm}`), + ttl: 60 + }); }); - }); + } + return sovrnBidResponses + } catch (e) { + new LogError(e, {id, seatbid}).send() } - return sovrnBidResponses; }, getUserSyncs: function(syncOptions, serverResponses, gdprConsent) { - if (serverResponses && serverResponses.length !== 0 && syncOptions.iframeEnabled) { - let iidArr = serverResponses.filter(rsp => rsp.body && rsp.body.ext && rsp.body.ext.iid) - .map(rsp => { return rsp.body.ext.iid }); - let consentString = ''; - if (gdprConsent && gdprConsent.gdprApplies && typeof gdprConsent.consentString === 'string') { - consentString = gdprConsent.consentString - } - if (iidArr[0]) { - return [{ - type: 'iframe', - url: '//ap.lijit.com/beacon?informer=' + iidArr[0] + '&gdpr_consent=' + consentString, - }]; + try { + if (serverResponses && serverResponses.length !== 0 && syncOptions.iframeEnabled) { + let iidArr = serverResponses.filter(rsp => rsp.body && rsp.body.ext && rsp.body.ext.iid) + .map(rsp => { return rsp.body.ext.iid }); + let consentString = ''; + if (gdprConsent && gdprConsent.gdprApplies && typeof gdprConsent.consentString === 'string') { + consentString = gdprConsent.consentString + } + if (iidArr[0]) { + return [{ + type: 'iframe', + url: '//ap.lijit.com/beacon?informer=' + iidArr[0] + '&gdpr_consent=' + consentString, + }]; + } } + return [] + } catch (e) { + new LogError(e, {syncOptions, serverResponses, gdprConsent}).send() } - return []; }, -}; +} + +class LogError { + constructor(e, data) { + utils.logError(e) + this.error = {} + this.error.payload = 'error' + this.error.ts = utils.timestamp() + this.error.message = e.message + this.error.data = data + this.error.source = 'sovrnBidAdapter' + this.error.stack = e.stack + this.error.prebidVersion = $$PREBID_GLOBAL$$.version + this.error.url = utils.getTopWindowLocation().href + this.error.userAgent = navigator.userAgent + } + send() { + ajax( + errorUrl, + null, + JSON.stringify(this.error), + { + contentType: 'application/json', + method: 'POST', + } + ) + } +} registerBidder(spec); diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js index 22c93505ecf..ce31b9e1771 100644 --- a/test/spec/modules/sovrnBidAdapter_spec.js +++ b/test/spec/modules/sovrnBidAdapter_spec.js @@ -16,7 +16,8 @@ describe('sovrnBidAdapter', function() { }, 'adUnitCode': 'adunit-code', 'sizes': [ - [300, 250] + [300, 250], + [300, 600] ], 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', @@ -47,7 +48,8 @@ describe('sovrnBidAdapter', function() { }, 'adUnitCode': 'adunit-code', 'sizes': [ - [300, 250] + [300, 250], + [300, 600] ], 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', @@ -64,6 +66,33 @@ describe('sovrnBidAdapter', function() { expect(request.url).to.equal(ENDPOINT) }); + it('sets the proper banner object', function() { + const payload = JSON.parse(request.data); + expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]) + expect(payload.imp[0].banner.w).to.equal(1) + expect(payload.imp[0].banner.h).to.equal(1) + }) + + it('accepts a single array as a size', function() { + const singleSize = [{ + 'bidder': 'sovrn', + 'params': { + 'tagid': '403370', + 'iv': 'vet' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [300, 250], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + }]; + const request = spec.buildRequests(singleSize) + const payload = JSON.parse(request.data) + expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}]) + expect(payload.imp[0].banner.w).to.equal(1) + expect(payload.imp[0].banner.h).to.equal(1) + }) + it('sends \'iv\' as query param if present', function () { const ivBidRequests = [{ 'bidder': 'sovrn', @@ -73,7 +102,8 @@ describe('sovrnBidAdapter', function() { }, 'adUnitCode': 'adunit-code', 'sizes': [ - [300, 250] + [300, 250], + [300, 600] ], 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', @@ -116,7 +146,8 @@ describe('sovrnBidAdapter', function() { }, 'adUnitCode': 'adunit-code', 'sizes': [ - [300, 250] + [300, 250], + [300, 600] ], 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', From f165cb0c11b2eb28c3c0e98ac51011e19d592ea9 Mon Sep 17 00:00:00 2001 From: jrosendahl Date: Wed, 31 Oct 2018 12:00:14 -0600 Subject: [PATCH 2/5] Updated to conform with prebid requirements --- modules/sovrnBidAdapter.js | 65 +++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index 5d6227dd69d..27255a95b5b 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -2,8 +2,8 @@ import * as utils from 'src/utils' import { registerBidder } from 'src/adapters/bidderFactory' import { BANNER } from 'src/mediaTypes' import { REPO_AND_VERSION } from 'src/constants' -import { ajax } from 'src/ajax' -const errorUrl = 'https://pcb.aws.lijit.com/collect' +const errorUrl = 'https://pcb.aws.lijit.com/c' +let errorpxls = [] export const spec = { code: 'sovrn', @@ -75,7 +75,7 @@ export const spec = { options: {contentType: 'text/plain'} } } catch (e) { - new LogError(e, {bidReqs, bidderRequest}).send() + new LogError(e, {bidReqs, bidderRequest}).append() } }, @@ -110,12 +110,13 @@ export const spec = { } return sovrnBidResponses } catch (e) { - new LogError(e, {id, seatbid}).send() + new LogError(e, {id, seatbid}).append() } }, getUserSyncs: function(syncOptions, serverResponses, gdprConsent) { try { + let tracks = [] if (serverResponses && serverResponses.length !== 0 && syncOptions.iframeEnabled) { let iidArr = serverResponses.filter(rsp => rsp.body && rsp.body.ext && rsp.body.ext.iid) .map(rsp => { return rsp.body.ext.iid }); @@ -124,15 +125,21 @@ export const spec = { consentString = gdprConsent.consentString } if (iidArr[0]) { - return [{ + tracks.push({ type: 'iframe', url: '//ap.lijit.com/beacon?informer=' + iidArr[0] + '&gdpr_consent=' + consentString, - }]; + }); } } - return [] + if (errorpxls.length && syncOptions.pixelEnabled) { + tracks = tracks.concat(errorpxls) + } + return tracks } catch (e) { - new LogError(e, {syncOptions, serverResponses, gdprConsent}).send() + if (syncOptions.pixelEnabled) { + return errorpxls + } + return [] } }, } @@ -141,26 +148,32 @@ class LogError { constructor(e, data) { utils.logError(e) this.error = {} - this.error.payload = 'error' - this.error.ts = utils.timestamp() - this.error.message = e.message - this.error.data = data - this.error.source = 'sovrnBidAdapter' - this.error.stack = e.stack - this.error.prebidVersion = $$PREBID_GLOBAL$$.version - this.error.url = utils.getTopWindowLocation().href - this.error.userAgent = navigator.userAgent + this.error.t = utils.timestamp() + this.error.m = e.message + this.error.s = e.stack + this.error.d = data + this.error.v = REPO_AND_VERSION + this.error.u = utils.getTopWindowLocation().href + this.error.ua = navigator.userAgent } - send() { - ajax( - errorUrl, - null, - JSON.stringify(this.error), - { - contentType: 'application/json', - method: 'POST', + buildErrorString(obj) { + return errorUrl + '?b=' + btoa(JSON.stringify(obj)) + } + append() { + let errstr = this.buildErrorString(this.error) + if (errstr.length > 2083) { + delete this.error.d + errstr = this.buildErrorString(this.error) + if (errstr.length > 2083) { + delete this.error.s + errstr = this.buildErrorString(this.error) + if (errstr.length > 2083) { + errstr = buildErrorString({m: 'unknown error message', t: this.error.t, u: this.error.u}) + } } - ) + } + let obj = {type: 'image', url: errstr} + errorpxls.push(obj) } } From e898d87151bbdac95dada4c483583d7e63cf5290 Mon Sep 17 00:00:00 2001 From: jrosendahl Date: Wed, 31 Oct 2018 12:13:11 -0600 Subject: [PATCH 3/5] added missing this --- modules/sovrnBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index 27255a95b5b..d55733d1417 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -168,7 +168,7 @@ class LogError { delete this.error.s errstr = this.buildErrorString(this.error) if (errstr.length > 2083) { - errstr = buildErrorString({m: 'unknown error message', t: this.error.t, u: this.error.u}) + errstr = this.buildErrorString({m: 'unknown error message', t: this.error.t, u: this.error.u}) } } } From a4c9780f36430f633d0298cc3c0c2931afb6c8f3 Mon Sep 17 00:00:00 2001 From: jrosendahl Date: Fri, 9 Nov 2018 11:04:27 -0700 Subject: [PATCH 4/5] Added tests for more coverage --- modules/sovrnBidAdapter.js | 5 +- test/spec/modules/sovrnBidAdapter_spec.js | 89 ++++++++++++++++++++--- 2 files changed, 83 insertions(+), 11 deletions(-) diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index d55733d1417..7ebe5dd0ecc 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -144,7 +144,7 @@ export const spec = { }, } -class LogError { +export class LogError { constructor(e, data) { utils.logError(e) this.error = {} @@ -175,6 +175,9 @@ class LogError { let obj = {type: 'image', url: errstr} errorpxls.push(obj) } + static getErrPxls() { + return errorpxls + } } registerBidder(spec); diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js index ce31b9e1771..6be19ea1648 100644 --- a/test/spec/modules/sovrnBidAdapter_spec.js +++ b/test/spec/modules/sovrnBidAdapter_spec.js @@ -1,7 +1,8 @@ import { expect } from 'chai'; -import { spec } from 'modules/sovrnBidAdapter'; +import { spec, LogError } from 'modules/sovrnBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; import { REPO_AND_VERSION } from 'src/constants'; +import { SSL_OP_SINGLE_ECDH_USE } from 'constants'; const ENDPOINT = `//ap.lijit.com/rtb/bid?src=${REPO_AND_VERSION}`; @@ -299,19 +300,87 @@ describe('sovrnBidAdapter', function() { 'type': 'iframe', 'url': '//ap.lijit.com/beacon?informer=13487408&gdpr_consent=', } - ]; + ] let returnStatement = spec.getUserSyncs(syncOptions, serverResponse); expect(returnStatement[0]).to.deep.equal(expectedReturnStatement[0]); - }); + }) it('should not return if iid missing on server response', () => { - let returnStatement = spec.getUserSyncs(syncOptions, []); + let returnStatement = spec.getUserSyncs(syncOptions, []) expect(returnStatement).to.be.empty; - }); + }) it('should not return if iframe syncs disabled', () => { - let returnStatement = spec.getUserSyncs(iframeDisabledSyncOptions, serverResponse); - expect(returnStatement).to.be.empty; - }); - }); -}); + let returnStatement = spec.getUserSyncs(iframeDisabledSyncOptions, serverResponse) + expect(returnStatement).to.be.empty + }) + }) + describe('LogError', () => { + it('should build and append an error object', () => { + const thrown = new Error() + const data = {name: 'Oscar Hathenswiotch'} + const err = new LogError(thrown, data) + err.append() + const errList = LogError.getErrPxls() + expect(errList.length).to.equal(1) + const errdata = JSON.parse(atob(errList[0].url.split('=')[1])) + expect(errdata.d.name).to.equal('Oscar Hathenswiotch') + }) + it('should drop data when there is too much', () => { + const thrown = new Error() + const tooLong = () => { + let str = '' + for (let i = 0; i < 10000; i++) { + str = str + String.fromCharCode(i % 100) + } + return str + } + const data = {name: 'Oscar Hathenswiotch', tooLong: tooLong()} + const err = new LogError(thrown, data) + err.append() + const errList = LogError.getErrPxls() + expect(errList.length).to.equal(2) + const errdata = JSON.parse(atob(errList[1].url.split('=')[1])) + expect(errdata.d).to.be.an('undefined') + }) + it('should drop data and stack when there is too much', () => { + const thrown = new Error() + const tooLong = () => { + let str = '' + for (let i = 0; i < 10000; i++) { + str = str + String.fromCharCode(i % 100) + } + return str + } + const data = {name: 'Oscar Hathenswiotch'} + thrown.stack = tooLong() + const err = new LogError(thrown, data) + err.append() + const errList = LogError.getErrPxls() + expect(errList.length).to.equal(3) + const errdata = JSON.parse(atob(errList[2].url.split('=')[1])) + expect(errdata.d).to.be.an('undefined') + expect(errdata.s).to.be.an('undefined') + }) + it('should drop send a reduced message when other reduction methods fail', () => { + const thrown = new Error() + const tooLong = () => { + let str = '' + for (let i = 0; i < 10000; i++) { + str = str + String.fromCharCode(i % 100) + } + return str + } + const data = {name: 'Oscar Hathenswiotch'} + thrown.message = tooLong() + const err = new LogError(thrown, data) + err.append() + const errList = LogError.getErrPxls() + expect(errList.length).to.equal(4) + const errdata = JSON.parse(atob(errList[3].url.split('=')[1])) + expect(errdata.d).to.be.an('undefined') + expect(errdata.s).to.be.an('undefined') + expect(errdata.m).to.equal('unknown error message') + }) + }) +}) From 2040b58a1b3ad22202d353c312536bd53731f92d Mon Sep 17 00:00:00 2001 From: jrosendahl Date: Fri, 9 Nov 2018 11:14:28 -0700 Subject: [PATCH 5/5] simplified error object for edge 15 --- test/spec/modules/sovrnBidAdapter_spec.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js index 6be19ea1648..a774aa64062 100644 --- a/test/spec/modules/sovrnBidAdapter_spec.js +++ b/test/spec/modules/sovrnBidAdapter_spec.js @@ -317,7 +317,10 @@ describe('sovrnBidAdapter', function() { }) describe('LogError', () => { it('should build and append an error object', () => { - const thrown = new Error() + const thrown = { + message: 'message', + stack: 'stack' + } const data = {name: 'Oscar Hathenswiotch'} const err = new LogError(thrown, data) err.append() @@ -327,7 +330,10 @@ describe('sovrnBidAdapter', function() { expect(errdata.d.name).to.equal('Oscar Hathenswiotch') }) it('should drop data when there is too much', () => { - const thrown = new Error() + const thrown = { + message: 'message', + stack: 'stack' + } const tooLong = () => { let str = '' for (let i = 0; i < 10000; i++) { @@ -344,7 +350,10 @@ describe('sovrnBidAdapter', function() { expect(errdata.d).to.be.an('undefined') }) it('should drop data and stack when there is too much', () => { - const thrown = new Error() + const thrown = { + message: 'message', + stack: 'stack' + } const tooLong = () => { let str = '' for (let i = 0; i < 10000; i++) { @@ -363,7 +372,10 @@ describe('sovrnBidAdapter', function() { expect(errdata.s).to.be.an('undefined') }) it('should drop send a reduced message when other reduction methods fail', () => { - const thrown = new Error() + const thrown = { + message: 'message', + stack: 'stack' + } const tooLong = () => { let str = '' for (let i = 0; i < 10000; i++) {