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

LiveConnect, lighter-weight module #6016

Merged
merged 9 commits into from
Jan 5, 2021
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';
Copy link
Contributor

@msm0504 msm0504 Dec 10, 2020

Choose a reason for hiding this comment

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

@jankoulaga Will this result in both libraries (LiveConnect and MinimalLiveConnect) being added to the bundle?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hey @msm0504 well, the bundle is generated by webpack, so no.

If this were pieced together without any treeshaking, then yes, both sources would have been added, however, the trick here is that at some point, there's an evaluation of a boolean which is either true or false, and that's how webpack will know which import will be used. Anything unused will be automatically removed from the bundle.


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