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

Invibes Bid Adapter: write id to first party from bid adapter #8202

Merged
merged 5 commits into from
May 13, 2022
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
89 changes: 55 additions & 34 deletions modules/invibesBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const CONSTANTS = {
SYNC_ENDPOINT: 'https://k.r66net.com/GetUserSync',
TIME_TO_LIVE: 300,
DEFAULT_CURRENCY: 'EUR',
PREBID_VERSION: 7,
PREBID_VERSION: 8,
METHOD: 'GET',
INVIBES_VENDOR_ID: 436,
USERID_PROVIDERS: ['pubcid', 'pubProvidedId', 'uid2', 'zeotapIdPlus', 'id5id'],
Expand Down Expand Up @@ -95,8 +95,6 @@ function buildRequest(bidRequests, bidderRequest) {
invibes.optIn = invibes.optIn || readGdprConsent(bidderRequest.gdprConsent);

invibes.visitId = invibes.visitId || generateRandomId();
invibes.noCookies = invibes.noCookies || invibes.getCookie('ivNoCookie');
let lid = initDomainId(invibes.domainOptions);

const currentQueryStringParams = parseQueryStringParams();
let userIdModel = getUserIds(_userId);
Expand All @@ -113,7 +111,7 @@ function buildRequest(bidRequests, bidderRequest) {
location: getDocumentLocation(topWin),
videoAdHtmlId: generateRandomId(),
showFallback: currentQueryStringParams['advs'] === '0',
ivbsCampIdsLocal: invibes.getCookie('IvbsCampIdsLocal'),
ivbsCampIdsLocal: readFromLocalStorage('IvbsCampIdsLocal'),

bidParamsJson: JSON.stringify(bidParamsJson),
capCounts: getCappedCampaignsAsString(),
Expand All @@ -129,9 +127,21 @@ function buildRequest(bidRequests, bidderRequest) {
purposes: invibes.purposes.toString(),
li: invibes.legitimateInterests.toString(),

tc: invibes.gdpr_consent
tc: invibes.gdpr_consent,
isLocalStorageEnabled: storage.hasLocalStorage(),
};

let lid = readFromLocalStorage('ivbsdid');
if (!lid) {
let str = invibes.getCookie('ivbsdid');
if (str) {
try {
let cookieLid = JSON.parse(str);
lid = cookieLid.id ? cookieLid.id : cookieLid;
} catch (e) {
}
}
}
if (lid) {
data.lId = lid;
}
Expand Down Expand Up @@ -172,6 +182,15 @@ function handleResponse(responseObj, bidRequests) {
responseObj = responseObj.body || responseObj;
responseObj = responseObj.videoAdContentResult || responseObj;

if (responseObj.ShouldSetLId && responseObj.LId) {
if ((!invibes.optIn || !invibes.purposes[0]) && responseObj.PrivacyPolicyRule && responseObj.TcModel && responseObj.TcModel.PurposeConsents) {
invibes.optIn = responseObj.PrivacyPolicyRule;
invibes.purposes = responseObj.TcModel.PurposeConsents;
}

setInLocalStorage('ivbsdid', responseObj.LId);
}

if (typeof invibes.bidResponse === 'object') {
if (responseObj.MultipositionEnabled === true) {
invibes.bidResponse.AdPlacements = invibes.bidResponse.AdPlacements.concat(responseObj.AdPlacements);
Expand Down Expand Up @@ -411,6 +430,22 @@ function renderCreative(bidModel) {
.replace('creativeHtml', bidModel.CreativeHtml);
}

function readFromLocalStorage(key) {
if (invibes.GdprModuleInstalled && (!invibes.optIn || !invibes.purposes[0])) {
return;
}

return storage.getDataFromLocalStorage(key) || '';
}

function setInLocalStorage(key, value) {
if (!invibes.optIn || !invibes.purposes[0]) {
return;
}

storage.setDataInLocalStorage(key, value);
}

function getCappedCampaignsAsString() {
const key = 'ivvcap';

Expand Down Expand Up @@ -471,21 +506,28 @@ function buildSyncUrl() {
syncUrl += '?visitId=' + invibes.visitId;
syncUrl += '&optIn=' + invibes.optIn;

const did = invibes.getCookie('ivbsdid');
if (did) {
syncUrl += '&ivbsdid=' + encodeURIComponent(did);
let did = readFromLocalStorage('ivbsdid');
if (!did) {
let str = invibes.getCookie('ivbsdid');
if (str) {
try {
let cookieLid = JSON.parse(str);
did = cookieLid.id ? cookieLid.id : cookieLid;
} catch (e) {
}
}
}

const bks = invibes.getCookie('ivvbks');
if (bks) {
syncUrl += '&ivvbks=' + encodeURIComponent(bks);
if (did) {
syncUrl += '&ivbsdid=' + encodeURIComponent(did);
}

return syncUrl;
}

function readGdprConsent(gdprConsent) {
if (gdprConsent && gdprConsent.vendorData) {
invibes.GdprModuleInstalled = true;
invibes.gdpr_consent = getVendorConsentData(gdprConsent.vendorData);

if (!gdprConsent.vendorData.gdprApplies || gdprConsent.vendorData.hasGlobalConsent) {
Expand Down Expand Up @@ -528,6 +570,7 @@ function readGdprConsent(gdprConsent) {
return 2;
}

invibes.GdprModuleInstalled = false;
return 0;
}

Expand Down Expand Up @@ -637,34 +680,13 @@ invibes.getCookie = function (name) {
return;
}

if (!invibes.optIn || !invibes.purposes[0]) {
if (invibes.GdprModuleInstalled && (!invibes.optIn || !invibes.purposes[0])) {
return;
}

return storage.getCookie(name);
};

let initDomainId = function (options) {
let cookiePersistence = {
cname: 'ivbsdid',
load: function () {
let str = invibes.getCookie(this.cname) || '';
try {
return JSON.parse(str);
} catch (e) {
}
}
};

options = options || {};

var persistence = options.persistence || cookiePersistence;

let state = persistence.load();

return state ? (state.id || state.tempId) : undefined;
};

let keywords = (function () {
const cap = 300;
let headTag = document.getElementsByTagName('head')[0];
Expand Down Expand Up @@ -738,7 +760,6 @@ let keywords = (function () {

export function resetInvibes() {
invibes.optIn = undefined;
invibes.noCookies = undefined;
invibes.dom = undefined;
invibes.bidResponse = undefined;
invibes.domainOptions = undefined;
Expand Down
105 changes: 98 additions & 7 deletions test/spec/modules/invibesBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {expect} from 'chai';
import { config } from 'src/config.js';
import {spec, resetInvibes, stubDomainOptions, readGdprConsent} from 'modules/invibesBidAdapter.js';

describe('invibesBidAdapter:', function () {
Expand Down Expand Up @@ -93,13 +94,25 @@ describe('invibesBidAdapter:', function () {
}
};

let SetBidderAccess = function() {
config.setConfig({
deviceAccess: true
});
$$PREBID_GLOBAL$$.bidderSettings = {
invibes: {
storageAllowed: true
}
};
}

beforeEach(function () {
resetInvibes();
document.cookie = '';
this.cStub1 = sinon.stub(console, 'info');
});

afterEach(function () {
$$PREBID_GLOBAL$$.bidderSettings = {};
this.cStub1.restore();
});

Expand Down Expand Up @@ -296,13 +309,40 @@ describe('invibesBidAdapter:', function () {
top.window.invibes.optIn = 1;
top.window.invibes.purposes = [true, false, false, false, false, false, false, false, false, false];
localStorage.ivvcap = '{"9731":[1,1768600800000]}';
SetBidderAccess();

const request = spec.buildRequests(bidRequests, {auctionStart: Date.now()});

expect(request.data.capCounts).to.equal('9731=1');
});

it('does not have capped ids if local storage variable is correctly formatted but no opt in', function () {
let bidderRequest = {
auctionStart: Date.now(),
gdprConsent: {
vendorData: {
gdprApplies: true,
hasGlobalConsent: false,
purpose: {
consents: {
1: false,
2: false,
3: false,
4: false,
5: false,
6: false,
7: false,
8: false,
9: false,
10: false
}
}
}
}
};

localStorage.ivvcap = '{"9731":[1,1768600800000]}';
const request = spec.buildRequests(bidRequests, {auctionStart: Date.now()});
const request = spec.buildRequests(bidRequests, bidderRequest);
expect(request.data.capCounts).to.equal('');
});

Expand Down Expand Up @@ -369,6 +409,8 @@ describe('invibesBidAdapter:', function () {
top.window.invibes.purposes = [true, false, false, false, false, false, false, false, false, false];
global.document.cookie = 'ivbsdid={"id":"dvdjkams6nkq","cr":' + Date.now() + ',"hc":0}';
let bidderRequest = {gdprConsent: {vendorData: {vendorConsents: {436: true}}}};
SetBidderAccess();

let request = spec.buildRequests(bidRequests, bidderRequest);
expect(request.data.lId).to.exist;
});
Expand Down Expand Up @@ -496,7 +538,7 @@ describe('invibesBidAdapter:', function () {
let request = spec.buildRequests(bidRequests, bidderRequest);
expect(request.data.li.split(',')[1] && request.data.li.split(',')[6]).to.equal('true');
});
it('should send oi = 0 when vendorData is null', function () {
it('should send oi = 1 when vendorData is null (calculation will be performed by ADWEB)', function () {
let bidderRequest = {
gdprConsent: {
vendorData: null
Expand Down Expand Up @@ -962,7 +1004,29 @@ describe('invibesBidAdapter:', function () {
UseAdUnitCode: true
};

var buildResponse = function(placementId, cid, blcids, creativeId) {
var buildResponse = function(placementId, cid, blcids, creativeId, ShouldSetLId) {
if (ShouldSetLId) {
return {
MultipositionEnabled: true,
AdPlacements: [{
Ads: [{
BidPrice: 0.5,
VideoExposedId: creativeId,
Cid: cid,
Blcids: blcids
}],
BidModel: {
BidVersion: 1,
PlacementId: placementId,
AuctionStartTime: Date.now(),
CreativeHtml: '<!-- Creative -->'
}
}],
ShouldSetLId: true,
LId: 'dvdjkams6nkq'
}
}

return {
MultipositionEnabled: true,
AdPlacements: [{
Expand Down Expand Up @@ -1066,6 +1130,22 @@ describe('invibesBidAdapter:', function () {
});
});

context('AdWeb generates LIDs', function() {
it('works when no LID is not sent from AdWeb', function() {
var firstResponse = buildResponse('12345', 1, [], 123);

var firstResult = spec.interpretResponse({body: firstResponse}, {bidRequests});
expect(firstResult[0].creativeId).to.equal(123);
});

it('sets lid when AdWeb sends it', function() {
var firstResponse = buildResponse('12345', 1, [], 123, true);

spec.interpretResponse({body: firstResponse}, {bidRequests});
expect(global.document.cookie.indexOf('ivbsdid')).to.greaterThanOrEqual(0);
});
});

context('in multiposition context, with conflicting ads', function() {
it('registers the second ad when no conflict', function() {
var firstResponse = buildResponse('12345', 1, [1], 123);
Expand Down Expand Up @@ -1123,22 +1203,33 @@ describe('invibesBidAdapter:', function () {

it('returns an iframe with params if enabled', function () {
top.window.invibes.optIn = 1;
global.document.cookie = 'ivvbks=17639.0,1,2';
let response = spec.getUserSyncs({iframeEnabled: true});
expect(response.type).to.equal('iframe');
expect(response.url).to.include(SYNC_ENDPOINT);
expect(response.url).to.include('optIn');
expect(response.url).to.include('ivvbks');
});

it('returns an iframe with params including if enabled', function () {
top.window.invibes.optIn = 1;
global.document.cookie = 'ivvbks=17639.0,1,2;ivbsdid={"id":"dvdjkams6nkq","cr":' + Date.now() + ',"hc":0}';
global.document.cookie = 'ivbsdid={"id":"dvdjkams6nkq","cr":' + Date.now() + ',"hc":0}';
SetBidderAccess();

let response = spec.getUserSyncs({iframeEnabled: true});
expect(response.type).to.equal('iframe');
expect(response.url).to.include(SYNC_ENDPOINT);
expect(response.url).to.include('optIn');
expect(response.url).to.include('ivbsdid');
});

it('returns an iframe with params including if enabled read from LocalStorage', function () {
top.window.invibes.optIn = 1;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you confirm you have access to storage in your test ? #8279

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I just made a sync with the MASTER from prebid and re-ran all my tests and the passed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right but can you make sure your tests will also pass when #8279 is merged?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I've made the necessary updates and tested with the default config changed as in #8279

localStorage.ivbsdid = 'dvdjkams6nkq';
SetBidderAccess();

let response = spec.getUserSyncs({iframeEnabled: true});
expect(response.type).to.equal('iframe');
expect(response.url).to.include(SYNC_ENDPOINT);
expect(response.url).to.include('optIn');
expect(response.url).to.include('ivvbks');
expect(response.url).to.include('ivbsdid');
});

Expand Down