Skip to content

Commit

Permalink
LiveIntent Identity Module: Introduce First Party ID (prebid#11437)
Browse files Browse the repository at this point in the history
* fpid

* lint

* Adjust tests

* Fix test expectation

* live-connect v6.7.3
  • Loading branch information
3link authored and zkosanovic committed May 28, 2024
1 parent bca27f4 commit 724785d
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 130 deletions.
103 changes: 66 additions & 37 deletions modules/liveIntentIdSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
import { triggerPixel, logError } from '../src/utils.js';
import { ajaxBuilder } from '../src/ajax.js';
import { submodule } from '../src/hook.js';
import { LiveConnect } from 'live-connect-js'; // eslint-disable-line prebid/validate-imports
import { gdprDataHandler, uspDataHandler, gppDataHandler } from '../src/adapterManager.js';
import {getStorageManager} from '../src/storageManager.js';
import {MODULE_TYPE_UID} from '../src/activities/modules.js';
import { LiveConnect } from 'live-connect-js/prebid'; // eslint-disable-line prebid/validate-imports
import { gdprDataHandler, uspDataHandler, gppDataHandler, coppaDataHandler } from '../src/adapterManager.js';
import { getStorageManager } from '../src/storageManager.js';
import { MODULE_TYPE_UID } from '../src/activities/modules.js';
import { UID2_EIDS } from '../libraries/uid2Eids/uid2Eids.js';
import {UID1_EIDS} from '../libraries/uid1Eids/uid1Eids.js';
import {UID2_EIDS} from '../libraries/uid2Eids/uid2Eids.js';
import { getRefererInfo } from '../src/refererDetection.js';

/**
Expand All @@ -21,12 +21,12 @@ import { getRefererInfo } from '../src/refererDetection.js';
* @typedef {import('../modules/userId/index.js').IdResponse} IdResponse
*/

const DEFAULT_AJAX_TIMEOUT = 5000
const EVENTS_TOPIC = 'pre_lips'
const DEFAULT_AJAX_TIMEOUT = 5000;
const EVENTS_TOPIC = 'pre_lips';
const MODULE_NAME = 'liveIntentId';
const LI_PROVIDER_DOMAIN = 'liveintent.com';
export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME});
const defaultRequestedAttributes = {'nonId': true}
const defaultRequestedAttributes = {'nonId': true};
const calls = {
ajaxGet: (url, onSuccess, onError, timeout) => {
ajaxBuilder(timeout)(
Expand All @@ -53,10 +53,10 @@ let liveConnect = null;
*/
export function reset() {
if (window && window.liQ_instances) {
window.liQ_instances.forEach(i => i.eventBus.off(EVENTS_TOPIC, setEventFiredFlag))
window.liQ_instances.forEach(i => i.eventBus.off(EVENTS_TOPIC, setEventFiredFlag));
window.liQ_instances = [];
}
liveIntentIdSubmodule.setModuleMode(null)
liveIntentIdSubmodule.setModuleMode(null);
eventFired = false;
liveConnect = null;
}
Expand All @@ -70,7 +70,7 @@ export function setEventFiredFlag() {

function parseLiveIntentCollectorConfig(collectConfig) {
const config = {};
collectConfig = collectConfig || {}
collectConfig = collectConfig || {};
collectConfig.appId && (config.appId = collectConfig.appId);
collectConfig.fpiStorageStrategy && (config.storageStrategy = collectConfig.fpiStorageStrategy);
collectConfig.fpiExpirationDays && (config.expirationDays = collectConfig.fpiExpirationDays);
Expand All @@ -86,30 +86,39 @@ function parseLiveIntentCollectorConfig(collectConfig) {
* @returns {Array}
*/
function parseRequestedAttributes(overrides) {
function renameAttribute(attribute) {
if (attribute === 'fpid') {
return 'idCookie';
} else {
return attribute;
};
}
function createParameterArray(config) {
return Object.entries(config).flatMap(([k, v]) => (typeof v === 'boolean' && v) ? [k] : []);
return Object.entries(config).flatMap(([k, v]) => (typeof v === 'boolean' && v) ? [renameAttribute(k)] : []);
}
if (typeof overrides === 'object') {
return createParameterArray({...defaultRequestedAttributes, ...overrides})
return createParameterArray({...defaultRequestedAttributes, ...overrides});
} else {
return createParameterArray(defaultRequestedAttributes);
}
}

function initializeLiveConnect(configParams) {
configParams = configParams || {};
if (liveConnect) {
return liveConnect;
}

configParams = configParams || {};
const fpidConfig = configParams.fpid || {};

const publisherId = configParams.publisherId || 'any';
const identityResolutionConfig = {
publisherId: publisherId,
requestedAttributes: parseRequestedAttributes(configParams.requestedAttributesOverrides)
};
if (configParams.url) {
identityResolutionConfig.url = configParams.url
}
identityResolutionConfig.url = configParams.url;
};

identityResolutionConfig.ajaxTimeout = configParams.ajaxTimeout || DEFAULT_AJAX_TIMEOUT;

Expand All @@ -119,19 +128,24 @@ function initializeLiveConnect(configParams) {
liveConnectConfig.distributorId = configParams.distributorId;
identityResolutionConfig.source = configParams.distributorId;
} else {
identityResolutionConfig.source = configParams.partner || 'prebid'
identityResolutionConfig.source = configParams.partner || 'prebid';
}

liveConnectConfig.wrapperName = 'prebid';
liveConnectConfig.trackerVersion = '$prebid.version$';
liveConnectConfig.identityResolutionConfig = identityResolutionConfig;
liveConnectConfig.identifiersToResolve = configParams.identifiersToResolve || [];
liveConnectConfig.fireEventDelay = configParams.fireEventDelay;

liveConnectConfig.idCookie = {};
liveConnectConfig.idCookie.name = fpidConfig.name;
liveConnectConfig.idCookie.strategy = fpidConfig.strategy == 'html5' ? 'localStorage' : fpidConfig.strategy;

const usPrivacyString = uspDataHandler.getConsentData();
if (usPrivacyString) {
liveConnectConfig.usPrivacyString = usPrivacyString;
}
const gdprConsent = gdprDataHandler.getConsentData()
const gdprConsent = gdprDataHandler.getConsentData();
if (gdprConsent) {
liveConnectConfig.gdprApplies = gdprConsent.gdprApplies;
liveConnectConfig.gdprConsent = gdprConsent.consentString;
Expand All @@ -145,21 +159,21 @@ function initializeLiveConnect(configParams) {
// The third param is the ajax and pixel object, the ajax and pixel use PBJS
liveConnect = liveIntentIdSubmodule.getInitializer()(liveConnectConfig, storage, calls);
if (configParams.emailHash) {
liveConnect.push({ hash: configParams.emailHash })
liveConnect.push({ hash: configParams.emailHash });
}
return liveConnect;
}

function tryFireEvent() {
if (!eventFired && liveConnect) {
const eventDelay = liveConnect.config.fireEventDelay || 500
const eventDelay = liveConnect.config.fireEventDelay || 500;
setTimeout(() => {
const instances = window.liQ_instances
instances.forEach(i => i.eventBus.once(EVENTS_TOPIC, setEventFiredFlag))
const instances = window.liQ_instances;
instances.forEach(i => i.eventBus.once(EVENTS_TOPIC, setEventFiredFlag));
if (!eventFired && liveConnect) {
liveConnect.fire();
}
}, eventDelay)
}, eventDelay);
}
}

Expand All @@ -173,10 +187,10 @@ export const liveIntentIdSubmodule = {
name: MODULE_NAME,

setModuleMode(mode) {
this.moduleMode = mode
this.moduleMode = mode;
},
getInitializer() {
return (liveConnectConfig, storage, calls) => LiveConnect(liveConnectConfig, storage, calls, this.moduleMode)
return (liveConnectConfig, storage, calls) => LiveConnect(liveConnectConfig, storage, calls, this.moduleMode);
},

/**
Expand All @@ -194,46 +208,54 @@ export const liveIntentIdSubmodule = {
const result = {};

// old versions stored lipbid in unifiedId. Ensure that we can still read the data.
const lipbid = value.nonId || value.unifiedId
const lipbid = value.nonId || value.unifiedId;
if (lipbid) {
value.lipbid = lipbid
delete value.unifiedId
result.lipb = value
const lipb = { ...value, lipbid };
delete lipb.unifiedId;
result.lipb = lipb;
}

// Lift usage of uid2 by exposing uid2 if we were asked to resolve it.
// As adapters are applied in lexicographical order, we will always
// be overwritten by the 'proper' uid2 module if it is present.
if (value.uid2) {
result.uid2 = { 'id': value.uid2, ext: { provider: LI_PROVIDER_DOMAIN } }
result.uid2 = { 'id': value.uid2, ext: { provider: LI_PROVIDER_DOMAIN } };
}

if (value.bidswitch) {
result.bidswitch = { 'id': value.bidswitch, ext: { provider: LI_PROVIDER_DOMAIN } }
result.bidswitch = { 'id': value.bidswitch, ext: { provider: LI_PROVIDER_DOMAIN } };
}

if (value.medianet) {
result.medianet = { 'id': value.medianet, ext: { provider: LI_PROVIDER_DOMAIN } }
result.medianet = { 'id': value.medianet, ext: { provider: LI_PROVIDER_DOMAIN } };
}

if (value.magnite) {
result.magnite = { 'id': value.magnite, ext: { provider: LI_PROVIDER_DOMAIN } }
result.magnite = { 'id': value.magnite, ext: { provider: LI_PROVIDER_DOMAIN } };
}

if (value.index) {
result.index = { 'id': value.index, ext: { provider: LI_PROVIDER_DOMAIN } }
result.index = { 'id': value.index, ext: { provider: LI_PROVIDER_DOMAIN } };
}

if (value.openx) {
result.openx = { 'id': value.openx, ext: { provider: LI_PROVIDER_DOMAIN } }
result.openx = { 'id': value.openx, ext: { provider: LI_PROVIDER_DOMAIN } };
}

if (value.pubmatic) {
result.pubmatic = { 'id': value.pubmatic, ext: { provider: LI_PROVIDER_DOMAIN } }
result.pubmatic = { 'id': value.pubmatic, ext: { provider: LI_PROVIDER_DOMAIN } };
}

if (value.sovrn) {
result.sovrn = { 'id': value.sovrn, ext: { provider: LI_PROVIDER_DOMAIN } }
result.sovrn = { 'id': value.sovrn, ext: { provider: LI_PROVIDER_DOMAIN } };
}

if (value.idCookie) {
if (!coppaDataHandler.getCoppa()) {
result.lipb = { ...result.lipb, fpid: value.idCookie };
result.fpid = { 'id': value.idCookie };
}
delete result.lipb.idCookie;
}

if (value.thetradedesk) {
Expand Down Expand Up @@ -380,6 +402,13 @@ export const liveIntentIdSubmodule = {
return data.ext;
}
}
},
'fpid': {
source: 'fpid.liveintent.com',
atype: 1,
getValue: function(data) {
return data.id;
}
}
}
};
Expand Down
17 changes: 14 additions & 3 deletions modules/userId/eids.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ userIdAsEids = [
segments: ['s1', 's2']
}
},
{
source: 'bidswitch.net',
uids: [{
Expand All @@ -118,7 +118,7 @@ userIdAsEids = [
}
}]
},
{
source: 'liveintent.indexexchange.com',
uids: [{
Expand Down Expand Up @@ -161,7 +161,7 @@ userIdAsEids = [
provider: 'liveintent.com'
}
}]
},
},
{
source: 'media.net',
Expand All @@ -185,6 +185,17 @@ userIdAsEids = [
}]
},
{
source: 'fpid.liveintent.com',
uids: [{
id: 'some-random-id-value',
atype: 1,
ext: {
provider: 'liveintent.com'
}
}]
},
{
source: 'merkleinc.com',
uids: [{
Expand Down
30 changes: 15 additions & 15 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 @@ -144,7 +144,7 @@
"fun-hooks": "^0.9.9",
"gulp-wrap": "^0.15.0",
"klona": "^2.0.6",
"live-connect-js": "^6.3.4"
"live-connect-js": "^6.7.3"
},
"optionalDependencies": {
"fsevents": "^2.3.2"
Expand Down
16 changes: 15 additions & 1 deletion test/spec/modules/eids_spec.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {createEidsArray} from 'modules/userId/eids.js';
import {expect} from 'chai';

// Note: In unit tets cases for bidders, call the createEidsArray function over userId object that is used for calling fetchBids
// Note: In unit test cases for bidders, call the createEidsArray function over userId object that is used for calling fetchBids
// this way the request will stay consistent and unit test cases will not need lots of changes.

describe('eids array generation for known sub-modules', function() {
Expand Down Expand Up @@ -184,6 +184,20 @@ describe('eids array generation for known sub-modules', function() {
});
});

it('fpid; getValue call', function() {
const userId = {
fpid: {
id: 'some-random-id-value'
}
};
const newEids = createEidsArray(userId);
expect(newEids.length).to.equal(1);
expect(newEids[0]).to.deep.equal({
source: 'fpid.liveintent.com',
uids: [{id: 'some-random-id-value', atype: 1}]
});
});

it('bidswitch', function() {
const userId = {
bidswitch: {'id': 'sample_id'}
Expand Down
Loading

0 comments on commit 724785d

Please sign in to comment.