Skip to content

Commit

Permalink
Tnc id module: initial release (prebid#8435)
Browse files Browse the repository at this point in the history
* - TNCID UserID Module

* revert

* - TNCID UserID Module

* - TNCID UserID Module

* Converted loadTNCScript to Promise

* Added CacheID

* - TNCID UserID module

* - Documentation updated

* - File naming fix

* - type fix

* -no change

* - space removed

* new line removed

* - lint fix

* - eids fix

* - .submodules.json: module name moved in alphabetical order
-  modules/tncIdSystem.md: spacing fix
-  added eids.md, user id spec and eids spec

* - tnc script loaded by loadExternalScript util function

* -resolved lint errors

* - module name refactoring

* - input parameter added to disable load of fallback script

* - TNC fallback script is now input param

* - Added missing tests

Co-authored-by: AzureAD\AnnaVane <[email protected]>
Co-authored-by: micheletnc <[email protected]>
  • Loading branch information
3 people authored and bwhisp committed Jul 13, 2022
1 parent 5687946 commit 5659fa4
Show file tree
Hide file tree
Showing 10 changed files with 258 additions and 3 deletions.
1 change: 1 addition & 0 deletions modules/.submodules.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"quantcastIdSystem",
"sharedIdSystem",
"tapadIdSystem",
"tncIdSystem",
"trustpidSystem",
"uid2IdSystem",
"unifiedIdSystem",
Expand Down
63 changes: 63 additions & 0 deletions modules/tncIdSystem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { submodule } from '../src/hook.js';
import { logInfo } from '../src/utils.js';
import { loadExternalScript } from '../src/adloader.js';

const MODULE_NAME = 'tncId';
let url = null;

const waitTNCScript = (tncNS) => {
return new Promise((resolve, reject) => {
var tnc = window[tncNS];
if (!tnc) reject(new Error('No TNC Object'));
if (tnc.tncid) resolve(tnc.tncid);
tnc.ready(() => {
tnc = window[tncNS];
if (tnc.tncid) resolve(tnc.tncid);
else tnc.on('data-sent', () => resolve(tnc.tncid));
});
});
}

const loadRemoteScript = () => {
return new Promise((resolve) => {
loadExternalScript(url, MODULE_NAME, resolve);
})
}

const tncCallback = function (cb) {
let tncNS = '__tnc';
let promiseArray = [];
if (!window[tncNS]) {
tncNS = '__tncPbjs';
promiseArray.push(loadRemoteScript());
}

return Promise.all(promiseArray).then(() => waitTNCScript(tncNS)).then(cb).catch(() => cb());
}

export const tncidSubModule = {
name: MODULE_NAME,
decode(id) {
return {
tncid: id
};
},
gvlid: 750,
getId(config, consentData) {
const gdpr = (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) ? 1 : 0;
const consentString = gdpr ? consentData.consentString : '';

if (gdpr && !consentString) {
logInfo('Consent string is required for TNCID module');
return;
}

if (config.params && config.params.url) { url = config.params.url; }

return {
callback: function (cb) { return tncCallback(cb); }
}
}
}

submodule('userId', tncidSubModule)
33 changes: 33 additions & 0 deletions modules/tncIdSystem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# TNCID UserID Module

### Prebid Configuration

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

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

### TNCIDIdSystem module Configuration

You can configure this submodule in your `userSync.userIds[]` configuration:

```javascript
pbjs.setConfig({
userSync: {
userIds: [{
name: 'tncId',
params: {
url: 'https://js.tncid.app/remote.min.js' //Optional
}
}],
syncDelay: 5000
}
});
```
#### Configuration Params

| Param Name | Required | Type | Description |
| --- | --- | --- | --- |
| name | Required | String | ID value for the TNCID module: `"tncId"` |
| params.url | Optional | String | Provide TNC fallback script URL, this script is loaded if there is no TNC script on page |
7 changes: 7 additions & 0 deletions modules/userId/eids.js
Original file line number Diff line number Diff line change
Expand Up @@ -307,11 +307,18 @@ export const USER_IDS_CONFIG = {
}
},

// tncId
'tncid': {
source: 'thenewco.it',
atype: 3
},

// Gravito MP ID
'gravitompId': {
source: 'gravito.net',
atype: 1
},

};

// this function will create an eid object for the given UserId sub-module
Expand Down
7 changes: 7 additions & 0 deletions modules/userId/eids.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,13 @@ userIdAsEids = [
id: 'some-random-id-value',
atype: 3
}]
},
{
source: 'thenewco.it',
uids: [{
id: 'some-random-id-value',
atype: 3
}]
}
]
```
17 changes: 17 additions & 0 deletions modules/userId/userId.md
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,20 @@ pbjs.setConfig({
}
});
```
```
Example showing how to configure a `params` object to pass directly to bid adapters
```
pbjs.setConfig({
userSync: {
userIds: [{
name: 'tncId',
params: {
providerId: "c8549079-f149-4529-a34b-3fa91ef257d1"
}
}],
syncDelay: 5000
}
});
```
1 change: 1 addition & 0 deletions src/adloader.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const _approvedLoadExternalJSList = [
'browsi',
'brandmetrics',
'justtag',
'tncId',
'akamaidap',
'ftrackId',
'inskin',
Expand Down
14 changes: 14 additions & 0 deletions test/spec/modules/eids_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,20 @@ describe('eids array generation for known sub-modules', function() {
}]
});
});
it('tncid', function() {
const userId = {
tncid: 'TEST_TNCID'
};
const newEids = createEidsArray(userId);
expect(newEids.length).to.equal(1);
expect(newEids[0]).to.deep.equal({
source: 'thenewco.it',
uids: [{
id: 'TEST_TNCID',
atype: 3
}]
});
});
it('pubProvidedId', function() {
const userId = {
pubProvidedId: [{
Expand Down
109 changes: 109 additions & 0 deletions test/spec/modules/tncIdSystem_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { tncidSubModule } from 'modules/tncIdSystem';

const consentData = {
gdprApplies: true,
consentString: 'GDPR_CONSENT_STRING'
};

describe('TNCID tests', function () {
describe('name', () => {
it('should expose the name of the submodule', () => {
expect(tncidSubModule.name).to.equal('tncId');
});
});

describe('gvlid', () => {
it('should expose the vendor id', () => {
expect(tncidSubModule.gvlid).to.equal(750);
});
});

describe('decode', () => {
it('should wrap the given value inside an object literal', () => {
expect(tncidSubModule.decode('TNCID_TEST_ID')).to.deep.equal({
tncid: 'TNCID_TEST_ID'
});
});
});

describe('getId', () => {
afterEach(function () {
Object.defineProperty(window, '__tnc', {value: undefined, configurable: true});
Object.defineProperty(window, '__tncPbjs', {value: undefined, configurable: true});
});

it('Should NOT give TNCID if GDPR applies but consent string is missing', function () {
const res = tncidSubModule.getId({}, { gdprApplies: true });
expect(res).to.be.undefined;
});

it('GDPR is OK and page has no TNC script on page, script goes in error, no TNCID is returned', function () {
const completeCallback = sinon.spy();
const {callback} = tncidSubModule.getId({}, consentData);

return callback(completeCallback).then(() => {
expect(completeCallback.calledOnce).to.be.true;
})
});

it('GDPR is OK and page has TNC script with ns: __tnc, present TNCID is returned', function () {
Object.defineProperty(window, '__tnc', {
value: {
ready: (readyFunc) => { readyFunc() },
on: (name, cb) => { cb() },
tncid: 'TNCID_TEST_ID_1',
providerId: 'TEST_PROVIDER_ID_1',
},
configurable: true
});

const completeCallback = sinon.spy();
const {callback} = tncidSubModule.getId({}, { gdprApplies: false });

return callback(completeCallback).then(() => {
expect(completeCallback.calledOnceWithExactly('TNCID_TEST_ID_1')).to.be.true;
})
});

it('GDPR is OK and page has TNC script with ns: __tnc but not loaded, TNCID is assigned and returned', function () {
Object.defineProperty(window, '__tnc', {
value: {
ready: (readyFunc) => { readyFunc() },
on: (name, cb) => { cb() },
providerId: 'TEST_PROVIDER_ID_1',
},
configurable: true
});

const completeCallback = sinon.spy();
const {callback} = tncidSubModule.getId({}, { gdprApplies: false });

return callback(completeCallback).then(() => {
expect(completeCallback.calledOnceWithExactly(undefined)).to.be.true;
})
});

it('GDPR is OK and page has TNC script with ns: __tncPbjs, TNCID is returned', function () {
Object.defineProperty(window, '__tncPbjs', {
value: {
ready: (readyFunc) => { readyFunc() },
on: (name, cb) => {
window.__tncPbjs.tncid = 'TNCID_TEST_ID_2';
cb();
},
providerId: 'TEST_PROVIDER_ID_1',
options: {},
},
configurable: true,
writable: true
});

const completeCallback = sinon.spy();
const {callback} = tncidSubModule.getId({params: {url: 'TEST_URL'}}, consentData);

return callback(completeCallback).then(() => {
expect(completeCallback.calledOnceWithExactly('TNCID_TEST_ID_2')).to.be.true;
})
});
});
});
9 changes: 6 additions & 3 deletions test/spec/modules/userId_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {pubProvidedIdSubmodule} from 'modules/pubProvidedIdSystem.js';
import {criteoIdSubmodule} from 'modules/criteoIdSystem.js';
import {mwOpenLinkIdSubModule} from 'modules/mwOpenLinkIdSystem.js';
import {tapadIdSubmodule} from 'modules/tapadIdSystem.js';
import {tncidSubModule} from 'modules/tncIdSystem.js';
import {getPrebidInternal} from 'src/utils.js';
import {uid2IdSubmodule} from 'modules/uid2IdSystem.js';
import {admixerIdSubmodule} from 'modules/admixerIdSystem.js';
Expand Down Expand Up @@ -784,9 +785,9 @@ describe('User ID', function () {
expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 1 submodules');
});

it('config with 21 configurations should result in 21 submodules add', function () {
it('config with 22 configurations should result in 22 submodules add', function () {
init(config);
setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, hadronIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule]);
setSubmoduleRegistry([sharedIdSystemSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, hadronIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule, admixerIdSubmodule, deepintentDpesSubmodule, dmdIdSubmodule, amxIdSubmodule, kinessoIdSubmodule, adqueryIdSubmodule, tncidSubModule]);
config.setConfig({
userSync: {
syncDelay: 0,
Expand Down Expand Up @@ -847,10 +848,12 @@ describe('User ID', function () {
}, {
name: 'qid',
storage: {name: 'qid', type: 'html5'}
}, {
name: 'tncId'
}]
}
});
expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 21 submodules');
expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 22 submodules');
});

it('config syncDelay updates module correctly', function () {
Expand Down

0 comments on commit 5659fa4

Please sign in to comment.