diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index b425b80149a..2f116323a42 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -21,7 +21,7 @@ export const spec = { isBidRequestValid: function (bidRequest) { return !!(bidRequest.params.unit && bidRequest.params.delDomain); }, - buildRequests: function (bidRequests) { + buildRequests: function (bidRequests, bidderRequest) { if (bidRequests.length === 0) { return []; } @@ -31,12 +31,12 @@ export const spec = { // build banner requests if (bannerBids.length > 0) { - requests.push(buildOXBannerRequest(bannerBids)); + requests.push(buildOXBannerRequest(bannerBids, bidderRequest)); } // build video requests if (videoBids.length > 0) { videoBids.forEach(videoBid => { - requests.push(buildOXVideoRequest(videoBid)) + requests.push(buildOXVideoRequest(videoBid, bidderRequest)) }); } @@ -177,10 +177,11 @@ function getMediaTypeFromRequest(serverRequest) { return /avjp$/.test(serverRequest.url) ? VIDEO : BANNER; } -function buildCommonQueryParamsFromBids(bids) { +function buildCommonQueryParamsFromBids(bids, bidderRequest) { const isInIframe = utils.inIframe(); + let defaultParams; - return { + defaultParams = { ju: config.getConfig('pageUrl') || utils.getTopWindowUrl(), jr: utils.getTopWindowReferrer(), ch: document.charSet || document.characterSet, @@ -190,12 +191,30 @@ function buildCommonQueryParamsFromBids(bids) { tws: getViewportDimensions(isInIframe), be: 1, dddid: utils._map(bids, bid => bid.transactionId).join(','), - nocache: new Date().getTime(), + nocache: new Date().getTime() }; + + if (utils.deepAccess(bidderRequest, 'gdprConsent')) { + let gdprConsentConfig = bidderRequest.gdprConsent; + + if (gdprConsentConfig.consentString !== undefined) { + defaultParams.gdpr_consent = gdprConsentConfig.consentString; + } + + if (gdprConsentConfig.gdprApplies !== undefined) { + defaultParams.gdpr = gdprConsentConfig.gdprApplies ? 1 : 0; + } + + if (config.getConfig('consentManagement.cmpApi') === 'iab') { + defaultParams.x_gdpr_f = 1; + } + } + + return defaultParams; } -function buildOXBannerRequest(bids) { - let queryParams = buildCommonQueryParamsFromBids(bids); +function buildOXBannerRequest(bids, bidderRequest) { + let queryParams = buildCommonQueryParamsFromBids(bids, bidderRequest); queryParams.auid = utils._map(bids, bid => bid.params.unit).join(','); queryParams.aus = utils._map(bids, bid => utils.parseSizesInput(bid.sizes).join(',')).join('|'); @@ -240,9 +259,9 @@ function buildOXBannerRequest(bids) { }; } -function buildOXVideoRequest(bid) { +function buildOXVideoRequest(bid, bidderRequest) { let url = `//${bid.params.delDomain}/v/1.0/avjp`; - let oxVideoParams = generateVideoParameters(bid); + let oxVideoParams = generateVideoParameters(bid, bidderRequest); return { method: 'GET', url: url, @@ -251,8 +270,8 @@ function buildOXVideoRequest(bid) { }; } -function generateVideoParameters(bid) { - let queryParams = buildCommonQueryParamsFromBids([bid]); +function generateVideoParameters(bid, bidderRequest) { + let queryParams = buildCommonQueryParamsFromBids([bid], bidderRequest); let oxVideoConfig = utils.deepAccess(bid, 'params.video') || {}; let context = utils.deepAccess(bid, 'mediaTypes.video.context'); let playerSize = utils.deepAccess(bid, 'mediaTypes.video.playerSize'); diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index e4a1c9a979d..3585987e045 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -2,6 +2,7 @@ import {expect} from 'chai'; import {spec, resetBoPixel} from 'modules/openxBidAdapter'; import {newBidder} from 'src/adapters/bidderFactory'; import {userSync} from 'src/userSync'; +import {config} from 'src/config'; import * as utils from 'src/utils'; const URLBASE = '/w/1.0/arj'; @@ -343,6 +344,186 @@ describe('OpenxAdapter', () => { expect(dataParams.bc).to.exist; expect(dataParams.bc).to.equal('hb_override'); }); + + it('should not send any consent management properties', function () { + const request = spec.buildRequests(bidRequestsWithMediaTypes); + expect(request[0].data.gdpr).to.equal(undefined); + expect(request[0].data.gdpr_consent).to.equal(undefined); + expect(request[0].data.x_gdpr_f).to.equal(undefined); + }); + + describe('when there is a consent management framework', () => { + let bidRequests; + let mockConfig; + let bidderRequest; + const IAB_CONSENT_FRAMEWORK_CODE = 1; + + beforeEach(() => { + bidRequests = [{ + bidder: 'openx', + params: { + unit: '12345678-banner', + delDomain: 'test-del-domain' + }, + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + bidId: 'test-bid-id', + bidderRequestId: 'test-bidder-request-id', + auctionId: 'test-auction-id' + }, { + 'bidder': 'openx', + 'mediaTypes': { + video: { + playerSize: [640, 480] + } + }, + 'params': { + 'unit': '12345678-video', + 'delDomain': 'test-del-domain' + }, + 'adUnitCode': 'adunit-code', + + bidId: 'test-bid-id', + bidderRequestId: 'test-bidder-request-id', + auctionId: 'test-auction-id', + transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e' + }]; + }); + + afterEach(function () { + config.getConfig.restore(); + }); + + describe('when GDPR applies', function () { + beforeEach(function () { + bidderRequest = { + gdprConsent: { + consentString: 'test-gdpr-consent-string', + gdprApplies: true + } + }; + + mockConfig = { + consentManagement: { + cmpApi: 'iab', + timeout: 1111, + allowAuctionWithoutConsent: 'cancel' + } + }; + + sinon.stub(config, 'getConfig').callsFake((key) => { + return utils.deepAccess(mockConfig, key); + }); + }); + + it('should send a signal to specify that GDPR applies to this request', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request[0].data.gdpr).to.equal(1); + expect(request[1].data.gdpr).to.equal(1); + }); + + it('should send the consent string', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request[0].data.gdpr_consent).to.equal(bidderRequest.gdprConsent.consentString); + expect(request[1].data.gdpr_consent).to.equal(bidderRequest.gdprConsent.consentString); + }); + + it('should send the consent management framework code', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request[0].data.x_gdpr_f).to.equal(IAB_CONSENT_FRAMEWORK_CODE); + expect(request[1].data.x_gdpr_f).to.equal(IAB_CONSENT_FRAMEWORK_CODE); + }); + }); + + describe('when GDPR does not apply', function () { + beforeEach(function () { + bidderRequest = { + gdprConsent: { + consentString: 'test-gdpr-consent-string', + gdprApplies: false + } + }; + + mockConfig = { + consentManagement: { + cmpApi: 'iab', + timeout: 1111, + allowAuctionWithoutConsent: 'cancel' + } + }; + + sinon.stub(config, 'getConfig').callsFake((key) => { + return utils.deepAccess(mockConfig, key); + }); + }); + + it('should not send a signal to specify that GDPR does not apply to this request', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request[0].data.gdpr).to.equal(0); + expect(request[1].data.gdpr).to.equal(0); + }); + + it('should send the consent string', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request[0].data.gdpr_consent).to.equal(bidderRequest.gdprConsent.consentString); + expect(request[1].data.gdpr_consent).to.equal(bidderRequest.gdprConsent.consentString); + }); + + it('should send the consent management framework code', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request[0].data.x_gdpr_f).to.equal(IAB_CONSENT_FRAMEWORK_CODE); + expect(request[1].data.x_gdpr_f).to.equal(IAB_CONSENT_FRAMEWORK_CODE); + }); + }); + + describe('when GDPR consent has undefined data', function () { + beforeEach(function () { + bidderRequest = { + gdprConsent: { + consentString: 'test-gdpr-consent-string', + gdprApplies: true + } + }; + + mockConfig = { + consentManagement: { + cmpApi: 'iab', + timeout: 1111, + allowAuctionWithoutConsent: 'cancel' + } + }; + + sinon.stub(config, 'getConfig').callsFake((key) => { + return utils.deepAccess(mockConfig, key); + }); + }); + + it('should not send a signal to specify whether GDPR applies to this request, when GDPR application is undefined', function () { + delete bidderRequest.gdprConsent.gdprApplies; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request[0].data).to.not.have.property('gdpr'); + expect(request[1].data).to.not.have.property('gdpr'); + }); + + it('should not send the consent string, when consent string is undefined', function () { + delete bidderRequest.gdprConsent.consentString; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request[0].data).to.not.have.property('gdpr_consent'); + expect(request[1].data).to.not.have.property('gdpr_consent'); + }); + + it('should not send the consent management framework code, when format is undefined', function () { + delete mockConfig.consentManagement.cmpApi; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request[0].data).to.not.have.property('x_gdpr_f'); + expect(request[1].data).to.not.have.property('x_gdpr_f'); + }); + }); + }); }); describe('buildRequests for video', () => {