Skip to content

Commit

Permalink
LiveConnect, lighter-weight module (prebid#6016)
Browse files Browse the repository at this point in the history
* NT. Trying out how webpack plays with env vars

* NT. Test run before merge

* NT. POC kinda ok for me now

* NT. Updated the lc version to released one.

* NT. Reverting test data. Ready for PR

* NT. Fixing linting.

* NT. Fixing the email hash scenario.
  • Loading branch information
jankoulaga authored and GeneGenie committed Jan 13, 2021
1 parent e8ce9e2 commit d9b45cc
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 41 deletions.
41 changes: 21 additions & 20 deletions modules/liveIntentIdSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import * as utils from '../src/utils.js';
import { triggerPixel } from '../src/utils.js';
import { ajaxBuilder } from '../src/ajax.js';
import { submodule } from '../src/hook.js';
import { LiveConnect } from 'live-connect-js/cjs/live-connect.js';
import { LiveConnect } from 'live-connect-js/esm/initializer.js';
import { gdprDataHandler, uspDataHandler } from '../src/adapterManager.js';
import { getStorageManager } from '../src/storageManager.js';
import { MinimalLiveConnect } from 'live-connect-js/esm/minimal-live-connect.js';

const MODULE_NAME = 'liveIntentId';
export const storage = getStorageManager(null, MODULE_NAME);
Expand Down Expand Up @@ -42,26 +43,18 @@ export function reset() {
if (window && window.liQ) {
window.liQ = [];
}
liveIntentIdSubmodule.setModuleMode(null)
eventFired = false;
liveConnect = null;
}

function parseLiveIntentCollectorConfig(collectConfig) {
const config = {};
if (collectConfig) {
if (collectConfig.appId) {
config.appId = collectConfig.appId;
}
if (collectConfig.fpiStorageStrategy) {
config.storageStrategy = collectConfig.fpiStorageStrategy;
}
if (collectConfig.fpiExpirationDays) {
config.expirationDays = collectConfig.fpiExpirationDays;
}
if (collectConfig.collectorUrl) {
config.collectorUrl = collectConfig.collectorUrl;
}
}
collectConfig = collectConfig || {}
collectConfig.appId && (config.appId = collectConfig.appId);
collectConfig.fpiStorageStrategy && (config.storageStrategy = collectConfig.fpiStorageStrategy);
collectConfig.fpiExpirationDays && (config.expirationDays = collectConfig.fpiExpirationDays);
collectConfig.collectorUrl && (config.collectorUrl = collectConfig.collectorUrl);
return config;
}

Expand Down Expand Up @@ -90,9 +83,6 @@ function initializeLiveConnect(configParams) {
liveConnectConfig.wrapperName = 'prebid';
liveConnectConfig.identityResolutionConfig = identityResolutionConfig;
liveConnectConfig.identifiersToResolve = configParams.identifiersToResolve || [];
if (configParams.emailHash) {
liveConnectConfig.eventSource = { hash: configParams.emailHash }
}
const usPrivacyString = uspDataHandler.getConsentData();
if (usPrivacyString) {
liveConnectConfig.usPrivacyString = usPrivacyString;
Expand All @@ -105,7 +95,10 @@ function initializeLiveConnect(configParams) {

// The second param is the storage object, LS & Cookie manipulation uses PBJS utils.
// The third param is the ajax and pixel object, the ajax and pixel use PBJS utils.
liveConnect = LiveConnect(liveConnectConfig, storage, calls);
liveConnect = liveIntentIdSubmodule.getInitializer()(liveConnectConfig, storage, calls);
if (configParams.emailHash) {
liveConnect.push({ hash: configParams.emailHash })
}
return liveConnect;
}

Expand All @@ -118,12 +111,20 @@ function tryFireEvent() {

/** @type {Submodule} */
export const liveIntentIdSubmodule = {
moduleMode: process.env.LiveConnectMode,
/**
* used to link submodule with config
* @type {string}
*/
name: MODULE_NAME,

setModuleMode(mode) {
this.moduleMode = mode
},
getInitializer() {
return this.moduleMode === 'minimal' ? MinimalLiveConnect : LiveConnect
},

/**
* decode the stored id value for passing to bid requests. Note that lipb object is a wrapper for everything, and
* internally it could contain more data other than `lipbid`(e.g. `segments`) depending on the `partner` and
Expand All @@ -136,7 +137,7 @@ export const liveIntentIdSubmodule = {
decode(value, config) {
const configParams = (config && config.params) || {};
function composeIdObject(value) {
const base = { 'lipbid': value['unifiedId'] };
const base = { 'lipbid': value.unifiedId };
delete value.unifiedId;
return { 'lipb': { ...base, ...value } };
}
Expand Down
24 changes: 6 additions & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,6 @@
"fun-hooks": "^0.9.9",
"jsencrypt": "^3.0.0-rc.1",
"just-clone": "^1.0.2",
"live-connect-js": "^1.1.23"
"live-connect-js": "2.0.0"
}
}
188 changes: 188 additions & 0 deletions test/spec/modules/liveIntentIdMinimalSystem_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import * as utils from 'src/utils.js';
import { gdprDataHandler, uspDataHandler } from '../../../src/adapterManager.js';
import { server } from 'test/mocks/xhr.js';
import { liveIntentIdSubmodule, reset as resetLiveIntentIdSubmodule, storage } from 'modules/liveIntentIdSystem.js';

const PUBLISHER_ID = '89899';
const defaultConfigParams = { params: {publisherId: PUBLISHER_ID} };
const responseHeader = {'Content-Type': 'application/json'};

describe('LiveIntentMinimalId', function() {
let logErrorStub;
let uspConsentDataStub;
let gdprConsentDataStub;
let getCookieStub;
let getDataFromLocalStorageStub;
let imgStub;

beforeEach(function() {
liveIntentIdSubmodule.setModuleMode('minimal');
imgStub = sinon.stub(utils, 'triggerPixel');
getCookieStub = sinon.stub(storage, 'getCookie');
getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage');
logErrorStub = sinon.stub(utils, 'logError');
uspConsentDataStub = sinon.stub(uspDataHandler, 'getConsentData');
gdprConsentDataStub = sinon.stub(gdprDataHandler, 'getConsentData');
});

afterEach(function() {
imgStub.restore();
getCookieStub.restore();
getDataFromLocalStorageStub.restore();
logErrorStub.restore();
uspConsentDataStub.restore();
gdprConsentDataStub.restore();
liveIntentIdSubmodule.setModuleMode('minimal');
resetLiveIntentIdSubmodule();
});
it('should not fire an event when getId', function() {
uspConsentDataStub.returns('1YNY');
gdprConsentDataStub.returns({
gdprApplies: true,
consentString: 'consentDataString'
})
liveIntentIdSubmodule.getId(defaultConfigParams);
expect(server.requests[0]).to.eql(undefined)
});

it('should not return a decoded identifier when the unifiedId is not present in the value', function() {
const result = liveIntentIdSubmodule.decode({ additionalData: 'data' });
expect(result).to.be.undefined;
});

it('should initialize LiveConnect and send no data', function() {
liveIntentIdSubmodule.getId(defaultConfigParams);
liveIntentIdSubmodule.decode({}, defaultConfigParams);
liveIntentIdSubmodule.getId(defaultConfigParams);
liveIntentIdSubmodule.decode({}, defaultConfigParams);
expect(server.requests.length).to.be.eq(0);
});

it('should call the Custom URL of the LiveIntent Identity Exchange endpoint', function() {
getCookieStub.returns(null);
let callBackSpy = sinon.spy();
let submoduleCallback = liveIntentIdSubmodule.getId({ params: {...defaultConfigParams.params, ...{'url': 'https://dummy.liveintent.com/idex'}} }).callback;
submoduleCallback(callBackSpy);
let request = server.requests[0];
expect(request.url).to.be.eq('https://dummy.liveintent.com/idex/prebid/89899');
request.respond(
200,
responseHeader,
JSON.stringify({})
);
expect(callBackSpy.calledOnce).to.be.true;
});

it('should call the default url of the LiveIntent Identity Exchange endpoint, with a partner', function() {
getCookieStub.returns(null);
let callBackSpy = sinon.spy();
let submoduleCallback = liveIntentIdSubmodule.getId({ params: {
...defaultConfigParams.params,
...{
'url': 'https://dummy.liveintent.com/idex',
'partner': 'rubicon'
}
} }).callback;
submoduleCallback(callBackSpy);
let request = server.requests[0];
expect(request.url).to.be.eq('https://dummy.liveintent.com/idex/rubicon/89899');
request.respond(
200,
responseHeader,
JSON.stringify({})
);
expect(callBackSpy.calledOnce).to.be.true;
});

it('should call the LiveIntent Identity Exchange endpoint, with no additional query params', function() {
getCookieStub.returns(null);
let callBackSpy = sinon.spy();
let submoduleCallback = liveIntentIdSubmodule.getId(defaultConfigParams).callback;
submoduleCallback(callBackSpy);
let request = server.requests[0];
expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/89899');
request.respond(
200,
responseHeader,
JSON.stringify({})
);
expect(callBackSpy.calledOnce).to.be.true;
});

it('should log an error and continue to callback if ajax request errors', function() {
getCookieStub.returns(null);
let callBackSpy = sinon.spy();
let submoduleCallback = liveIntentIdSubmodule.getId(defaultConfigParams).callback;
submoduleCallback(callBackSpy);
let request = server.requests[0];
expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/89899');
request.respond(
503,
responseHeader,
'Unavailable'
);
expect(logErrorStub.calledOnce).to.be.true;
expect(callBackSpy.calledOnce).to.be.true;
});

it('should include the LiveConnect identifier when calling the LiveIntent Identity Exchange endpoint', function() {
const oldCookie = 'a-xxxx--123e4567-e89b-12d3-a456-426655440000'
getDataFromLocalStorageStub.withArgs('_li_duid').returns(oldCookie);
let callBackSpy = sinon.spy();
let submoduleCallback = liveIntentIdSubmodule.getId(defaultConfigParams).callback;
submoduleCallback(callBackSpy);
let request = server.requests[0];
expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?duid=${oldCookie}`);
request.respond(
200,
responseHeader,
JSON.stringify({})
);
expect(callBackSpy.calledOnce).to.be.true;
});

it('should include the LiveConnect identifier and additional Identifiers to resolve', function() {
const oldCookie = 'a-xxxx--123e4567-e89b-12d3-a456-426655440000'
getDataFromLocalStorageStub.withArgs('_li_duid').returns(oldCookie);
getDataFromLocalStorageStub.withArgs('_thirdPC').returns('third-pc');
const configParams = { params: {
...defaultConfigParams.params,
...{
'identifiersToResolve': ['_thirdPC']
}
}};
let callBackSpy = sinon.spy();
let submoduleCallback = liveIntentIdSubmodule.getId(configParams).callback;
submoduleCallback(callBackSpy);
let request = server.requests[0];
expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?duid=${oldCookie}&_thirdPC=third-pc`);
request.respond(
200,
responseHeader,
JSON.stringify({})
);
expect(callBackSpy.calledOnce).to.be.true;
});

it('should include an additional identifier value to resolve even if it is an object', function() {
getCookieStub.returns(null);
getDataFromLocalStorageStub.withArgs('_thirdPC').returns({'key': 'value'});
const configParams = { params: {
...defaultConfigParams.params,
...{
'identifiersToResolve': ['_thirdPC']
}
}};
let callBackSpy = sinon.spy();
let submoduleCallback = liveIntentIdSubmodule.getId(configParams).callback;
submoduleCallback(callBackSpy);
let request = server.requests[0];
expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/89899?_thirdPC=%7B%22key%22%3A%22value%22%7D');
request.respond(
200,
responseHeader,
JSON.stringify({})
);
expect(callBackSpy.calledOnce).to.be.true;
});
});
4 changes: 3 additions & 1 deletion test/spec/modules/liveIntentIdSystem_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { liveIntentIdSubmodule, reset as resetLiveIntentIdSubmodule, storage } f
import * as utils from 'src/utils.js';
import { gdprDataHandler, uspDataHandler } from '../../../src/adapterManager.js';
import { server } from 'test/mocks/xhr.js';

resetLiveIntentIdSubmodule();
liveIntentIdSubmodule.setModuleMode('standard')
const PUBLISHER_ID = '89899';
const defaultConfigParams = { params: {publisherId: PUBLISHER_ID} };
const responseHeader = {'Content-Type': 'application/json'}
Expand All @@ -16,6 +17,7 @@ describe('LiveIntentId', function() {
let imgStub;

beforeEach(function() {
liveIntentIdSubmodule.setModuleMode('standard');
imgStub = sinon.stub(utils, 'triggerPixel');
getCookieStub = sinon.stub(storage, 'getCookie');
getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage');
Expand Down
3 changes: 2 additions & 1 deletion webpack.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ var neverBundle = [
];

var plugins = [
new RequireEnsureWithoutJsonp()
new RequireEnsureWithoutJsonp(),
new webpack.EnvironmentPlugin(['LiveConnectMode'])
];

if (argv.analyze) {
Expand Down

0 comments on commit d9b45cc

Please sign in to comment.