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

InvibesBidAdapter - gdpr support #3151

Merged
merged 1 commit into from
Oct 8, 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
60 changes: 38 additions & 22 deletions modules/invibesBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ const CONSTANTS = {
TIME_TO_LIVE: 300,
DEFAULT_CURRENCY: 'EUR',
PREBID_VERSION: 1,
METHOD: 'GET'
METHOD: 'GET',
INVIBES_VENDOR_ID: 436
};

export const spec = {
Expand All @@ -23,9 +24,7 @@ export const spec = {
* @param bidderRequest
* @return ServerRequest[]
*/
buildRequests: function (bidRequests, bidderRequest) {
return buildRequest(bidRequests, bidderRequest != null ? bidderRequest.auctionStart : null);
},
buildRequests: buildRequest,
/**
* @param {*} responseObj
* @param {requestParams} bidRequests
Expand Down Expand Up @@ -55,6 +54,11 @@ let invibes = topWin.invibes = topWin.invibes || {};
let _customUserSync;

function isBidRequestValid(bid) {
if (invibes && typeof invibes.bidResponse === 'object') {
utils.logInfo('Invibes Adapter - Bid response already received. Invibes only responds to one bid request per user visit');
return false;
}

if (typeof bid.params !== 'object') {
return false;
}
Expand All @@ -67,11 +71,11 @@ function isBidRequestValid(bid) {
return true;
}

function buildRequest(bidRequests, auctionStart) {
// invibes only responds to 1 bid request for each user visit
function buildRequest(bidRequests, bidderRequest) {
bidderRequest = bidderRequest || {};
const _placementIds = [];
let _loginId, _customEndpoint;
let _ivAuctionStart = auctionStart || Date.now();
let _ivAuctionStart = bidderRequest.auctionStart || Date.now();

bidRequests.forEach(function (bidRequest) {
bidRequest.startTime = new Date().getTime();
Expand All @@ -85,9 +89,9 @@ function buildRequest(bidRequests, auctionStart) {

cookieDomain = detectTopmostCookieDomain();
invibes.noCookies = invibes.noCookies || invibes.getCookie('ivNoCookie');
invibes.optIn = invibes.optIn || invibes.getCookie('ivOptIn');
invibes.optIn = invibes.optIn || invibes.getCookie('ivOptIn') || readGdprConsent(bidderRequest.gdprConsent);

initDomainId();
initDomainId(invibes.domainOptions);

const currentQueryStringParams = parseQueryStringParams();

Expand All @@ -96,7 +100,6 @@ function buildRequest(bidRequests, auctionStart) {
videoAdHtmlId: generateRandomId(),
showFallback: currentQueryStringParams['advs'] === '0',
ivbsCampIdsLocal: invibes.getCookie('IvbsCampIdsLocal'),
lId: invibes.dom.id,

bidParamsJson: JSON.stringify({
placementIds: _placementIds,
Expand All @@ -110,11 +113,12 @@ function buildRequest(bidRequests, auctionStart) {
width: topWin.innerWidth,
height: topWin.innerHeight,

noc: !cookieDomain
noc: !cookieDomain,
oi: invibes.optIn
};

if (invibes.optIn) {
data.oi = 1;
if (invibes.dom.id) {
data.lId = invibes.dom.id;
}

const parametersToPassForward = 'videoaddebug,advs,bvci,bvid,istop,trybvid,trybvci'.split(',');
Expand Down Expand Up @@ -323,10 +327,7 @@ function initLogger() {
function buildSyncUrl() {
let syncUrl = _customUserSync || CONSTANTS.SYNC_ENDPOINT;
syncUrl += '?visitId=' + invibes.visitId;

if (invibes.optIn) {
syncUrl += '&optIn=1';
}
syncUrl += '&optIn=' + invibes.optIn;

const did = invibes.getCookie('ivbsdid');
if (did) {
Expand Down Expand Up @@ -358,6 +359,14 @@ function acceptPostMessage(e) {
}
}

function readGdprConsent(gdprConsent) {
if (gdprConsent && gdprConsent.vendorData && gdprConsent.vendorData.vendorConsents) {
return !!gdprConsent.vendorData.vendorConsents[CONSTANTS.INVIBES_VENDOR_ID.toString(10)] === true ? 2 : -2;
}

return 0;
}

const ivLogger = initLogger();

/// Local domain cookie management =====================
Expand Down Expand Up @@ -400,9 +409,9 @@ invibes.setCookie = function (name, value, exdays, domain) {

let detectTopmostCookieDomain = function () {
let testCookie = invibes.Uid.generate();
let hostParts = location.host.split('.');
let hostParts = location.hostname.split('.');
if (hostParts.length === 1) {
return location.host;
return location.hostname;
}
for (let i = hostParts.length - 1; i >= 0; i--) {
let domain = '.' + hostParts.slice(i).join('.');
Expand Down Expand Up @@ -463,8 +472,8 @@ let initDomainId = function (options) {

let setId = function () {
invibes.dom = {
id: !state.cr && invibes.optIn ? state.id : undefined,
tempId: invibes.optIn ? state.id : undefined,
id: (!state.cr && invibes.optIn > 0) ? state.id : undefined,
tempId: (invibes.optIn > 0) ? state.id : undefined,
graduate: graduate
};
};
Expand All @@ -481,7 +490,7 @@ let initDomainId = function (options) {
if (state.hc < minHC) {
state.hc++;
}
if (state.hc >= minHC && validGradTime(state)) {
if ((state.hc >= minHC && validGradTime(state)) || options.skipGraduation) {
graduate();
}
}
Expand All @@ -496,4 +505,11 @@ export function resetInvibes() {
invibes.noCookies = undefined;
invibes.dom = undefined;
invibes.bidResponse = undefined;
invibes.domainOptions = undefined;
}

export function stubDomainOptions(persistence) {
invibes.domainOptions = {
persistence: persistence
};
}
77 changes: 59 additions & 18 deletions test/spec/modules/invibesBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from 'chai';
import { spec, resetInvibes } from 'modules/invibesBidAdapter';
import { spec, resetInvibes, stubDomainOptions } from 'modules/invibesBidAdapter';

describe('invibesBidAdapter:', function () {
const BIDDER_CODE = 'invibes';
Expand Down Expand Up @@ -40,6 +40,21 @@ describe('invibesBidAdapter:', function () {
}
];

let StubbedPersistence = function(initialValue) {
var value = initialValue;
return {
load: function () {
let str = value || '';
try {
return JSON.parse(str);
} catch (e) { }
},
save: function (obj) {
value = JSON.stringify(obj);
}
}
};

beforeEach(function () {
resetInvibes();
document.cookie = '';
Expand Down Expand Up @@ -83,6 +98,18 @@ describe('invibesBidAdapter:', function () {

expect(spec.isBidRequestValid(invalidBid)).to.be.false;
});

it('returns false when bid response was previously received', function() {
const validBid = {
bidder: BIDDER_CODE,
params: {
placementId: PLACEMENT_ID
}
}

top.window.invibes.bidResponse = { prop: 'prop' };
expect(spec.isBidRequestValid(validBid)).to.be.false;
});
});
});

Expand Down Expand Up @@ -126,38 +153,52 @@ describe('invibesBidAdapter:', function () {
expect(request.data.lId).to.not.exist;
});

it('try to graduate but not enough count - doesnt send the domain id', function () {
global.document.cookie = 'ivbsdid={"id":"dvdjkams6nkq","cr":1521818537626,"hc":5}';
let bidderRequest = { gdprConsent: { vendorData: { vendorConsents: { 436: true } } } };
let request = spec.buildRequests(bidRequests, bidderRequest);
expect(request.data.lId).to.not.exist;
});

it('try to graduate but not old enough - doesnt send the domain id', function () {
let bidderRequest = { gdprConsent: { vendorData: { vendorConsents: { 436: true } } } };
global.document.cookie = 'ivbsdid={"id":"dvdjkams6nkq","cr":' + Date.now() + ',"hc":5}';
let request = spec.buildRequests(bidRequests, bidderRequest);
expect(request.data.lId).to.not.exist;
});

it('graduate and send the domain id', function () {
top.window.invibes.optIn = 1;
global.document.cookie = 'ivbsdid={"id":"dvdjkams6nkq","cr":1521818537626,"hc":7}';
let request = spec.buildRequests(bidRequests);
let bidderRequest = { gdprConsent: { vendorData: { vendorConsents: { 436: true } } } };
stubDomainOptions(new StubbedPersistence('{"id":"dvdjkams6nkq","cr":1521818537626,"hc":7}'));
let request = spec.buildRequests(bidRequests, bidderRequest);
expect(request.data.lId).to.exist;
});

it('send the domain id if already graduated', function () {
top.window.invibes.optIn = 1;
global.document.cookie = 'ivbsdid={"id":"f8zoh044p9oi"}';
let request = spec.buildRequests(bidRequests);
let bidderRequest = { gdprConsent: { vendorData: { vendorConsents: { 436: true } } } };
stubDomainOptions(new StubbedPersistence('{"id":"f8zoh044p9oi"}'));
let request = spec.buildRequests(bidRequests, bidderRequest);
expect(request.data.lId).to.exist;
});

it('send the domain id after replacing it with new format', function () {
top.window.invibes.optIn = 1;
global.document.cookie = 'ivbsdid={"id":"f8zoh044p9oi.8537626"}';
let request = spec.buildRequests(bidRequests);
let bidderRequest = { gdprConsent: { vendorData: { vendorConsents: { 436: true } } } };
stubDomainOptions(new StubbedPersistence('{"id":"f8zoh044p9oi.8537626"}'));
let request = spec.buildRequests(bidRequests, bidderRequest);
expect(request.data.lId).to.exist;
});

it('try to graduate but not enough count - doesnt send the domain id', function () {
top.window.invibes.optIn = 1;
global.document.cookie = 'ivbsdid={"id":"dvdjkams6nkq","cr":1521818537626,"hc":5}';
let request = spec.buildRequests(bidRequests);
it('dont send the domain id if consent declined', function () {
let bidderRequest = { gdprConsent: { vendorData: { vendorConsents: { 436: false } } } };
stubDomainOptions(new StubbedPersistence('{"id":"f8zoh044p9oi.8537626"}'));
let request = spec.buildRequests(bidRequests, bidderRequest);
expect(request.data.lId).to.not.exist;
});

it('try to graduate but not old enough - doesnt send the domain id', function () {
top.window.invibes.optIn = 1;
global.document.cookie = 'ivbsdid={"id":"dvdjkams6nkq","cr":' + Date.now() + ',"hc":5}';
let request = spec.buildRequests(bidRequests);
it('dont send the domain id if no consent', function () {
let bidderRequest = { };
stubDomainOptions(new StubbedPersistence('{"id":"f8zoh044p9oi.8537626"}'));
let request = spec.buildRequests(bidRequests, bidderRequest);
expect(request.data.lId).to.not.exist;
});
});
Expand Down