Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AdKernelADN adapter GDPR support #2624

Merged
merged 1 commit into from
Jun 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions modules/adkernelAdnBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down Expand Up @@ -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: {
Expand All @@ -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) {
Expand All @@ -91,19 +102,15 @@ function buildBid(tag) {
}

export const spec = {

code: 'adkernelAdn',

supportedMediaTypes: [BANNER, VIDEO],

isBidRequestValid: function(bidRequest) {
return 'params' in bidRequest && (typeof bidRequest.params.host === 'undefined' || typeof bidRequest.params.host === 'string') &&
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];
Expand All @@ -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' : ''}`,
Expand Down
104 changes: 68 additions & 36 deletions test/spec/modules/adkernelAdnBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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,
Expand All @@ -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],
Expand All @@ -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],
Expand Down Expand Up @@ -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', () => {
Expand All @@ -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');
});
Expand All @@ -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');
Expand All @@ -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);
});
});

Expand Down Expand Up @@ -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);
});
Expand Down