From bcfca15184892df19265bb6fd98a907974669382 Mon Sep 17 00:00:00 2001 From: Sleiman Jneidi Date: Tue, 14 Apr 2020 05:09:39 +0100 Subject: [PATCH 01/19] Qc/qc usersyncs (#4923) * QC adapter. support usersyncs * cache user sync --- modules/quantcastBidAdapter.js | 23 +++++++ modules/quantcastBidAdapter.md | 10 ++- test/spec/modules/quantcastBidAdapter_spec.js | 62 +++++++++++++++++++ 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/modules/quantcastBidAdapter.js b/modules/quantcastBidAdapter.js index ab7ea54bfb6..fc3c2d400b3 100644 --- a/modules/quantcastBidAdapter.js +++ b/modules/quantcastBidAdapter.js @@ -2,6 +2,7 @@ import * as utils from '../src/utils.js'; import { ajax } from '../src/ajax.js'; import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +import find from 'core-js/library/fn/array/find.js'; const BIDDER_CODE = 'quantcast'; const DEFAULT_BID_FLOOR = 0.0000000001; @@ -80,6 +81,7 @@ export const spec = { code: BIDDER_CODE, GVLID: 11, supportedMediaTypes: ['banner', 'video'], + hasUserSynced: false, /** * Verify the `AdUnits.bids` response with `true` for valid request and `false` @@ -223,6 +225,27 @@ export const spec = { onTimeout(timeoutData) { const url = `${QUANTCAST_PROTOCOL}://${QUANTCAST_DOMAIN}:${QUANTCAST_PORT}/qchb_notify?type=timeout`; ajax(url, null, null); + }, + getUserSyncs(syncOptions, serverResponses) { + const syncs = [] + if (!this.hasUserSynced && syncOptions.pixelEnabled) { + const responseWithUrl = find(serverResponses, serverResponse => + utils.deepAccess(serverResponse.body, 'userSync.url') + ); + + if (responseWithUrl) { + const url = utils.deepAccess(responseWithUrl.body, 'userSync.url') + syncs.push({ + type: 'image', + url: url + }); + } + this.hasUserSynced = true; + } + return syncs; + }, + resetUserSync() { + this.hasUserSynced = false; } }; diff --git a/modules/quantcastBidAdapter.md b/modules/quantcastBidAdapter.md index 2b55eae9026..edbbc538b65 100644 --- a/modules/quantcastBidAdapter.md +++ b/modules/quantcastBidAdapter.md @@ -27,7 +27,10 @@ const adUnits = [{ battr: [1, 2] // OPTIONAL - Array of blocked creative attributes as per OpenRTB Spec List 5.3 } } - ] + ], + userSync: { + url: 'https://quantcast.com/pixelUrl' + } }]; ``` @@ -63,6 +66,9 @@ var adUnits = [{ } } } - ] + ], + userSync: { + url: 'https://quantcast.com/pixelUrl' + } }]; ``` diff --git a/test/spec/modules/quantcastBidAdapter_spec.js b/test/spec/modules/quantcastBidAdapter_spec.js index 5e4093c91be..c02c6fea5a7 100644 --- a/test/spec/modules/quantcastBidAdapter_spec.js +++ b/test/spec/modules/quantcastBidAdapter_spec.js @@ -542,5 +542,67 @@ describe('Quantcast adapter', function () { expect(interpretedResponse.length).to.equal(0); }); + + it('should return pixel url when available userSync available', function () { + const syncOptions = { + pixelEnabled: true + }; + const serverResponses = [ + { + body: { + userSync: { + url: 'http://quantcast.com/pixelUrl' + } + } + }, + { + body: { + + } + } + ]; + + const actualSyncs = qcSpec.getUserSyncs(syncOptions, serverResponses); + const expectedSync = { + type: 'image', + url: 'http://quantcast.com/pixelUrl' + }; + expect(actualSyncs.length).to.equal(1); + expect(actualSyncs[0]).to.deep.equal(expectedSync); + qcSpec.resetUserSync(); + }); + + it('should not return user syncs if done already', function () { + const syncOptions = { + pixelEnabled: true + }; + const serverResponses = [ + { + body: { + userSync: { + url: 'http://quantcast.com/pixelUrl' + } + } + }, + { + body: { + + } + } + ]; + + let actualSyncs = qcSpec.getUserSyncs(syncOptions, serverResponses); + const expectedSync = { + type: 'image', + url: 'http://quantcast.com/pixelUrl' + }; + expect(actualSyncs.length).to.equal(1); + expect(actualSyncs[0]).to.deep.equal(expectedSync); + + actualSyncs = qcSpec.getUserSyncs(syncOptions, serverResponses); + expect(actualSyncs.length).to.equal(0); + + qcSpec.resetUserSync(); + }); }); }); From bddf7e93fbd97c9494db82bd0ad9e35d7118da1c Mon Sep 17 00:00:00 2001 From: Vadim Mazzherin Date: Tue, 14 Apr 2020 15:27:28 +0600 Subject: [PATCH 02/19] ShowHeroes adapter v2 (#5085) * add ShowHeroes Adapter * ShowHeroes adapter - expanded outstream support * Revert "ShowHeroes adapter - expanded outstream support" This reverts commit bfcdb913b52012b5afbf95a84956b906518a4b51. * ShowHeroes adapter - expanded outstream support * ShowHeroes adapter - fixes (#4222) * ShowHeroes adapter - banner and outstream fixes (#4222) * ShowHeroes adapter - description and outstream changes (#4222) * ShowHeroes adapter - increase test coverage and small fix * ShowHeroes Adapter - naming convention issue * Mixed AdUnits declaration support Co-authored-by: veranevera --- modules/showheroes-bsBidAdapter.js | 111 ++++++++++-------- .../modules/showheroes-bsBidAdapter_spec.js | 110 +++++++++++++---- 2 files changed, 153 insertions(+), 68 deletions(-) diff --git a/modules/showheroes-bsBidAdapter.js b/modules/showheroes-bsBidAdapter.js index 2ae7cc56c0f..2831d793aad 100644 --- a/modules/showheroes-bsBidAdapter.js +++ b/modules/showheroes-bsBidAdapter.js @@ -29,6 +29,7 @@ export const spec = { return !!bid.params.playerId; }, buildRequests: function(validBidRequests, bidderRequest) { + let adUnits = []; const pageURL = validBidRequests[0].params.contentPageUrl || bidderRequest.refererInfo.referer; const isStage = !!validBidRequests[0].params.stage; const isOutstream = utils.deepAccess(validBidRequests[0], 'mediaTypes.video.context') === 'outstream'; @@ -38,53 +39,53 @@ export const spec = { const outstreamOptions = utils.deepAccess(validBidRequests[0], 'params.outstreamOptions'); const isBanner = !!validBidRequests[0].mediaTypes.banner || (isOutstream && !(isCustomRender || isNativeRender || isNodeRender)); - let adUnits = validBidRequests.map((bid) => { + validBidRequests.forEach((bid) => { + const videoSizes = getVideoSizes(bid); + const bannerSizes = getBannerSizes(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; - let mediaVideoSize = utils.deepAccess(bid, 'mediaTypes.video.playerSize'); - if (utils.isArray(mediaVideoSize)) { - mediaSize = mediaVideoSize; + const makeBids = (type, size) => { + let context = ''; + let streamType = 2; + + if (type === BANNER) { + streamType = 5; } else { - mediaSize = bid.mediaTypes.banner.sizes; - } - if (utils.isArray(mediaSize[0])) { - sizes = mediaSize[0]; - } else if (utils.isNumber(mediaSize[0])) { - sizes = mediaSize; + context = utils.deepAccess(bid, 'mediaTypes.video.context'); + if (vpaidMode && context === 'instream') { + streamType = 1; + } + if (context === 'outstream') { + streamType = 5; + } } - } - const context = utils.deepAccess(bid, 'mediaTypes.video.context'); - - let streamType = 2; + return { + type: streamType, + bidId: bid.bidId, + mediaType: type, + context: context, + playerId: utils.getBidIdParameter('playerId', bid.params), + auctionId: bidderRequest.auctionId, + bidderCode: BIDDER_CODE, + gdprConsent: bidderRequest.gdprConsent, + start: +new Date(), + timeout: 3000, + size: { + width: size[0], + height: size[1] + }, + params: bid.params, + }; + }; - if (vpaidMode && context === 'instream') { - streamType = 1; - } - if (isBanner || context === 'outstream') { - streamType = 5; - } + videoSizes.forEach((size) => { + adUnits.push(makeBids(VIDEO, size)); + }); - return { - type: streamType, - bidId: bid.bidId, - mediaType: isBanner ? BANNER : VIDEO, - context: context, - 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] - }, - params: bid.params, - }; + bannerSizes.forEach((size) => { + adUnits.push(makeBids(BANNER, size)); + }); }); return { @@ -94,6 +95,7 @@ export const spec = { data: { 'user': [], 'meta': { + 'adapterVersion': 2, 'pageURL': encodeURIComponent(pageURL), 'vastCacheEnabled': (!!config.getConfig('cache') && !isBanner && !outstreamOptions) || false, 'isDesktop': utils.getWindowTop().document.documentElement.clientWidth > 700, @@ -156,12 +158,12 @@ function createBids(bidRes, reqData) { bidUnit.cpm = bid.cpm; bidUnit.requestId = bid.bidId; bidUnit.currency = bid.currency; - bidUnit.mediaType = reqBid.mediaType || VIDEO; + bidUnit.mediaType = bid.mediaType || VIDEO; bidUnit.ttl = TTL; bidUnit.creativeId = 'c_' + bid.bidId; bidUnit.netRevenue = true; - bidUnit.width = bid.video.width; - bidUnit.height = bid.video.height; + bidUnit.width = bid.size.width; + bidUnit.height = bid.size.height; if (bid.vastXml) { bidUnit.vastXml = bid.vastXml; bidUnit.adResponse = { @@ -171,16 +173,16 @@ function createBids(bidRes, reqData) { if (bid.vastTag) { bidUnit.vastUrl = bid.vastTag; } - if (reqBid.mediaType === BANNER) { + if (bid.mediaType === BANNER) { bidUnit.ad = getBannerHtml(bid, reqBid, reqData); - } else if (reqBid.context === 'outstream') { + } else if (bid.context === 'outstream') { const renderer = Renderer.install({ id: bid.bidId, url: '//', config: { playerId: reqBid.playerId, - width: bid.video.width, - height: bid.video.height, + width: bid.size.width, + height: bid.size.height, vastUrl: bid.vastTag, vastXml: bid.vastXml, debug: reqData.debug, @@ -267,4 +269,19 @@ function getBannerHtml (bid, reqBid, reqData) { `; } +function getVideoSizes(bidRequest) { + return formatSizes(utils.deepAccess(bidRequest, 'mediaTypes.video.playerSize') || []); +} + +function getBannerSizes(bidRequest) { + return formatSizes(utils.deepAccess(bidRequest, 'mediaTypes.banner.sizes') || bidRequest.sizes || []); +} + +function formatSizes(sizes) { + if (!sizes || !sizes.length) { + return [] + } + return Array.isArray(sizes[0]) ? sizes : [sizes]; +} + registerBidder(spec); diff --git a/test/spec/modules/showheroes-bsBidAdapter_spec.js b/test/spec/modules/showheroes-bsBidAdapter_spec.js index c82042469ef..4f2fd9d641a 100644 --- a/test/spec/modules/showheroes-bsBidAdapter_spec.js +++ b/test/spec/modules/showheroes-bsBidAdapter_spec.js @@ -79,6 +79,22 @@ const bidRequestBanner = { } } +const bidRequestBannerMultiSizes = { + ...bidRequestCommonParams, + ...{ + 'mediaTypes': { + 'banner': { + 'sizes': [[640, 360], [480, 320]] + } + } + } +} + +const bidRequestVideoAndBanner = { + ...bidRequestBanner, + ...bidRequestVideo +} + describe('shBidAdapter', function () { const adapter = newBidder(spec) @@ -120,8 +136,8 @@ describe('shBidAdapter', function () { }], bidderRequest) const payload = request.data.requests[0]; expect(payload).to.be.an('object'); - expect(payload.video).to.have.property('width', 640); - expect(payload.video).to.have.property('height', 480); + expect(payload.size).to.have.property('width', 640); + expect(payload.size).to.have.property('height', 480); const request2 = spec.buildRequests([{ 'params': {}, @@ -130,8 +146,8 @@ describe('shBidAdapter', function () { }], bidderRequest) const payload2 = request2.data.requests[0]; expect(payload).to.be.an('object'); - expect(payload2.video).to.have.property('width', 320); - expect(payload2.video).to.have.property('height', 240); + expect(payload2.size).to.have.property('width', 320); + expect(payload2.size).to.have.property('height', 240); }) it('should get size from mediaTypes when sizes property is empty', function () { @@ -146,8 +162,8 @@ describe('shBidAdapter', function () { }], bidderRequest) const payload = request.data.requests[0]; expect(payload).to.be.an('object'); - expect(payload.video).to.have.property('width', 640); - expect(payload.video).to.have.property('height', 480); + expect(payload.size).to.have.property('width', 640); + expect(payload.size).to.have.property('height', 480); const request2 = spec.buildRequests([{ 'params': {}, @@ -160,8 +176,8 @@ describe('shBidAdapter', function () { }], bidderRequest) const payload2 = request2.data.requests[0]; expect(payload).to.be.an('object'); - expect(payload2.video).to.have.property('width', 320); - expect(payload2.video).to.have.property('height', 240); + expect(payload2.size).to.have.property('width', 320); + expect(payload2.size).to.have.property('height', 240); }) it('should attach valid params to the payload when type is video', function () { @@ -191,6 +207,38 @@ describe('shBidAdapter', function () { expect(payload).to.have.property('type', 5); }) + it('should attach valid params to the payload when type is banner (multi sizes)', function () { + const request = spec.buildRequests([bidRequestBannerMultiSizes], 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); + expect(payload).to.have.nested.property('size.width', 640); + expect(payload).to.have.nested.property('size.height', 360); + const payload2 = request.data.requests[1]; + expect(payload2).to.be.an('object'); + expect(payload2).to.have.property('playerId', '47427aa0-f11a-4d24-abca-1295a46a46cd'); + expect(payload2).to.have.property('mediaType', BANNER); + expect(payload2).to.have.property('type', 5); + expect(payload2).to.have.nested.property('size.width', 480); + expect(payload2).to.have.nested.property('size.height', 320); + }) + + it('should attach valid params to the payload when type is banner and video', function () { + const request = spec.buildRequests([bidRequestVideoAndBanner], 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); + const payload2 = request.data.requests[1]; + expect(payload2).to.be.an('object'); + expect(payload2).to.have.property('playerId', '47427aa0-f11a-4d24-abca-1295a46a46cd'); + expect(payload2).to.have.property('mediaType', BANNER); + expect(payload2).to.have.property('type', 5); + }) + it('passes gdpr if present', function () { const request = spec.buildRequests([bidRequestVideo], {...bidderRequest, ...gdpr}) const payload = request.data.requests[0]; @@ -208,16 +256,36 @@ describe('shBidAdapter', function () { const 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' const vastXml = '' - const response = { + const basicResponse = { + 'cpm': 5, + 'currency': 'EUR', + 'mediaType': VIDEO, + 'context': 'instream', + 'bidId': '38b373e1e31c18', + 'size': {'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', + 'vastXml': vastXml, + }; + + const responseVideo = { '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', - 'vastXml': vastXml, + ...basicResponse, }], - } + }; + + const responseVideoOutstream = { + 'bids': [{ + ...basicResponse, + 'context': 'outstream', + }], + }; + + const responseBanner = { + 'bids': [{ + ...basicResponse, + 'mediaType': BANNER, + }], + }; it('should get correct bid response when type is video', function () { const request = spec.buildRequests([bidRequestVideo], bidderRequest) @@ -240,14 +308,14 @@ describe('shBidAdapter', function () { } ] - const result = spec.interpretResponse({'body': response}, request) + const result = spec.interpretResponse({'body': responseVideo}, 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) + const result = spec.interpretResponse({'body': responseBanner}, request) expect(result[0]).to.have.property('mediaType', BANNER); expect(result[0].ad).to.include('