diff --git a/modules/imdsBidAdapter.js b/modules/imdsBidAdapter.js index 545a0bd1ac3..d6f3df94409 100644 --- a/modules/imdsBidAdapter.js +++ b/modules/imdsBidAdapter.js @@ -1,6 +1,6 @@ 'use strict'; -import {deepAccess, deepSetValue, getAdUnitSizes, isFn, isPlainObject, logWarn} from '../src/utils.js'; +import {deepAccess, deepSetValue, getAdUnitSizes, isFn, isPlainObject, logWarn, mergeDeep} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {includes} from '../src/polyfill.js'; @@ -8,7 +8,8 @@ import {config} from '../src/config.js'; const BID_SCHEME = 'https://'; const BID_DOMAIN = 'technoratimedia.com'; -const USER_SYNC_HOST = 'https://ad-cdn.technoratimedia.com'; +const USER_SYNC_IFRAME_URL = 'https://ad-cdn.technoratimedia.com/html/usersync.html'; +const USER_SYNC_PIXEL_URL = 'https://sync.technoratimedia.com/services'; const VIDEO_PARAMS = [ 'minduration', 'maxduration', 'startdelay', 'placement', 'linearity', 'mimes', 'protocols', 'api' ]; const BLOCKED_AD_SIZES = [ '1x1', @@ -38,11 +39,11 @@ export const spec = { return; } const refererInfo = bidderRequest.refererInfo; - const openRtbBidRequest = { + // start with some defaults, overridden by anything set in ortb2, if provided. + const openRtbBidRequest = mergeDeep({ id: bidderRequest.bidderRequestId, site: { - // TODO: does the fallback make sense here? - domain: refererInfo.domain || location.hostname, + domain: refererInfo.domain, page: refererInfo.page, ref: refererInfo.ref }, @@ -50,7 +51,7 @@ export const spec = { ua: navigator.userAgent }, imp: [] - }; + }, bidderRequest.ortb2 || {}); const tmax = bidderRequest.timeout; if (tmax) { @@ -101,9 +102,17 @@ export const spec = { } }); - // CCPA - if (bidderRequest.uspConsent) { - deepSetValue(openRtbBidRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); + // Move us_privacy from regs.ext to regs if there isn't already a us_privacy in regs + if (openRtbBidRequest.regs?.ext?.us_privacy && !openRtbBidRequest.regs?.us_privacy) { + deepSetValue(openRtbBidRequest, 'regs.us_privacy', openRtbBidRequest.regs.ext.us_privacy); + } + + // Remove regs.ext.us_privacy + if (openRtbBidRequest.regs?.ext?.us_privacy) { + delete openRtbBidRequest.regs.ext.us_privacy; + if (Object.keys(openRtbBidRequest.regs.ext).length < 1) { + delete openRtbBidRequest.regs.ext; + } } // User ID @@ -117,7 +126,7 @@ export const spec = { if (openRtbBidRequest.imp.length && seatId) { return { method: 'POST', - url: `${BID_SCHEME}${seatId}.${BID_DOMAIN}/openrtb/bids/${seatId}?src=$$REPO_AND_VERSION$$`, + url: `${BID_SCHEME}${seatId}.${BID_DOMAIN}/openrtb/bids/${seatId}?src=pbjs%2F$prebid.version$`, data: openRtbBidRequest, options: { contentType: 'application/json', @@ -294,16 +303,31 @@ export const spec = { } return bids; }, - getUserSyncs: function (syncOptions, serverResponses) { + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { const syncs = []; + const queryParams = ['src=pbjs%2F$prebid.version$']; + if (gdprConsent) { + queryParams.push(`gdpr=${Number(gdprConsent.gdprApplies && 1)}&consent=${encodeURIComponent(gdprConsent.consentString || '')}`); + } + if (uspConsent) { + queryParams.push('us_privacy=' + encodeURIComponent(uspConsent)); + } + if (gppConsent) { + queryParams.push('gpp=' + encodeURIComponent(gppConsent.gppString || '') + '&gppsid=' + encodeURIComponent((gppConsent.applicableSections || []).join(','))); + } + if (syncOptions.iframeEnabled) { syncs.push({ type: 'iframe', - url: `${USER_SYNC_HOST}/html/usersync.html?src=$$REPO_AND_VERSION$$` + url: `${USER_SYNC_IFRAME_URL}?${queryParams.join('&')}` + }); + } else if (syncOptions.pixelEnabled) { + syncs.push({ + type: 'pixel', + url: `${USER_SYNC_PIXEL_URL}?srv=cs&${queryParams.join('&')}` }); - } else { - logWarn('IMDS: Please enable iframe based user sync.'); } + return syncs; } }; diff --git a/test/spec/modules/imdsBidAdapter_spec.js b/test/spec/modules/imdsBidAdapter_spec.js index ce04fabe02b..7d808a2528f 100644 --- a/test/spec/modules/imdsBidAdapter_spec.js +++ b/test/spec/modules/imdsBidAdapter_spec.js @@ -192,12 +192,45 @@ describe('imdsBidAdapter ', function () { timeout: 3000 }; - let bidderRequestWithCCPA = { + let bidderRequestWithUSPInExt = { bidderRequestId: 'xyz123', refererInfo: { referer: 'https://test.com/foo/bar' }, - uspConsent: '1YYY' + ortb2: { + regs: { + ext: { + us_privacy: '1YYY' + } + } + } + }; + + let bidderRequestWithUSPInRegs = { + bidderRequestId: 'xyz123', + refererInfo: { + referer: 'https://test.com/foo/bar' + }, + ortb2: { + regs: { + us_privacy: '1YYY' + } + } + }; + + let bidderRequestWithUSPAndOthersInExt = { + bidderRequestId: 'xyz123', + refererInfo: { + referer: 'https://test.com/foo/bar' + }, + ortb2: { + regs: { + ext: { + extra: 'extra item', + us_privacy: '1YYY' + } + } + } }; let validBidRequestWithUserIds = { @@ -391,7 +424,7 @@ describe('imdsBidAdapter ', function () { let req = spec.buildRequests([badFloorBidRequest], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=pbjs%2F$prebid.version$'); expect(req.data.id).to.equal('xyz123'); expect(req.data.imp).to.eql([ { @@ -422,7 +455,7 @@ describe('imdsBidAdapter ', function () { let req = spec.buildRequests([badFloorBidRequest], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=pbjs%2F$prebid.version$'); expect(req.data.id).to.equal('xyz123'); expect(req.data.imp).to.eql([ { @@ -454,7 +487,7 @@ describe('imdsBidAdapter ', function () { let req = spec.buildRequests([newPosBidRequest], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=pbjs%2F$prebid.version$'); expect(req.data.id).to.equal('xyz123'); expect(req.data.imp).to.eql([ { @@ -485,7 +518,7 @@ describe('imdsBidAdapter ', function () { let req = spec.buildRequests([newPosBidRequest], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=pbjs%2F$prebid.version$'); expect(req.data.id).to.equal('xyz123'); expect(req.data.imp).to.eql([ { @@ -577,7 +610,7 @@ describe('imdsBidAdapter ', function () { let req = spec.buildRequests([validBidRequestVideo], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=pbjs%2F$prebid.version$'); expect(req.data.id).to.equal('xyz123'); expect(req.data.imp).to.eql([ { @@ -635,7 +668,7 @@ describe('imdsBidAdapter ', function () { let req = spec.buildRequests([validBidRequestVideo], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=pbjs%2F$prebid.version$'); expect(req.data.id).to.equal('xyz123'); expect(req.data.imp).to.eql([ { @@ -701,16 +734,42 @@ describe('imdsBidAdapter ', function () { } ]); }); - it('should contain the CCPA privacy string when UspConsent is in bidder request', function () { + it('should have us_privacy string in regs instead of regs.ext bidder request', function () { + let req = spec.buildRequests([validBidRequest], bidderRequestWithUSPInExt); + expect(req).be.an('object'); + expect(req).to.have.property('method', 'POST'); + expect(req).to.have.property('url'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?'); + expect(req.data).to.exist.and.to.be.an('object'); + expect(req.data.id).to.equal('xyz123'); + expect(req.data.regs.us_privacy).to.equal('1YYY'); + expect(req.data.regs.ext).to.not.exist; + expect(req.data.imp).to.eql([expectedDataImp1]); + }); + it('should accept us_privacy string in regs', function () { + // banner test + let req = spec.buildRequests([validBidRequest], bidderRequestWithUSPInRegs); + expect(req).be.an('object'); + expect(req).to.have.property('method', 'POST'); + expect(req).to.have.property('url'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?'); + expect(req.data).to.exist.and.to.be.an('object'); + expect(req.data.id).to.equal('xyz123'); + expect(req.data.regs.us_privacy).to.equal('1YYY'); + expect(req.data.regs.ext).to.not.exist; + expect(req.data.imp).to.eql([expectedDataImp1]); + }); + it('should not remove regs.ext when moving us_privacy if there are other things in regs.ext', function () { // banner test - let req = spec.buildRequests([validBidRequest], bidderRequestWithCCPA); + let req = spec.buildRequests([validBidRequest], bidderRequestWithUSPAndOthersInExt); expect(req).be.an('object'); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?'); expect(req.data).to.exist.and.to.be.an('object'); expect(req.data.id).to.equal('xyz123'); - expect(req.data.regs.ext.us_privacy).to.equal('1YYY'); + expect(req.data.regs.us_privacy).to.equal('1YYY'); + expect(req.data.regs.ext.extra).to.equal('extra item'); expect(req.data.imp).to.eql([expectedDataImp1]); }); it('should contain user object when user ids are present in the bidder request', function () { @@ -780,14 +839,14 @@ describe('imdsBidAdapter ', function () { let req = spec.buildRequests([validBannerBidRequest], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?src=pbjs%2F$prebid.version$'); }); it('should return valid bid request for video impression', function () { let req = spec.buildRequests([validVideoBidReq], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?src=pbjs%2F$prebid.version$'); }); }); @@ -887,7 +946,7 @@ describe('imdsBidAdapter ', function () { let req = spec.buildRequests([validBidReq], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?src=pbjs%2F$prebid.version$'); expect(req.data).to.have.property('source'); expect(req.data.source).to.have.property('ext'); expect(req.data.source.ext).to.have.property('schain'); @@ -1293,20 +1352,42 @@ describe('imdsBidAdapter ', function () { }); }); describe('getUserSyncs', function () { - it('should return a usersync when iframes is enabled', function () { + it('should return an iframe usersync when iframes is enabled', function () { let usersyncs = spec.getUserSyncs({ iframeEnabled: true }, null); - expect(usersyncs).to.be.an('array').that.is.not.empty; + expect(usersyncs).to.be.an('array').with.lengthOf(1); expect(usersyncs[0]).to.have.property('type', 'iframe'); expect(usersyncs[0]).to.have.property('url'); expect(usersyncs[0].url).to.contain('https://ad-cdn.technoratimedia.com/html/usersync.html'); }); - it('should not return a usersync when iframes are not enabled', function () { + it('should return a pixel usersync when pixels is enabled', function () { let usersyncs = spec.getUserSyncs({ pixelEnabled: true }, null); + expect(usersyncs).to.be.an('array').with.lengthOf(1); + expect(usersyncs[0]).to.have.property('type', 'pixel'); + expect(usersyncs[0]).to.have.property('url'); + expect(usersyncs[0].url).to.contain('https://sync.technoratimedia.com/services'); + }); + + it('should return an iframe usersync when both iframe and pixels is enabled', function () { + let usersyncs = spec.getUserSyncs({ + iframeEnabled: true, + pixelEnabled: true + }, null); + expect(usersyncs).to.be.an('array').with.lengthOf(1); + expect(usersyncs[0]).to.have.property('type', 'iframe'); + expect(usersyncs[0]).to.have.property('url'); + expect(usersyncs[0].url).to.contain('https://ad-cdn.technoratimedia.com/html/usersync.html'); + }); + + it('should not return a usersync when neither iframes nor pixel are enabled', function () { + let usersyncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: false + }, null); expect(usersyncs).to.be.an('array').that.is.empty; }); });