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

IDx user id submodule #5826

Merged
merged 6 commits into from
Oct 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions modules/.submodules.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"zeotapIdPlusIdSystem",
"haloIdSystem",
"quantcastIdSystem",
"idxIdSystem",
"fabrickIdSystem"
],
"adpod": [
Expand Down
61 changes: 61 additions & 0 deletions modules/idxIdSystem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* This module adds IDx to the User ID module
* The {@link module:modules/userId} module is required
* @module modules/idxIdSystem
* @requires module:modules/userId
*/
import * as utils from '../src/utils.js'
import { submodule } from '../src/hook.js';
import { getStorageManager } from '../src/storageManager.js';

const IDX_MODULE_NAME = 'idx';
const IDX_COOKIE_NAME = '_idx';
export const storage = getStorageManager();

function readIDxFromCookie() {
return storage.cookiesAreEnabled ? storage.getCookie(IDX_COOKIE_NAME) : null;
}

function readIDxFromLocalStorage() {
return storage.localStorageIsEnabled ? storage.getDataFromLocalStorage(IDX_COOKIE_NAME) : null;
}

/** @type {Submodule} */
export const idxIdSubmodule = {
/**
* used to link submodule with config
* @type {string}
*/
name: IDX_MODULE_NAME,
/**
* decode the stored id value for passing to bid requests
* @function
* @param { Object | string | undefined } value
* @return { Object | string | undefined }
*/
decode(value) {
const idxVal = value ? utils.isStr(value) ? value : utils.isPlainObject(value) ? value.id : undefined : undefined;
return idxVal ? {
'idx': idxVal
} : undefined;
},
/**
* performs action to obtain id and return a value in the callback's response argument
* @function
* @param {SubmoduleConfig} config
* @return {{id: string | undefined } | undefined}
*/
getId() {
const idxString = readIDxFromLocalStorage() || readIDxFromCookie();
if (typeof idxString == 'string' && idxString) {
try {
const idxObj = JSON.parse(idxString);
return idxObj && idxObj.idx ? { id: idxObj.idx } : undefined;
} catch (error) {
utils.logError(error);
}
}
return undefined;
}
};
submodule('userId', idxIdSubmodule);
22 changes: 22 additions & 0 deletions modules/idxIdSystem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## IDx User ID Submodule

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

### Prebid Params

Individual params may be set for the IDx Submodule.
```
pbjs.setConfig({
userSync: {
userIds: [{
name: 'idx',
}]
}
});
```
## Parameter Descriptions for the `userSync` Configuration Section
The below parameters apply only to the IDx integration.

| Param under usersync.userIds[] | Scope | Type | Description | Example |
| --- | --- | --- | --- | --- |
| name | Required | String | ID of the module - `"idx"` | `"idx"` |
8 changes: 7 additions & 1 deletion modules/userId/eids.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,13 @@ const USER_IDS_CONFIG = {
'quantcastId': {
source: 'quantcast.com',
atype: 1
}
},

// IDx
'idx': {
source: 'idx.lat',
atype: 1
},
};

// this function will create an eid object for the given UserId sub-module
Expand Down
117 changes: 117 additions & 0 deletions test/spec/modules/idxIdSystem_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { expect } from 'chai';
import find from 'core-js-pure/features/array/find.js';
import { config } from 'src/config.js';
import { init, requestBidsHook, setSubmoduleRegistry } from 'modules/userId/index.js';
import { storage, idxIdSubmodule } from 'modules/idxIdSystem.js';

const IDX_COOKIE_NAME = '_idx';
const IDX_DUMMY_VALUE = 'idx value for testing';
const IDX_COOKIE_STORED = '{ "idx": "' + IDX_DUMMY_VALUE + '" }';
const ID_COOKIE_OBJECT = { id: IDX_DUMMY_VALUE };
const IDX_COOKIE_OBJECT = { idx: IDX_DUMMY_VALUE };

function getConfigMock() {
return {
userSync: {
syncDelay: 0,
userIds: [{
name: 'idx'
}]
}
}
}

function getAdUnitMock(code = 'adUnit-code') {
return {
code,
mediaTypes: {banner: {}, native: {}},
sizes: [
[300, 200],
[300, 600]
],
bids: [{
bidder: 'sampleBidder',
params: { placementId: 'banner-only-bidder' }
}]
};
}

describe('IDx ID System', () => {
let getDataFromLocalStorageStub, localStorageIsEnabledStub;
let getCookieStub, cookiesAreEnabledStub;

beforeEach(() => {
getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage');
localStorageIsEnabledStub = sinon.stub(storage, 'localStorageIsEnabled');
getCookieStub = sinon.stub(storage, 'getCookie');
cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled');
});

afterEach(() => {
getDataFromLocalStorageStub.restore();
localStorageIsEnabledStub.restore();
getCookieStub.restore();
cookiesAreEnabledStub.restore();
});

describe('IDx: test "getId" method', () => {
it('provides the stored IDx if a cookie exists', () => {
getCookieStub.withArgs(IDX_COOKIE_NAME).returns(IDX_COOKIE_STORED);
let idx = idxIdSubmodule.getId();
expect(idx).to.deep.equal(ID_COOKIE_OBJECT);
});

it('provides the stored IDx if cookie is absent but present in local storage', () => {
getDataFromLocalStorageStub.withArgs(IDX_COOKIE_NAME).returns(IDX_COOKIE_STORED);
let idx = idxIdSubmodule.getId();
expect(idx).to.deep.equal(ID_COOKIE_OBJECT);
});

it('returns undefined if both cookie and local storage are empty', () => {
let idx = idxIdSubmodule.getId();
expect(idx).to.be.undefined;
})
});

describe('IDx: test "decode" method', () => {
it('provides the IDx from a stored object', () => {
expect(idxIdSubmodule.decode(ID_COOKIE_OBJECT)).to.deep.equal(IDX_COOKIE_OBJECT);
});

it('provides the IDx from a stored string', () => {
expect(idxIdSubmodule.decode(IDX_DUMMY_VALUE)).to.deep.equal(IDX_COOKIE_OBJECT);
});
});

describe('requestBids hook', () => {
let adUnits;

beforeEach(() => {
adUnits = [getAdUnitMock()];
setSubmoduleRegistry([idxIdSubmodule]);
init(config);
config.setConfig(getConfigMock());
getCookieStub.withArgs(IDX_COOKIE_NAME).returns(IDX_COOKIE_STORED);
});

it('when a stored IDx exists it is added to bids', (done) => {
requestBidsHook(() => {
adUnits.forEach(unit => {
unit.bids.forEach(bid => {
expect(bid).to.have.deep.nested.property('userId.idx');
expect(bid.userId.idx).to.equal(IDX_DUMMY_VALUE);
const idxIdAsEid = find(bid.userIdAsEids, e => e.source == 'idx.lat');
expect(idxIdAsEid).to.deep.equal({
source: 'idx.lat',
uids: [{
id: IDX_DUMMY_VALUE,
atype: 1,
}]
});
});
});
done();
}, { adUnits });
});
});
});