Skip to content

Commit

Permalink
new userId module - neustar's fabrick (#5802)
Browse files Browse the repository at this point in the history
* submitting userId module for neustar's fabrick - https://www.home.neustar/fabrick

* fixing 'gulp test' errors

* fixing another test issue (related to ie)

* removing another (last) repeat

* - expose full user id config (including storage) to user id modules (#5803
- removing TODO from test

* - updates to test

Co-authored-by: Anderson, Ben <Ben.Anderson@team.neustar>
  • Loading branch information
andersonbd1 and Anderson, Ben authored Oct 7, 2020
1 parent 16fabf4 commit f907ee6
Show file tree
Hide file tree
Showing 5 changed files with 282 additions and 1 deletion.
3 changes: 2 additions & 1 deletion modules/.submodules.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"intentIqIdSystem",
"zeotapIdPlusIdSystem",
"haloIdSystem",
"quantcastIdSystem"
"quantcastIdSystem",
"fabrickIdSystem"
],
"adpod": [
"freeWheelAdserverVideo",
Expand Down
147 changes: 147 additions & 0 deletions modules/fabrickIdSystem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/**
* This module adds neustar's fabrickId to the User ID module
* The {@link module:modules/userId} module is required
* @module modules/fabrickIdSystem
* @requires module:modules/userId
*/

import * as utils from '../src/utils.js'
import { ajax } from '../src/ajax.js';
import { submodule } from '../src/hook.js';
import { getRefererInfo } from '../src/refererDetection.js';

/** @type {Submodule} */
export const fabrickIdSubmodule = {
/**
* used to link submodule with config
* @type {string}
*/
name: 'fabrickId',

/**
* decode the stored id value for passing to bid requests
* @function decode
* @param {(Object|string)} value
* @returns {(Object|undefined)}
*/
decode(value) {
if (value && value.fabrickId) {
return { 'fabrickId': value.fabrickId };
} else {
return undefined;
}
},

/**
* performs action to obtain id and return a value in the callback's response argument
* @function getId
* @param {SubmoduleConfig} [config]
* @param {ConsentData}
* @param {Object} cacheIdObj - existing id, if any consentData]
* @returns {IdResponse|undefined}
*/
getId(config, consentData, cacheIdObj) {
try {
const configParams = (config && config.params) || {};
if (window.fabrickMod1) {
window.fabrickMod1(configParams, consentData, cacheIdObj);
}
if (!configParams || typeof configParams.apiKey !== 'string') {
utils.logError('fabrick submodule requires an apiKey.');
return;
}
try {
let url = _getBaseUrl(configParams);
let keysArr = Object.keys(configParams);
for (let i in keysArr) {
let k = keysArr[i];
if (k === 'url' || k === 'refererInfo') {
continue;
}
let v = configParams[k];
if (Array.isArray(v)) {
for (let j in v) {
url += `${k}=${v[j]}&`;
}
} else {
url += `${k}=${v}&`;
}
}
// pull off the trailing &
url = url.slice(0, -1)
const referer = _getRefererInfo(configParams);
const urls = new Set();
url = truncateAndAppend(urls, url, 'r', referer.referer);
if (referer.stack && referer.stack[0]) {
url = truncateAndAppend(urls, url, 'r', referer.stack[0]);
}
url = truncateAndAppend(urls, url, 'r', referer.canonicalUrl);
url = truncateAndAppend(urls, url, 'r', window.location.href);

const resp = function (callback) {
const callbacks = {
success: response => {
if (window.fabrickMod2) {
return window.fabrickMod2(
callback, response, configParams, consentData, cacheIdObj);
} else {
let responseObj;
if (response) {
try {
responseObj = JSON.parse(response);
} catch (error) {
utils.logError(error);
responseObj = {};
}
}
callback(responseObj);
}
},
error: error => {
utils.logError(`fabrickId fetch encountered an error`, error);
callback();
}
};
ajax(url, callbacks, null, {method: 'GET', withCredentials: true});
};
return {callback: resp};
} catch (e) {
utils.logError(`fabrickIdSystem encountered an error`, e);
}
} catch (e) {
utils.logError(`fabrickIdSystem encountered an error`, e);
}
}
};

function _getRefererInfo(configParams) {
if (configParams.refererInfo) {
return configParams.refererInfo;
} else {
return getRefererInfo();
}
}

function _getBaseUrl(configParams) {
if (configParams.url) {
return configParams.url;
} else {
return `https://fid.agkn.com/f?`;
}
}

function truncateAndAppend(urls, url, paramName, s) {
if (s && url.length < 2000) {
if (s.length > 200) {
s = s.substring(0, 200);
}
// Don't send the same url in multiple params
if (!urls.has(s)) {
urls.add(s);
return `${url}&${paramName}=${s}`
}
}
return url;
}

submodule('userId', fabrickIdSubmodule);
24 changes: 24 additions & 0 deletions modules/fabrickIdSystem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## Neustar Fabrick User ID Submodule

Fabrick ID Module - https://www.home.neustar/fabrick
Product and Sales Inquiries: 1-855-898-0036

## Example configuration for publishers:
```
pbjs.setConfig({
userSync: {
userIds: [{
name: 'fabrickId',
storage: {
name: 'pbjs_fabrickId',
type: 'cookie',
expires: 7
},
params: {
apiKey: 'your apiKey', // provided to you by Neustar
e: '31c5543c1734d25c7206f5fd591525d0295bec6fe84ff82f946a34fe970a1e66' // example hash identifier (sha256)
}
}]
}
});
```
3 changes: 3 additions & 0 deletions test/mocks/fabrickId.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"fabrickId": 1980
}
106 changes: 106 additions & 0 deletions test/spec/modules/fabrickIdSystem_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import * as utils from '../../../src/utils.js';
import {server} from '../../mocks/xhr.js';

import * as fabrickIdSystem from 'modules/fabrickIdSystem.js';

const defaultConfigParams = {
apiKey: '123',
e: 'abc',
p: ['def', 'hij'],
url: 'http://localhost:9999/test/mocks/fabrickId.json?'
};
const responseHeader = {'Content-Type': 'application/json'}
const fabrickIdSubmodule = fabrickIdSystem.fabrickIdSubmodule;

describe('Fabrick ID System', function() {
let logErrorStub;

beforeEach(function () {
logErrorStub = sinon.stub(utils, 'logError');
});

afterEach(function () {
logErrorStub.restore();
fabrickIdSubmodule.getRefererInfoOverride = null;
});

it('should log an error if no configParams were passed into getId', function () {
fabrickIdSubmodule.getId();
expect(logErrorStub.calledOnce).to.be.true;
});

it('should error on json parsing', function() {
let submoduleCallback = fabrickIdSubmodule.getId({
name: 'fabrickId',
params: defaultConfigParams
}).callback;
let callBackSpy = sinon.spy();
submoduleCallback(callBackSpy);
let request = server.requests[0];
request.respond(
200,
responseHeader,
'] this is not json {'
);
expect(callBackSpy.calledOnce).to.be.true;
expect(logErrorStub.calledOnce).to.be.true;
});

it('should truncate the params', function() {
let r = '';
for (let i = 0; i < 300; i++) {
r += 'r';
}
let configParams = Object.assign({}, defaultConfigParams, {
refererInfo: {
referer: r,
stack: ['s-0'],
canonicalUrl: 'cu-0'
}
});
let submoduleCallback = fabrickIdSubmodule.getId({
name: 'fabrickId',
params: configParams
}).callback;
let callBackSpy = sinon.spy();
submoduleCallback(callBackSpy);
let request = server.requests[0];
r = '';
for (let i = 0; i < 200; i++) {
r += 'r';
}
expect(request.url).to.match(new RegExp(`r=${r}&r=`));
request.respond(
200,
responseHeader,
JSON.stringify({})
);
expect(callBackSpy.calledOnce).to.be.true;
expect(logErrorStub.calledOnce).to.be.false;
});

it('should complete successfully', function() {
let configParams = Object.assign({}, defaultConfigParams, {
refererInfo: {
referer: 'r-0',
stack: ['s-0'],
canonicalUrl: 'cu-0'
}
});
let submoduleCallback = fabrickIdSubmodule.getId({
name: 'fabrickId',
params: configParams
}).callback;
let callBackSpy = sinon.spy();
submoduleCallback(callBackSpy);
let request = server.requests[0];
expect(request.url).to.match(/r=r-0&r=s-0&r=cu-0&r=http/);
request.respond(
200,
responseHeader,
JSON.stringify({})
);
expect(callBackSpy.calledOnce).to.be.true;
expect(logErrorStub.calledOnce).to.be.false;
});
});

0 comments on commit f907ee6

Please sign in to comment.