From 9aa2a4f73b022da3421b7868fbd969f0af930044 Mon Sep 17 00:00:00 2001 From: Denis Logachev Date: Fri, 1 Jun 2018 21:42:51 +0300 Subject: [PATCH] AdKernelADN adapter GDPR support (#2624) Ad units size issue fix --- modules/adkernelAdnBidAdapter.js | 32 ++++-- .../modules/adkernelAdnBidAdapter_spec.js | 104 ++++++++++++------ 2 files changed, 88 insertions(+), 48 deletions(-) diff --git a/modules/adkernelAdnBidAdapter.js b/modules/adkernelAdnBidAdapter.js index d99ee023772e..16b2efce901b 100644 --- a/modules/adkernelAdnBidAdapter.js +++ b/modules/adkernelAdnBidAdapter.js @@ -20,12 +20,12 @@ function buildImp(bidRequest) { }; if (bidRequest.mediaType === BANNER || utils.deepAccess(bidRequest, `mediaTypes.banner`) || (bidRequest.mediaTypes === undefined && bidRequest.mediaType === undefined)) { - let sizes = canonicalizeSizesArray(utils.deepAccess(bidRequest, `mediaTypes.banner.sizes`) || bidRequest.sizes); + let sizes = canonicalizeSizesArray(bidRequest.sizes); imp.banner = { format: utils.parseSizesInput(sizes) } } else if (bidRequest.mediaType === VIDEO || utils.deepAccess(bidRequest, `mediaTypes.video`)) { - let size = utils.deepAccess(bidRequest, `mediaTypes.video.playerSize`) || canonicalizeSizesArray(bidRequest.sizes)[0]; + let size = canonicalizeSizesArray(bidRequest.sizes)[0]; imp.video = { w: size[0], h: size[1], @@ -54,9 +54,9 @@ function canonicalizeSizesArray(sizes) { return sizes; } -function buildRequestParams(auctionId, transactionId, tags) { +function buildRequestParams(tags, auctionId, transactionId, gdprConsent) { let loc = utils.getTopWindowLocation(); - return { + let req = { id: auctionId, tid: transactionId, site: { @@ -66,6 +66,17 @@ function buildRequestParams(auctionId, transactionId, tags) { }, imp: tags }; + + if (gdprConsent && (gdprConsent.gdprApplies !== undefined || gdprConsent.consentString !== undefined)) { + req.user = {}; + if (gdprConsent.gdprApplies !== undefined) { + req.user.gdpr = ~~(gdprConsent.gdprApplies); + } + if (gdprConsent.consentString !== undefined) { + req.user.consent = gdprConsent.consentString; + } + } + return req; } function buildBid(tag) { @@ -91,9 +102,7 @@ function buildBid(tag) { } export const spec = { - code: 'adkernelAdn', - supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function(bidRequest) { @@ -101,9 +110,7 @@ export const spec = { typeof bidRequest.params.pubId === 'number'; }, - buildRequests: function(bidRequests) { - let transactionId; - let auctionId; + buildRequests: function(bidRequests, bidderRequest) { let dispatch = bidRequests.map(buildImp) .reduce((acc, curr, index) => { let bidRequest = bidRequests[index]; @@ -112,14 +119,15 @@ export const spec = { acc[host] = acc[host] || {}; acc[host][pubId] = acc[host][pubId] || []; acc[host][pubId].push(curr); - transactionId = bidRequest.transactionId; - auctionId = bidRequest.bidderRequestId; return acc; }, {}); + let auctionId = bidderRequest.auctionId; + let gdprConsent = bidderRequest.gdprConsent; + let transactionId = bidderRequest.transactionId; let requests = []; Object.keys(dispatch).forEach(host => { Object.keys(dispatch[host]).forEach(pubId => { - let request = buildRequestParams(auctionId, transactionId, dispatch[host][pubId]); + let request = buildRequestParams(dispatch[host][pubId], auctionId, transactionId, gdprConsent); requests.push({ method: 'POST', url: `//${host}/tag?account=${pubId}&pb=1${isRtbDebugEnabled() ? '&debug=1' : ''}`, diff --git a/test/spec/modules/adkernelAdnBidAdapter_spec.js b/test/spec/modules/adkernelAdnBidAdapter_spec.js index a7bd959ee8ea..af9ccfd78ed3 100644 --- a/test/spec/modules/adkernelAdnBidAdapter_spec.js +++ b/test/spec/modules/adkernelAdnBidAdapter_spec.js @@ -7,6 +7,7 @@ describe('AdkernelAdn adapter', () => { bidder: 'adkernelAdn', transactionId: 'transact0', bidderRequestId: 'req0', + auctionId: '5c66da22-426a-4bac-b153-77360bef5337', bidId: 'bidid_1', params: { pubId: 1 @@ -16,8 +17,9 @@ describe('AdkernelAdn adapter', () => { }, bid2_pub1 = { bidder: 'adkernelAdn', - transactionId: 'transact1', - bidderRequestId: 'req1', + transactionId: 'transact0', + bidderRequestId: 'req0', + auctionId: '5c66da22-426a-4bac-b153-77360bef5337', bidId: 'bidid_2', params: { pubId: 1 @@ -29,6 +31,7 @@ describe('AdkernelAdn adapter', () => { bidder: 'adkernelAdn', transactionId: 'transact2', bidderRequestId: 'req1', + auctionId: '5c66da22-426a-4bac-b153-77360bef5337', bidId: 'bidid_3', params: { pubId: 7, @@ -40,6 +43,7 @@ describe('AdkernelAdn adapter', () => { bidder: 'adkernelAdn', transactionId: 'transact3', bidderRequestId: 'req1', + auctionId: '5c66da22-426a-4bac-b153-77360bef5337', bidId: 'bidid_4', mediaType: 'video', sizes: [640, 300], @@ -56,7 +60,9 @@ describe('AdkernelAdn adapter', () => { bidder: 'adkernelAdn', transactionId: 'transact3', bidderRequestId: 'req1', + auctionId: '5c66da22-426a-4bac-b153-77360bef5337', bidId: 'bidid_5', + sizes: [[1920, 1080]], mediaTypes: { video: { playerSize: [1920, 1080], @@ -106,8 +112,7 @@ describe('AdkernelAdn adapter', () => { describe('input parameters validation', () => { it('empty request shouldn\'t generate exception', () => { - expect(spec.isBidRequestValid({ - bidderCode: 'adkernelAdn' + expect(spec.isBidRequestValid({bidderCode: 'adkernelAdn' })).to.be.equal(false); }); it('request without pubid should be ignored', () => { @@ -130,25 +135,32 @@ describe('AdkernelAdn adapter', () => { }); }); - describe('banner request building', () => { - let pbRequest; - let tagRequest; - - before(() => { - let mock = sinon.stub(utils, 'getTopWindowLocation').callsFake(() => { - return { - protocol: 'https:', - hostname: 'example.com', - host: 'example.com', - pathname: '/index.html', - href: 'https://example.com/index.html' - }; - }); - pbRequest = spec.buildRequests([bid1_pub1])[0]; - tagRequest = JSON.parse(pbRequest.data); - mock.restore(); + function buildRequest(bidRequests, bidderRequest = {}) { + let mock = sinon.stub(utils, 'getTopWindowLocation').callsFake(() => { + return { + protocol: 'https:', + hostname: 'example.com', + host: 'example.com', + pathname: '/index.html', + href: 'https://example.com/index.html' + }; }); + bidderRequest.auctionId = bidRequests[0].auctionId; + bidderRequest.transactionId = bidRequests[0].transactionId; + bidderRequest.bidderRequestId = bidRequests[0].bidderRequestId; + + let pbRequests = spec.buildRequests(bidRequests, bidderRequest); + let tagRequests = pbRequests.map(r => JSON.parse(r.data)); + mock.restore(); + + return [pbRequests, tagRequests]; + } + + describe('banner request building', () => { + let [_, tagRequests] = buildRequest([bid1_pub1]); + let tagRequest = tagRequests[0]; + it('should have request id', () => { expect(tagRequest).to.have.property('id'); }); @@ -169,11 +181,35 @@ describe('AdkernelAdn adapter', () => { expect(tagRequest.site).to.have.property('page', 'https://example.com/index.html'); expect(tagRequest.site).to.have.property('secure', 1); }); + + it('should not have user object', () => { + expect(tagRequest).to.not.have.property('user'); + }); + + it('shouldn\'t contain gdpr-related information for default request', () => { + let [_, tagRequests] = buildRequest([bid1_pub1]); + expect(tagRequests[0]).to.not.have.property('user'); + }); + + it('should contain gdpr-related information if consent is configured', () => { + let [_, bidRequests] = buildRequest([bid1_pub1], + {gdprConsent: {gdprApplies: true, consentString: 'test-consent-string'}}); + expect(bidRequests[0]).to.have.property('user'); + expect(bidRequests[0].user).to.have.property('gdpr', 1); + expect(bidRequests[0].user).to.have.property('consent', 'test-consent-string'); + }); + + it('should\'t contain consent string if gdpr isn\'t applied', () => { + let [_, bidRequests] = buildRequest([bid1_pub1], {gdprConsent: {gdprApplies: false}}); + expect(bidRequests[0]).to.have.property('user'); + expect(bidRequests[0].user).to.have.property('gdpr', 0); + expect(bidRequests[0].user).to.not.have.property('consent'); + }); }); describe('video request building', () => { - let pbRequest = spec.buildRequests([bid_video1, bid_video2])[0]; - let tagRequest = JSON.parse(pbRequest.data); + let [_, tagRequests] = buildRequest([bid_video1, bid_video2]); + let tagRequest = tagRequests[0]; it('should have video object', () => { expect(tagRequest.imp[0]).to.have.property('video'); @@ -193,24 +229,20 @@ describe('AdkernelAdn adapter', () => { describe('requests routing', () => { it('should issue a request for each publisher', () => { - let pbRequests = spec.buildRequests([bid1_pub1, bid_video1]); + let [pbRequests, tagRequests] = buildRequest([bid1_pub1, bid_video1]); expect(pbRequests).to.have.length(2); expect(pbRequests[0].url).to.have.string(`account=${bid1_pub1.params.pubId}`); expect(pbRequests[1].url).to.have.string(`account=${bid1_pub2.params.pubId}`); - let tagRequest1 = JSON.parse(pbRequests[0].data); - let tagRequest2 = JSON.parse(pbRequests[1].data); - expect(tagRequest1.imp).to.have.length(1); - expect(tagRequest2.imp).to.have.length(1); + expect(tagRequests[0].imp).to.have.length(1); + expect(tagRequests[1].imp).to.have.length(1); }); it('should issue a request for each host', () => { - let pbRequests = spec.buildRequests([bid1_pub1, bid1_pub2]); + let [pbRequests, tagRequests] = buildRequest([bid1_pub1, bid1_pub2]); expect(pbRequests).to.have.length(2); expect(pbRequests[0].url).to.have.string('//tag.adkernel.com/tag'); expect(pbRequests[1].url).to.have.string(`//${bid1_pub2.params.host}/tag`); - let tagRequest1 = JSON.parse(pbRequests[0].data); - let tagRequest2 = JSON.parse(pbRequests[1].data); - expect(tagRequest1.imp).to.have.length(1); - expect(tagRequest2.imp).to.have.length(1); + expect(tagRequests[0].imp).to.have.length(1); + expect(tagRequests[1].imp).to.have.length(1); }); }); @@ -257,11 +289,11 @@ describe('AdkernelAdn adapter', () => { expect(syncs[0]).to.have.property('url', 'https://dsp.adkernel.com/sync'); }); it('should handle user-sync only response', () => { - let request = spec.buildRequests([bid1_pub1])[0]; - let resp = spec.interpretResponse({body: usersyncOnlyResponse}, request); + let [pbRequests, tagRequests] = buildRequest([bid1_pub1]); + let resp = spec.interpretResponse({body: usersyncOnlyResponse}, pbRequests[0]); expect(resp).to.have.length(0); }); - it('shouldn\' fail in empty response', () => { + it('shouldn\' fail on empty response', () => { let syncs = spec.getUserSyncs({iframeEnabled: true}, [{body: ''}]); expect(syncs).to.have.length(0); });