From 8acd2b4a223bc938c8048530ecaaf1c40e6eef9f Mon Sep 17 00:00:00 2001 From: Meven Courouble Date: Wed, 9 Nov 2022 16:41:42 +0100 Subject: [PATCH 01/12] Smartadserver Bid Adapter: Add support for SDA user and site --- modules/smartadserverBidAdapter.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index 5d64cad27b5..8d5d9384765 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -154,7 +154,9 @@ export const spec = { timeout: config.getConfig('bidderTimeout'), bidId: bid.bidId, prebidVersion: '$prebid.version$', - schain: spec.serializeSupplyChain(bid.schain) + schain: spec.serializeSupplyChain(bid.schain), + sda: deepAccess(config, 'ortb2.user.data', deepAccess(bidderRequest, 'ortb2.user.data', undefined)), + sdc: deepAccess(config, 'ortb2.site.content.data', deepAccess(bidderRequest, 'ortb2.site.content.data', undefined)) }; if (bidderRequest && bidderRequest.gdprConsent) { From 24ebad807f69cf22eb84d72b1247600c6310d45a Mon Sep 17 00:00:00 2001 From: Meven Courouble Date: Wed, 16 Nov 2022 11:43:24 +0100 Subject: [PATCH 02/12] Smartadserver Bid Adapter: Fix SDA support getConfig and add to unit testing --- modules/smartadserverBidAdapter.js | 6 +- .../modules/smartadserverBidAdapter_spec.js | 59 +++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index 0b18a911b1f..6ff0e592542 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -133,6 +133,8 @@ export const spec = { // use bidderRequest.bids[] to get bidder-dependent request info const adServerCurrency = config.getConfig('currency.adServerCurrency'); + const sellerDefinedAudience = deepAccess(bidderRequest, 'ortb2.user.data', config.getAnyConfig('ortb2.user.data')); + const sellerDefinedContext = deepAccess(bidderRequest, 'ortb2.site.content.data', config.getAnyConfig('ortb2.site.content.data')); // pull requested transaction ID from bidderRequest.bids[].transactionId return validBidRequests.reduce((bidRequests, bid) => { @@ -155,8 +157,8 @@ export const spec = { bidId: bid.bidId, prebidVersion: '$prebid.version$', schain: spec.serializeSupplyChain(bid.schain), - sda: deepAccess(config, 'ortb2.user.data', deepAccess(bidderRequest, 'ortb2.user.data', undefined)), - sdc: deepAccess(config, 'ortb2.site.content.data', deepAccess(bidderRequest, 'ortb2.site.content.data', undefined)) + sda: sellerDefinedAudience, + sdc: sellerDefinedContext }; if (bidderRequest && bidderRequest.gdprConsent) { diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index 18308481e1a..db61983c9c9 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -158,12 +158,44 @@ describe('Smart bid adapter tests', function () { } }; + var sellerDefinedAudience = [ + { + 'name': 'hearst.com', + 'ext': { 'segtax': 1 }, + 'segment': [ + { 'id': '1001' }, + { 'id': '1002' } + ] + } + ]; + + var sellerDefinedContext = [ + { + 'name': 'cnn.com', + 'ext': { 'segtax': 2 }, + 'segment': [ + { 'id': '2002' } + ] + } + ]; + it('Verify build request', function () { config.setConfig({ 'currency': { 'adServerCurrency': 'EUR' + }, + ortb2: { + 'user': { + 'data': sellerDefinedAudience + }, + 'site': { + 'content': { + 'data': sellerDefinedContext + } + } } }); + const request = spec.buildRequests(DEFAULT_PARAMS); expect(request[0]).to.have.property('url').and.to.equal('https://prg.smartadserver.com/prebid/v1'); expect(request[0]).to.have.property('method').and.to.equal('POST'); @@ -186,6 +218,8 @@ describe('Smart bid adapter tests', function () { expect(requestContent).to.have.property('buid').and.to.equal('7569'); expect(requestContent).to.have.property('appname').and.to.equal('Mozilla'); expect(requestContent).to.have.property('ckid').and.to.equal(42); + expect(requestContent).to.have.property('sda').and.to.deep.equal(sellerDefinedAudience); + expect(requestContent).to.have.property('sdc').and.to.deep.equal(sellerDefinedContext); }); it('Verify parse response with no ad', function () { @@ -358,6 +392,7 @@ describe('Smart bid adapter tests', function () { describe('gdpr tests', function () { afterEach(function () { + config.setConfig({ ortb2: undefined }); config.resetConfig(); $$PREBID_GLOBAL$$.requestBids.removeAll(); }); @@ -489,6 +524,16 @@ describe('Smart bid adapter tests', function () { config.setConfig({ 'currency': { 'adServerCurrency': 'EUR' + }, + ortb2: { + 'user': { + 'data': sellerDefinedAudience + }, + 'site': { + 'content': { + 'data': sellerDefinedContext + } + } } }); const request = spec.buildRequests(INSTREAM_DEFAULT_PARAMS); @@ -507,6 +552,8 @@ describe('Smart bid adapter tests', function () { expect(requestContent).to.have.property('buid').and.to.equal('7569'); expect(requestContent).to.have.property('appname').and.to.equal('Mozilla'); expect(requestContent).to.have.property('ckid').and.to.equal(42); + expect(requestContent).to.have.property('sda').and.to.deep.equal(sellerDefinedAudience); + expect(requestContent).to.have.property('sdc').and.to.deep.equal(sellerDefinedContext); expect(requestContent).to.have.property('isVideo').and.to.equal(true); expect(requestContent).to.have.property('videoData'); expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(6); @@ -748,6 +795,16 @@ describe('Smart bid adapter tests', function () { config.setConfig({ 'currency': { 'adServerCurrency': 'EUR' + }, + ortb2: { + 'user': { + 'data': sellerDefinedAudience + }, + 'site': { + 'content': { + 'data': sellerDefinedContext + } + } } }); const request = spec.buildRequests(OUTSTREAM_DEFAULT_PARAMS); @@ -766,6 +823,8 @@ describe('Smart bid adapter tests', function () { expect(requestContent).to.have.property('buid').and.to.equal('7579'); expect(requestContent).to.have.property('appname').and.to.equal('Mozilla'); expect(requestContent).to.have.property('ckid').and.to.equal(43); + expect(requestContent).to.have.property('sda').and.to.deep.equal(sellerDefinedAudience); + expect(requestContent).to.have.property('sdc').and.to.deep.equal(sellerDefinedContext); expect(requestContent).to.have.property('isVideo').and.to.equal(false); expect(requestContent).to.have.property('videoData'); expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(7); From 36312fd411d9ffb0dbd0e8bc1201fbcddd890dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Sok=C3=B3=C5=82?= Date: Tue, 17 Jan 2023 12:46:27 +0100 Subject: [PATCH 03/12] support floors per media type --- modules/smartadserverBidAdapter.js | 20 ++-- .../modules/smartadserverBidAdapter_spec.js | 97 ++++++++++++++++++- 2 files changed, 103 insertions(+), 14 deletions(-) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index 6ff0e592542..85b08bc4613 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -13,6 +13,7 @@ export const spec = { gvlid: GVL_ID, aliases: ['smart'], // short code supportedMediaTypes: [BANNER, VIDEO], + /** * Determines whether or not the given bid request is valid. * @@ -131,7 +132,6 @@ export const spec = { */ buildRequests: function (validBidRequests, bidderRequest) { // use bidderRequest.bids[] to get bidder-dependent request info - const adServerCurrency = config.getConfig('currency.adServerCurrency'); const sellerDefinedAudience = deepAccess(bidderRequest, 'ortb2.user.data', config.getAnyConfig('ortb2.user.data')); const sellerDefinedContext = deepAccess(bidderRequest, 'ortb2.site.content.data', config.getAnyConfig('ortb2.site.content.data')); @@ -144,7 +144,6 @@ export const spec = { pageid: bid.params.pageId, formatid: bid.params.formatId, currencyCode: adServerCurrency, - bidfloor: bid.params.bidfloor || spec.getBidFloor(bid, adServerCurrency), targeting: bid.params.target && bid.params.target !== '' ? bid.params.target : undefined, buid: bid.params.buId && bid.params.buId !== '' ? bid.params.buId : undefined, appname: bid.params.appName && bid.params.appName !== '' ? bid.params.appName : undefined, @@ -178,11 +177,14 @@ export const spec = { const videoMediaType = deepAccess(bid, 'mediaTypes.video'); const bannerMediaType = deepAccess(bid, 'mediaTypes.banner'); const isAdUnitContainingVideo = videoMediaType && (videoMediaType.context === 'instream' || videoMediaType.context === 'outstream'); + if (!isAdUnitContainingVideo && bannerMediaType) { payload.sizes = spec.adaptBannerSizes(bannerMediaType.sizes); + payload.bidfloor = bid.params.bidfloor || spec.getBidFloor(bid, adServerCurrency, BANNER); bidRequests.push(spec.createServerRequest(payload, bid.params.domain)); } else if (isAdUnitContainingVideo && !bannerMediaType) { spec.fillPayloadForVideoBidRequest(payload, videoMediaType, bid.params.video); + payload.bidfloor = bid.params.bidfloor || spec.getBidFloor(bid, adServerCurrency, VIDEO); bidRequests.push(spec.createServerRequest(payload, bid.params.domain)); } else if (isAdUnitContainingVideo && bannerMediaType) { // If there are video and banner media types in the ad unit, we clone the payload @@ -190,9 +192,12 @@ export const spec = { let videoPayload = deepClone(payload); spec.fillPayloadForVideoBidRequest(videoPayload, videoMediaType, bid.params.video); + videoPayload.bidfloor = bid.params.bidfloor || spec.getBidFloor(bid, adServerCurrency, VIDEO); bidRequests.push(spec.createServerRequest(videoPayload, bid.params.domain)); payload.sizes = spec.adaptBannerSizes(bannerMediaType.sizes); + payload.bidfloor = bid.params.bidfloor || spec.getBidFloor(bid, adServerCurrency, BANNER); + bidRequests.push(spec.createServerRequest(payload, bid.params.domain)); } else { bidRequests.push({}); @@ -253,24 +258,21 @@ export const spec = { * * @param {object} bid Bid request object * @param {string} currency Ad server currency + * @param {string} mediaType Bid media type * @return {number} Floor price */ - getBidFloor: function (bid, currency) { + getBidFloor: function (bid, currency, mediaType) { if (!isFn(bid.getFloor)) { return DEFAULT_FLOOR; } const floor = bid.getFloor({ currency: currency || 'USD', - mediaType: '*', + mediaType, size: '*' }); - if (isPlainObject(floor) && !isNaN(floor.floor)) { - return floor.floor; - } - - return DEFAULT_FLOOR; + return isPlainObject(floor) && !isNaN(floor.floor) ? floor.floor : DEFAULT_FLOOR; }, /** diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index db61983c9c9..4dacb356894 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -1,4 +1,5 @@ import { expect } from 'chai'; +import { BANNER, VIDEO } from 'src/mediaTypes.js'; import { config } from 'src/config.js'; import { spec } from 'modules/smartadserverBidAdapter.js'; @@ -394,7 +395,6 @@ describe('Smart bid adapter tests', function () { afterEach(function () { config.setConfig({ ortb2: undefined }); config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeAll(); }); it('Verify build request with GDPR', function () { @@ -446,7 +446,6 @@ describe('Smart bid adapter tests', function () { describe('ccpa/us privacy tests', function () { afterEach(function () { config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeAll(); }); it('Verify build request with us privacy', function () { @@ -475,7 +474,6 @@ describe('Smart bid adapter tests', function () { describe('Instream video tests', function () { afterEach(function () { config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeAll(); }); const INSTREAM_DEFAULT_PARAMS = [{ @@ -746,7 +744,6 @@ describe('Smart bid adapter tests', function () { describe('Outstream video tests', function () { afterEach(function () { config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeAll(); }); const OUTSTREAM_DEFAULT_PARAMS = [{ @@ -1055,6 +1052,17 @@ describe('Smart bid adapter tests', function () { }); describe('Floors module', function () { + const getFloor = (bid) => { + switch (bid.mediaType) { + case BANNER: + return { currency: 'USD', floor: 1.93 }; + case VIDEO: + return { currency: 'USD', floor: 2.72 }; + default: + return {}; + } + }; + it('should include floor from bid params', function() { const bidRequest = JSON.parse((spec.buildRequests(DEFAULT_PARAMS))[0].data); expect(bidRequest.bidfloor).to.deep.equal(DEFAULT_PARAMS[0].params.bidfloor); @@ -1094,12 +1102,91 @@ describe('Smart bid adapter tests', function () { const floor = spec.getBidFloor(bidRequest, null); expect(floor).to.deep.equal(0); }); + + it('should take floor from bidder params over ad unit', function() { + const bidRequest = [{ + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + getFloor, + params: { siteId: 1234, pageId: 678, formatId: 73, bidfloor: 1.25 } + }]; + + const request = spec.buildRequests(bidRequest); + const requestContent = JSON.parse(request[0].data); + + expect(requestContent).to.have.property('bidfloor').and.to.equal(1.25); + }); + + it('should take floor from banner ad unit', function() { + const bidRequest = [{ + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + getFloor, + params: { siteId: 1234, pageId: 678, formatId: 73 } + }]; + + const request = spec.buildRequests(bidRequest); + const requestContent = JSON.parse(request[0].data); + + expect(requestContent).to.have.property('bidfloor').and.to.equal(1.93); + }); + + it('should take floor from video ad unit', function() { + const bidRequest = [{ + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[640, 480]] + } + }, + getFloor, + params: { siteId: 1234, pageId: 678, formatId: 73 } + }]; + + const request = spec.buildRequests(bidRequest); + const requestContent = JSON.parse(request[0].data); + + expect(requestContent).to.have.property('bidfloor').and.to.equal(2.72); + }); + + it('should take floor from multiple media type ad unit', function() { + const bidRequest = [{ + mediaTypes: { + banner: { + sizes: [[300, 600]] + }, + video: { + context: 'outstream', + playerSize: [[640, 480]] + } + }, + getFloor, + params: { siteId: 1234, pageId: 678, formatId: 73 } + }]; + + const requests = spec.buildRequests(bidRequest); + expect(requests).to.have.lengthOf(2); + + const requestContents = requests.map(r => JSON.parse(r.data)); + const videoRequest = requestContents.filter(r => r.videoData)[0]; + expect(videoRequest).to.not.equal(null).and.to.not.be.undefined; + expect(videoRequest).to.have.property('bidfloor').and.to.equal(2.72); + + const bannerRequest = requestContents.filter(r => !r.videoData)[0]; + expect(bannerRequest).to.not.equal(null).and.to.not.be.undefined; + expect(bannerRequest).to.have.property('bidfloor').and.to.equal(1.93); + }); }); describe('Verify bid requests with multiple mediaTypes', function () { afterEach(function () { config.resetConfig(); - $$PREBID_GLOBAL$$.requestBids.removeAll(); }); var DEFAULT_PARAMS_MULTIPLE_MEDIA_TYPES = [{ From de0d7622e63544800bce06ac4e2c1e613ecb4884 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Sok=C3=B3=C5=82?= Date: Tue, 24 Jan 2023 08:34:03 +0100 Subject: [PATCH 04/12] Add GPP support --- modules/smartadserverBidAdapter.js | 23 +++++++++++------- .../modules/smartadserverBidAdapter_spec.js | 24 +++++++++++++++++++ 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index 85b08bc4613..9395d4c4c83 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -160,20 +160,27 @@ export const spec = { sdc: sellerDefinedContext }; - if (bidderRequest && bidderRequest.gdprConsent) { - payload.addtl_consent = bidderRequest.gdprConsent.addtlConsent; - payload.gdpr_consent = bidderRequest.gdprConsent.consentString; - payload.gdpr = bidderRequest.gdprConsent.gdprApplies; // we're handling the undefined case server side + if (bidderRequest) { + if (bidderRequest.gdprConsent) { + payload.addtl_consent = bidderRequest.gdprConsent.addtlConsent; + payload.gdpr_consent = bidderRequest.gdprConsent.consentString; + payload.gdpr = bidderRequest.gdprConsent.gdprApplies; // we're handling the undefined case server side + } + + if (bidderRequest.gppConsent) { + payload.gpp = bidderRequest.gppConsent.gppString; + payload.gpp_sid = bidderRequest.gppConsent.applicableSections; + } + + if (bidderRequest.uspConsent) { + payload.us_privacy = bidderRequest.uspConsent; + } } if (bid && bid.userId) { payload.eids = createEidsArray(bid.userId); } - if (bidderRequest && bidderRequest.uspConsent) { - payload.us_privacy = bidderRequest.uspConsent; - } - const videoMediaType = deepAccess(bid, 'mediaTypes.video'); const bannerMediaType = deepAccess(bid, 'mediaTypes.banner'); const isAdUnitContainingVideo = videoMediaType && (videoMediaType.context === 'instream' || videoMediaType.context === 'outstream'); diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index 4dacb356894..97250ad0ebc 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -443,6 +443,30 @@ describe('Smart bid adapter tests', function () { }); }); + describe('GPP', function () { + it('should be added to payload when gppConsent available in bidder request', function () { + const options = { + gppConsent: { + gppString: 'some-gpp-string', + applicableSections: [3, 5] + } + }; + const request = spec.buildRequests(DEFAULT_PARAMS_WO_OPTIONAL, options); + const payload = JSON.parse(request[0].data); + + expect(payload).to.have.property('gpp').and.to.equal(options.gppConsent.gppString); + expect(payload).to.have.property('gpp_sid').and.to.be.an('array'); + expect(payload.gpp_sid).to.have.lengthOf(2).and.to.deep.equal(options.gppConsent.applicableSections); + }); + + it('should be undefined on payload when gppConsent unavailable in bidder request', function () { + const request = spec.buildRequests(DEFAULT_PARAMS_WO_OPTIONAL, {}); + const payload = JSON.parse(request[0].data); + + expect(payload.gpp).to.be.undefined; + }); + }); + describe('ccpa/us privacy tests', function () { afterEach(function () { config.resetConfig(); From 5b89a6ec68330c9bcd3e4b02b15e3c49616167f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Sok=C3=B3=C5=82?= Date: Tue, 24 Jan 2023 15:28:56 +0100 Subject: [PATCH 05/12] Rework payloads enriching --- modules/smartadserverBidAdapter.js | 38 ++++++++++++++---------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index 85b08bc4613..719d621b056 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -174,30 +174,28 @@ export const spec = { payload.us_privacy = bidderRequest.uspConsent; } - const videoMediaType = deepAccess(bid, 'mediaTypes.video'); const bannerMediaType = deepAccess(bid, 'mediaTypes.banner'); - const isAdUnitContainingVideo = videoMediaType && (videoMediaType.context === 'instream' || videoMediaType.context === 'outstream'); - - if (!isAdUnitContainingVideo && bannerMediaType) { - payload.sizes = spec.adaptBannerSizes(bannerMediaType.sizes); - payload.bidfloor = bid.params.bidfloor || spec.getBidFloor(bid, adServerCurrency, BANNER); - bidRequests.push(spec.createServerRequest(payload, bid.params.domain)); - } else if (isAdUnitContainingVideo && !bannerMediaType) { - spec.fillPayloadForVideoBidRequest(payload, videoMediaType, bid.params.video); - payload.bidfloor = bid.params.bidfloor || spec.getBidFloor(bid, adServerCurrency, VIDEO); - bidRequests.push(spec.createServerRequest(payload, bid.params.domain)); - } else if (isAdUnitContainingVideo && bannerMediaType) { - // If there are video and banner media types in the ad unit, we clone the payload - // to create a specific one for video. - let videoPayload = deepClone(payload); + const videoMediaType = deepAccess(bid, 'mediaTypes.video'); + const isSupportedVideoContext = videoMediaType && (videoMediaType.context === 'instream' || videoMediaType.context === 'outstream'); - spec.fillPayloadForVideoBidRequest(videoPayload, videoMediaType, bid.params.video); - videoPayload.bidfloor = bid.params.bidfloor || spec.getBidFloor(bid, adServerCurrency, VIDEO); - bidRequests.push(spec.createServerRequest(videoPayload, bid.params.domain)); + if (bannerMediaType || isSupportedVideoContext) { + let type; + if (bannerMediaType) { + type = BANNER; + payload.sizes = spec.adaptBannerSizes(bannerMediaType.sizes); - payload.sizes = spec.adaptBannerSizes(bannerMediaType.sizes); - payload.bidfloor = bid.params.bidfloor || spec.getBidFloor(bid, adServerCurrency, BANNER); + if (isSupportedVideoContext) { + let videoPayload = deepClone(payload); + spec.fillPayloadForVideoBidRequest(videoPayload, videoMediaType, bid.params.video); + videoPayload.bidfloor = bid.params.bidfloor || spec.getBidFloor(bid, adServerCurrency, VIDEO); + bidRequests.push(spec.createServerRequest(videoPayload, bid.params.domain)); + } + } else { + type = VIDEO; + spec.fillPayloadForVideoBidRequest(payload, videoMediaType, bid.params.video); + } + payload.bidfloor = bid.params.bidfloor || spec.getBidFloor(bid, adServerCurrency, type); bidRequests.push(spec.createServerRequest(payload, bid.params.domain)); } else { bidRequests.push({}); From 73233399ea391b96adc4864890ed2bfc7aa639ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Sok=C3=B3=C5=82?= Date: Mon, 22 May 2023 11:51:36 +0200 Subject: [PATCH 06/12] Add gpid support --- modules/smartadserverBidAdapter.js | 5 +++ .../modules/smartadserverBidAdapter_spec.js | 42 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index fd2d6e16463..4b7e65cae87 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -159,6 +159,11 @@ export const spec = { sdc: sellerDefinedContext }; + const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid', deepAccess(bid, 'ortb2Imp.ext.data.pbadslot', '')); + if (gpid) { + payload.gpid = gpid; + } + if (bidderRequest) { if (bidderRequest.gdprConsent) { payload.addtl_consent = bidderRequest.gdprConsent.addtlConsent; diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index 504ff978e9e..9142b730b27 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { BANNER, VIDEO } from 'src/mediaTypes.js'; import { config } from 'src/config.js'; +import { deepClone } from 'src/utils.js'; import { spec } from 'modules/smartadserverBidAdapter.js'; // Default params with optional ones @@ -1339,4 +1340,45 @@ describe('Smart bid adapter tests', function () { expect(bannerRequest).to.have.property('formatid').and.to.equal('90'); }); }); + + describe('Global Placement ID (GPID)', function () { + it('should not include gpid by default', () => { + const request = spec.buildRequests(DEFAULT_PARAMS_WO_OPTIONAL); + const requestContent = JSON.parse(request[0].data); + + expect(requestContent).to.not.have.property('gdid'); + }); + + it('should include gpid if pbadslot in ortb2Imp', () => { + const gpid = '/19968336/header-bid-tag-1'; + const bidRequests = deepClone(DEFAULT_PARAMS_WO_OPTIONAL); + + bidRequests[0].ortb2Imp = { + ext: { + data: { + pbadslot: gpid + } + } + }; + + const request = spec.buildRequests(bidRequests); + const requestContent = JSON.parse(request[0].data); + + expect(requestContent).to.have.property('gpid').and.to.equal(gpid); + }); + + it('should include gpid if imp[].ext.gpid exists', () => { + const gpid = '/1111/homepage#div-leftnav'; + const bidRequests = deepClone(DEFAULT_PARAMS_WO_OPTIONAL); + + bidRequests[0].ortb2Imp = { + ext: { gpid } + }; + + const request = spec.buildRequests(bidRequests); + const requestContent = JSON.parse(request[0].data); + + expect(requestContent).to.have.property('gpid').and.to.equal(gpid); + }); + }); }); From 27957919b0f846b43897cee777244aecb048fd96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Sok=C3=B3=C5=82?= Date: Fri, 1 Dec 2023 14:04:11 +0100 Subject: [PATCH 07/12] Support additional video params --- modules/smartadserverBidAdapter.js | 45 +++++++++-- .../modules/smartadserverBidAdapter_spec.js | 76 ++++++++++++++++++- 2 files changed, 114 insertions(+), 7 deletions(-) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index ca43c26ffd7..afafd9b9ce1 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -59,16 +59,51 @@ export const spec = { */ fillPayloadForVideoBidRequest: function(payload, videoMediaType, videoParams) { const playerSize = videoMediaType.playerSize[0]; - payload.isVideo = videoMediaType.context === 'instream'; + const map = { + maxbitrate: 'vbrmax', + maxduration: 'vdmax', + minbitrate: 'vbrmin', + minduration: 'vdmin', + placement: 'vpt', + plcmt: 'vplcmt', + skip: 'skip' + }; + payload.mediaType = VIDEO; + payload.isVideo = videoMediaType.context === 'instream'; + payload.videoData = {}; + + for (const [key, value] of Object.entries(map)) { + payload.videoData = { + ...payload.videoData, + ...this.getValuableProperty(value, videoMediaType[key]) + }; + } + payload.videoData = { - videoProtocol: this.getProtocolForVideoBidRequest(videoMediaType, videoParams), - playerWidth: playerSize[0], - playerHeight: playerSize[1], - adBreak: this.getStartDelayForVideoBidRequest(videoMediaType, videoParams) + ...payload.videoData, + ...this.getValuableProperty('playerWidth', playerSize[0]), + ...this.getValuableProperty('playerHeight', playerSize[1]), + ...this.getValuableProperty('adBreak', this.getStartDelayForVideoBidRequest(videoMediaType, videoParams)), + ...this.getValuableProperty('videoProtocol', this.getProtocolForVideoBidRequest(videoMediaType, videoParams)), + ...this.getValuableProperty('iabframeworks', videoMediaType.api && videoMediaType.api.toString()), + ...this.getValuableProperty('vpmt', videoMediaType.playbackmethod && videoMediaType.playbackmethod[0]) }; }, + /** + * Gets a property object if the value not falsy + * @param {string} property + * @param {number | string} value + * @returns object with the property or empty + */ + getValuableProperty: function(property, value) { + const type = typeof value; + + return typeof property === 'string' && (type === 'number' || type === 'string') && value + ? { [property]: value } : {}; + }, + /** * Gets the protocols from either videoParams or VideoMediaType * @param {*} videoMediaType diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index 9daa6a87826..e8cefff7709 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -786,7 +786,7 @@ describe('Smart bid adapter tests', function () { expect(request[0]).to.have.property('method').and.to.equal('POST'); const requestContent = JSON.parse(request[0].data); expect(requestContent).to.have.property('videoData'); - expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(null); + expect(requestContent.videoData).not.to.have.property('videoProtocol').eq(true); expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(2); }); @@ -833,6 +833,41 @@ describe('Smart bid adapter tests', function () { expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(6); expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(3); }); + + it('should pass additional parameters', function () { + const request = spec.buildRequests([{ + bidder: 'smartadserver', + mediaTypes: { + video: { + context: 'instream', + api: [1, 2, 3], + maxbitrate: 20, + minbitrate: 50, + maxduration: 30, + minduration: 5, + placement: 3, + playbackmethod: [2, 4], + playerSize: [[640, 480]], + plcmt: 1, + skip: 0 + } + }, + params: { + siteId: '123' + } + }]); + const requestContent = JSON.parse(request[0].data); + + expect(requestContent.videoData).to.have.property('iabframeworks').and.to.equal('1,2,3'); + expect(requestContent.videoData).not.to.have.property('skip'); + expect(requestContent.videoData).to.have.property('vbrmax').and.to.equal(20); + expect(requestContent.videoData).to.have.property('vbrmin').and.to.equal(50); + expect(requestContent.videoData).to.have.property('vdmax').and.to.equal(30); + expect(requestContent.videoData).to.have.property('vdmin').and.to.equal(5); + expect(requestContent.videoData).to.have.property('vplcmt').and.to.equal(1); + expect(requestContent.videoData).to.have.property('vpmt').and.to.equal(2); + expect(requestContent.videoData).to.have.property('vpt').and.to.equal(3); + }); }); }); @@ -1029,7 +1064,7 @@ describe('Smart bid adapter tests', function () { expect(request[0]).to.have.property('method').and.to.equal('POST'); const requestContent = JSON.parse(request[0].data); expect(requestContent).to.have.property('videoData'); - expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(null); + expect(requestContent.videoData).not.to.have.property('videoProtocol').eq(true); expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(2); }); @@ -1393,4 +1428,41 @@ describe('Smart bid adapter tests', function () { expect(requestContent).to.have.property('gpid').and.to.equal(gpid); }); }); + + describe('#getValuableProperty method', function () { + it('should return an object when calling with a string value', () => { + const obj = spec.getValuableProperty('prop', 'str'); + expect(obj).to.deep.equal({ prop: 'str' }); + }); + + it('should return an object when calling with a number value', () => { + const obj = spec.getValuableProperty('prop', 3); + expect(obj).to.deep.equal({ prop: 3 }); + }); + + it('should return an empty object when calling with a number property', () => { + const obj = spec.getValuableProperty(7, 'str'); + expect(obj).to.deep.equal({}); + }); + + it('should return an empty object when calling with a null value', () => { + const obj = spec.getValuableProperty('prop', null); + expect(obj).to.deep.equal({}); + }); + + it('should return an empty object when calling with an object value', () => { + const obj = spec.getValuableProperty('prop', {}); + expect(obj).to.deep.equal({}); + }); + + it('should return an empty object when calling with a 0 value', () => { + const obj = spec.getValuableProperty('prop', 0); + expect(obj).to.deep.equal({}); + }); + + it('should return an empty object when calling without the value argument', () => { + const obj = spec.getValuableProperty('prop'); + expect(obj).to.deep.equal({}); + }); + }); }); From dd0e8863ac77dfb15f31d5aa523d4694a6e6432a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Sok=C3=B3=C5=82?= Date: Wed, 6 Dec 2023 17:36:09 +0100 Subject: [PATCH 08/12] vpmt as array of numbers --- modules/smartadserverBidAdapter.js | 12 ++--- .../modules/smartadserverBidAdapter_spec.js | 52 +++++++++++++++---- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index afafd9b9ce1..53045cd7fc2 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -1,4 +1,4 @@ -import { deepAccess, deepClone, logError, isFn, isPlainObject } from '../src/utils.js'; +import { deepAccess, deepClone, isArrayOfNums, isFn, isInteger, isPlainObject, logError } from '../src/utils.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; @@ -86,21 +86,19 @@ export const spec = { ...this.getValuableProperty('playerHeight', playerSize[1]), ...this.getValuableProperty('adBreak', this.getStartDelayForVideoBidRequest(videoMediaType, videoParams)), ...this.getValuableProperty('videoProtocol', this.getProtocolForVideoBidRequest(videoMediaType, videoParams)), - ...this.getValuableProperty('iabframeworks', videoMediaType.api && videoMediaType.api.toString()), - ...this.getValuableProperty('vpmt', videoMediaType.playbackmethod && videoMediaType.playbackmethod[0]) + ...(isArrayOfNums(videoMediaType.api) && videoMediaType.api.length ? { iabframeworks: videoMediaType.api.toString() } : {}), + ...(isArrayOfNums(videoMediaType.playbackmethod) && videoMediaType.playbackmethod.length ? { vpmt: videoMediaType.playbackmethod } : {}) }; }, /** * Gets a property object if the value not falsy * @param {string} property - * @param {number | string} value + * @param {number} value * @returns object with the property or empty */ getValuableProperty: function(property, value) { - const type = typeof value; - - return typeof property === 'string' && (type === 'number' || type === 'string') && value + return typeof property === 'string' && isInteger(value) && value ? { [property]: value } : {}; }, diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index e8cefff7709..58b4cd8c0d0 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -841,8 +841,8 @@ describe('Smart bid adapter tests', function () { video: { context: 'instream', api: [1, 2, 3], - maxbitrate: 20, - minbitrate: 50, + maxbitrate: 50, + minbitrate: 20, maxduration: 30, minduration: 5, placement: 3, @@ -860,14 +860,46 @@ describe('Smart bid adapter tests', function () { expect(requestContent.videoData).to.have.property('iabframeworks').and.to.equal('1,2,3'); expect(requestContent.videoData).not.to.have.property('skip'); - expect(requestContent.videoData).to.have.property('vbrmax').and.to.equal(20); - expect(requestContent.videoData).to.have.property('vbrmin').and.to.equal(50); + expect(requestContent.videoData).to.have.property('vbrmax').and.to.equal(50); + expect(requestContent.videoData).to.have.property('vbrmin').and.to.equal(20); expect(requestContent.videoData).to.have.property('vdmax').and.to.equal(30); expect(requestContent.videoData).to.have.property('vdmin').and.to.equal(5); expect(requestContent.videoData).to.have.property('vplcmt').and.to.equal(1); - expect(requestContent.videoData).to.have.property('vpmt').and.to.equal(2); + expect(requestContent.videoData).to.have.property('vpmt').and.to.have.lengthOf(2); + expect(requestContent.videoData.vpmt[0]).to.equal(2); + expect(requestContent.videoData.vpmt[1]).to.equal(4); expect(requestContent.videoData).to.have.property('vpt').and.to.equal(3); }); + + it('should not pass not valuable parameters', function () { + const request = spec.buildRequests([{ + bidder: 'smartadserver', + mediaTypes: { + video: { + context: 'instream', + maxbitrate: 20, + minbitrate: null, + maxduration: 0, + playbackmethod: [], + playerSize: [[640, 480]], + plcmt: 1 + } + }, + params: { + siteId: '123' + } + }]); + const requestContent = JSON.parse(request[0].data); + + expect(requestContent.videoData).not.to.have.property('iabframeworks'); + expect(requestContent.videoData).to.have.property('vbrmax').and.to.equal(20); + expect(requestContent.videoData).not.to.have.property('vbrmin'); + expect(requestContent.videoData).not.to.have.property('vdmax'); + expect(requestContent.videoData).not.to.have.property('vdmin'); + expect(requestContent.videoData).to.have.property('vplcmt').and.to.equal(1); + expect(requestContent.videoData).not.to.have.property('vpmt'); + expect(requestContent.videoData).not.to.have.property('vpt'); + }); }); }); @@ -1430,16 +1462,16 @@ describe('Smart bid adapter tests', function () { }); describe('#getValuableProperty method', function () { - it('should return an object when calling with a string value', () => { - const obj = spec.getValuableProperty('prop', 'str'); - expect(obj).to.deep.equal({ prop: 'str' }); - }); - it('should return an object when calling with a number value', () => { const obj = spec.getValuableProperty('prop', 3); expect(obj).to.deep.equal({ prop: 3 }); }); + it('should return an empty object when calling with a string value', () => { + const obj = spec.getValuableProperty('prop', 'str'); + expect(obj).to.deep.equal({}); + }); + it('should return an empty object when calling with a number property', () => { const obj = spec.getValuableProperty(7, 'str'); expect(obj).to.deep.equal({}); From 9f5d9d21dc706ad44b338433aa6845d0809394e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Sok=C3=B3=C5=82?= Date: Wed, 3 Jan 2024 17:55:34 +0100 Subject: [PATCH 09/12] Fix comment --- modules/smartadserverBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index 53045cd7fc2..313d466bd2e 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -55,7 +55,7 @@ export const spec = { * Fills the payload with specific video attributes. * * @param {*} payload Payload that will be sent in the ServerRequest - * @param {*} videoMediaType Video media type. + * @param {*} videoMediaType Video media type */ fillPayloadForVideoBidRequest: function(payload, videoMediaType, videoParams) { const playerSize = videoMediaType.playerSize[0]; From d8d0b6707db3dd29109ee48ed971df985d934c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Sok=C3=B3=C5=82?= Date: Tue, 12 Mar 2024 14:39:48 +0100 Subject: [PATCH 10/12] Update default startDelay --- modules/smartadserverBidAdapter.js | 19 ++++++++++--------- .../modules/smartadserverBidAdapter_spec.js | 4 ++-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index 9146bba6514..923223f70c2 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -132,18 +132,19 @@ export const spec = { * @returns positive integer value of startdelay */ getStartDelayForVideoBidRequest: function(videoMediaType, videoParams) { - if (videoParams !== undefined && videoParams.startDelay) { + if (videoParams?.startDelay) { return videoParams.startDelay; - } else if (videoMediaType !== undefined) { - if (videoMediaType.startdelay == 0) { - return 1; - } else if (videoMediaType.startdelay == -1) { - return 2; - } else if (videoMediaType.startdelay == -2) { - return 3; + } else if (videoMediaType) { + switch (videoMediaType) { + case 0: + return 1; + case -1: + return 2; + case -2: + return 3; } } - return 2;// Default value for all exotic cases set to bid.params.video.startDelay midroll hence 2. + return 1; // SADR-5619 }, /** diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index 58b4cd8c0d0..d76a7b882f0 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -787,7 +787,7 @@ describe('Smart bid adapter tests', function () { const requestContent = JSON.parse(request[0].data); expect(requestContent).to.have.property('videoData'); expect(requestContent.videoData).not.to.have.property('videoProtocol').eq(true); - expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(2); + expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(1); }); it('Verify videoData params override meta values', function () { @@ -1097,7 +1097,7 @@ describe('Smart bid adapter tests', function () { const requestContent = JSON.parse(request[0].data); expect(requestContent).to.have.property('videoData'); expect(requestContent.videoData).not.to.have.property('videoProtocol').eq(true); - expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(2); + expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(1); }); it('Verify videoData params override meta values', function () { From 78f1201cd91705f608b4df85ef32f3762eae6848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Sok=C3=B3=C5=82?= Date: Tue, 19 Mar 2024 10:50:43 +0100 Subject: [PATCH 11/12] Include videoMediaType's startdelay --- modules/smartadserverBidAdapter.js | 4 +--- .../modules/smartadserverBidAdapter_spec.js | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index 923223f70c2..8f2cd7e2477 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -135,9 +135,7 @@ export const spec = { if (videoParams?.startDelay) { return videoParams.startDelay; } else if (videoMediaType) { - switch (videoMediaType) { - case 0: - return 1; + switch (videoMediaType.startdelay) { case -1: return 2; case -2: diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index d76a7b882f0..200cff3f659 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -1143,6 +1143,28 @@ describe('Smart bid adapter tests', function () { expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(6); expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(3); }); + + it('should handle value of videoMediaType.startdelay', function () { + const request = spec.buildRequests([{ + bidder: 'smartadserver', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[640, 480]], + startdelay: -2 + } + }, + params: { + siteId: 123, + pageId: 456, + formatId: 78 + } + }]); + + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('videoData'); + expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(3); + }); }); describe('External ids tests', function () { From f4dd20355a06d1613d686a991c20c6d6ddbc5fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Sok=C3=B3=C5=82?= Date: Mon, 25 Mar 2024 10:22:09 +0100 Subject: [PATCH 12/12] Handle specified midroll --- modules/smartadserverBidAdapter.js | 11 +++++----- .../modules/smartadserverBidAdapter_spec.js | 22 +++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index 8f2cd7e2477..7edaaa36957 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -134,12 +134,11 @@ export const spec = { getStartDelayForVideoBidRequest: function(videoMediaType, videoParams) { if (videoParams?.startDelay) { return videoParams.startDelay; - } else if (videoMediaType) { - switch (videoMediaType.startdelay) { - case -1: - return 2; - case -2: - return 3; + } else if (videoMediaType?.startdelay) { + if (videoMediaType.startdelay > 0 || videoMediaType.startdelay == -1) { + return 2; + } else if (videoMediaType.startdelay == -2) { + return 3; } } return 1; // SADR-5619 diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index 200cff3f659..b01d95e2a4c 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -1165,6 +1165,28 @@ describe('Smart bid adapter tests', function () { expect(requestContent).to.have.property('videoData'); expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(3); }); + + it('should return specified value of videoMediaType.startdelay', function () { + const request = spec.buildRequests([{ + bidder: 'smartadserver', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[640, 480]], + startdelay: 60 + } + }, + params: { + siteId: 123, + pageId: 456, + formatId: 78 + } + }]); + + const requestContent = JSON.parse(request[0].data); + expect(requestContent).to.have.property('videoData'); + expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(2); + }); }); describe('External ids tests', function () {