Skip to content

Commit

Permalink
Just Id Userid System: add new ID module (prebid#7985)
Browse files Browse the repository at this point in the history
* just id user module

* fix

* fix

* docs fix

* basic mode

* fixes

* fix

* .

* .

Co-authored-by: pchrominski <pawel.chrominski@netsprint.eu>
  • Loading branch information
pchrominski and pchrominski authored Feb 15, 2022
1 parent 27c101b commit 2a05213
Show file tree
Hide file tree
Showing 7 changed files with 509 additions and 1 deletion.
1 change: 1 addition & 0 deletions modules/.submodules.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"idxIdSystem",
"imuIdSystem",
"intentIqIdSystem",
"justIdSystem",
"kinessoIdSystem",
"liveIntentIdSystem",
"lotamePanoramaIdSystem",
Expand Down
206 changes: 206 additions & 0 deletions modules/justIdSystem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/**
* This module adds JustId to the User ID module
* The {@link module:modules/userId} module is required
* @module modules/justIdSystem
* @requires module:modules/userId
*/

import * as utils from '../src/utils.js'
import { submodule } from '../src/hook.js'
import { loadExternalScript } from '../src/adloader.js'
import includes from 'core-js-pure/features/array/includes.js';

const MODULE_NAME = 'justId';
const EXTERNAL_SCRIPT_MODULE_CODE = 'justtag';
const LOG_PREFIX = 'User ID - JustId submodule: ';
const GVLID = 160;
const DEFAULT_PARTNER = 'pbjs-just-id-module';
const DEFAULT_ATM_VAR_NAME = '__atm';

const MODE_BASIC = 'BASIC';
const MODE_COMBINED = 'COMBINED';
const DEFAULT_MODE = MODE_BASIC;

export const EX_URL_REQUIRED = new Error(`params.url is required in ${MODE_COMBINED} mode`);
export const EX_INVALID_MODE = new Error(`Invalid params.mode. Allowed values: ${MODE_BASIC}, ${MODE_COMBINED}`);

/** @type {Submodule} */
export const justIdSubmodule = {
/**
* used to link submodule with config
* @type {string}
*/
name: MODULE_NAME,
/**
* required for the gdpr enforcement module
*/
gvlid: GVLID,

/**
* decode the stored id value for passing to bid requests
* @function
* @param {{uid:string}} value
* @returns {{justId:string}}
*/
decode(value) {
utils.logInfo(LOG_PREFIX, 'decode', value);
const justId = value && value.uid;
return justId && {justId: justId};
},

/**
* performs action to obtain id and return a value in the callback's response argument
* @function
* @param {SubmoduleConfig} config
* @param {ConsentData} consentData
* @param {(Object|undefined)} cacheIdObj
* @returns {IdResponse|undefined}
*/
getId(config, consentData, cacheIdObj) {
utils.logInfo(LOG_PREFIX, 'getId', config, consentData, cacheIdObj);

var configWrapper
try {
configWrapper = new ConfigWrapper(config);
} catch (e) {
utils.logError(LOG_PREFIX, e);
}

return configWrapper && {
callback: function(cbFun) {
try {
utils.logInfo(LOG_PREFIX, 'fetching uid...');

var uidProvider = configWrapper.isCombinedMode()
? new CombinedUidProvider(configWrapper, consentData, cacheIdObj)
: new BasicUidProvider(configWrapper);

uidProvider.getUid(justId => {
if (utils.isEmptyStr(justId)) {
utils.logError(LOG_PREFIX, 'empty uid!');
cbFun();
return;
}
cbFun({uid: justId});
}, err => {
utils.logError(LOG_PREFIX, 'error during fetching', err);
cbFun();
});
} catch (e) {
utils.logError(LOG_PREFIX, 'Error during fetching...', e);
}
}
};
}
};

export const ConfigWrapper = function(config) {
this.getConfig = function() {
return config;
}

this.getMode = function() {
return (params().mode || DEFAULT_MODE).toUpperCase();
}

this.getPartner = function() {
return params().partner || DEFAULT_PARTNER;
}

this.isCombinedMode = function() {
return this.getMode() === MODE_COMBINED;
}

this.getAtmVarName = function() {
return params().atmVarName || DEFAULT_ATM_VAR_NAME;
}

this.getUrl = function() {
const u = params().url;
const url = new URL(u);
url.searchParams.append('sourceId', this.getPartner());
return url.toString();
}

function params() {
return config.params || {};
}

// validation
if (!includes([MODE_BASIC, MODE_COMBINED], this.getMode())) {
throw EX_INVALID_MODE;
}

var url = params().url;
if (this.isCombinedMode() && (utils.isEmptyStr(url) || !utils.isStr(url))) {
throw EX_URL_REQUIRED;
}
}

const CombinedUidProvider = function(configWrapper, consentData, cacheIdObj) {
const url = configWrapper.getUrl();

this.getUid = function(idCallback, errCallback) {
const scriptTag = loadExternalScript(url, EXTERNAL_SCRIPT_MODULE_CODE, () => {
utils.logInfo(LOG_PREFIX, 'script loaded', url);

const eventDetails = {
detail: {
config: configWrapper.getConfig(),
consentData: consentData,
cacheIdObj: cacheIdObj
}
}

scriptTag.dispatchEvent(new CustomEvent('prebidGetId', eventDetails));
})

scriptTag.addEventListener('justIdReady', event => {
utils.logInfo(LOG_PREFIX, 'received justId', event);
idCallback(event.detail && event.detail.justId);
});

scriptTag.onerror = errCallback;
}
}

const BasicUidProvider = function(configWrapper) {
const atmVarName = configWrapper.getAtmVarName();

this.getUid = function(idCallback, errCallback) {
var atm = getAtm();
if (typeof atm !== 'function') { // it may be AsyncFunction, so we can't use utils.isFn
utils.logInfo(LOG_PREFIX, 'ATM function not found!', atmVarName, atm);
errCallback('ATM not found');
return
}

atm = function() { // stub is replaced after ATM is loaded so we must refer them directly by global variable
return getAtm().apply(this, arguments);
}

atm('getReadyState', () => {
Promise.resolve(atm('getVersion')) // atm('getVersion') returns string || Promise<string>
.then(atmVersion => {
utils.logInfo(LOG_PREFIX, 'ATM Version', atmVersion);
if (utils.isStr(atmVersion)) { // getVersion command was introduced in same ATM version as getUid command
atm('getUid', idCallback);
} else {
errCallback('ATM getUid not supported');
}
})
});
}

function getAtm() {
return jtUtils.getAtm(atmVarName);
}
}

export const jtUtils = {
getAtm(atmVarName) {
return window[atmVarName];
}
}

submodule('userId', justIdSubmodule);
70 changes: 70 additions & 0 deletions modules/justIdSystem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
## Just ID User ID Submodule

For assistance setting up your module please contact us at [prebid@justtag.com](prebid@justtag.com).

First, make sure to add the Just ID submodule to your Prebid.js package with:

```
gulp build --modules=userId,justIdSystem
```

### Modes

- **BASIC** - in this mode we rely on Justtag library that already exists on publisher page. Typicaly that library expose global variable called `__atm`

- **COMBINED** - Just ID generation process may differ between various cases depends on publishers. This mode combines our js library with prebid for ease of integration

### Disclosure

This module in `COMBINED` mode loads external JavaScript to generate optimal quality user ID. It is possible to retrieve user ID, without loading additional script by this module in `BASIC` mode.

### Just ID Example

ex. 1. Mode `COMBINED`

```
pbjs.setConfig({
userSync: {
userIds: [{
name: 'justId',
params: {
mode: 'COMBINED',
url: 'https://id.nsaudience.pl/getId.js', // required in COMBINED mode
partner: 'pbjs-just-id-module' // optional, may be required in some custom integrations with Justtag
}
}]
}
});
```

ex. 2. Mode `BASIC`

```
pbjs.setConfig({
userSync: {
userIds: [{
name: 'justId',
params: {
mode: 'BASIC', // default
atmVarName: '__atm' // optional
}
}]
}
});
```

### Prebid Params

Individual params may be set for the Just ID Submodule.

## Parameter Descriptions for the `userSync` Configuration Section
The below parameters apply only to the Just ID integration.

| Param under usersync.userIds[] | Scope | Type | Description | Example |
| --- | --- | --- | --- | --- |
| name | Required | String | ID of the module - `'justId'` | `'justId'` |
| params | Optional | Object | Details for Just ID syncing. | |
| params.mode | Optional | String | Mode in which the module works. Available Modes: `'COMBINED'`, `'BASIC'`(default) | `'COMBINED'` |
| params.atmVarName | Optional | String | Name of global object property that point to Justtag ATM Library. Defaults to `'__atm'` | `'__atm'` |
| params.url | Optional | String | API Url, **required** in `COMBINED` mode | `'https://id.nsaudience.pl/getId.js'` |
| params.partner | Optional | String | This is the Justtag Partner Id which may be required in some custom integrations with Justtag | `'some-publisher'` |
6 changes: 6 additions & 0 deletions modules/userId/eids.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ const USER_IDS_CONFIG = {
atype: 1
},

// justId
'justId': {
source: 'justtag.com',
atype: 1
},

// pubCommonId
'pubcid': {
source: 'pubcid.org',
Expand Down
8 changes: 8 additions & 0 deletions modules/userId/eids.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ userIdAsEids = [
}]
},
{
source: 'justtag.com',
uids: [{
id: 'justId',
atype: 1
}]
},
{
source: 'neustar.biz',
uids: [{
Expand Down
3 changes: 2 additions & 1 deletion src/adloader.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ const _approvedLoadExternalJSList = [
'outstream',
'adagio',
'browsi',
'brandmetrics'
'brandmetrics',
'justtag'
]

/**
Expand Down
Loading

0 comments on commit 2a05213

Please sign in to comment.