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

Navegg UserID Submodule: conform with pub storage configuration #12032

Merged
merged 6 commits into from
Aug 5, 2024
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
120 changes: 62 additions & 58 deletions modules/naveggIdSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
*/
import { isStr, isPlainObject, logError } from '../src/utils.js';
import { submodule } from '../src/hook.js';
import { ajax } from '../src/ajax.js';
import {getStorageManager} from '../src/storageManager.js';
import {MODULE_TYPE_UID} from '../src/activities/modules.js';
import { ajaxBuilder } from '../src/ajax.js';
import { getStorageManager } from '../src/storageManager.js';
import { MODULE_TYPE_UID } from '../src/activities/modules.js';

/**
* @typedef {import('../modules/userId/index.js').Submodule} Submodule
Expand All @@ -19,61 +19,72 @@ const MODULE_NAME = 'naveggId';
const OLD_NAVEGG_ID = 'nid';
const NAVEGG_ID = 'nvggid';
const BASE_URL = 'https://id.navegg.com/uid/';
const DEFAULT_EXPIRE = 8 * 24 * 3600 * 1000;
const INVALID_EXPIRE = 3600 * 1000;

export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME});

function getNaveggIdFromApi() {
const callbacks = {
success: response => {
if (response) {
try {
const responseObj = JSON.parse(response);
writeCookie(NAVEGG_ID, responseObj[NAVEGG_ID]);
} catch (error) {
logError(error);
function getIdFromAPI() {
const resp = function (callback) {
ajaxBuilder()(
BASE_URL,
response => {
if (response) {
let responseObj;
try {
responseObj = JSON.parse(response);
} catch (error) {
logError(error);
const fallbackValue = getNaveggIdFromLocalStorage() || getOldCookie();
callback(fallbackValue);
}

if (responseObj && responseObj[NAVEGG_ID]) {
callback(responseObj[NAVEGG_ID]);
} else {
const fallbackValue = getNaveggIdFromLocalStorage() || getOldCookie();
callback(fallbackValue);
}
}
}
},
error: error => {
logError('Navegg ID fetch encountered an error', error);
}
},
error => {
logError('Navegg ID fetch encountered an error', error);
const fallbackValue = getNaveggIdFromLocalStorage() || getOldCookie();
callback(fallbackValue);
},
{method: 'GET', withCredentials: false});
};
ajax(BASE_URL, callbacks, undefined, { method: 'GET', withCredentials: false });
}

function writeCookie(key, value) {
try {
if (storage.cookiesAreEnabled) {
let expTime = new Date();
const expires = value ? DEFAULT_EXPIRE : INVALID_EXPIRE;
expTime.setTime(expTime.getTime() + expires);
storage.setCookie(key, value, expTime.toUTCString(), 'none');
}
} catch (e) {
logError(e);
}
return resp;
}

function readnaveggIdFromLocalStorage() {
return storage.localStorageIsEnabled ? storage.getDataFromLocalStorage(NAVEGG_ID) : null;
/**
* @returns {string | null}
*/
function readNvgIdFromCookie() {
return storage.cookiesAreEnabled ? (storage.findSimilarCookies('nvg') ? storage.findSimilarCookies('nvg')[0] : null) : null;
}

function readnaveggIDFromCookie() {
return storage.cookiesAreEnabled ? storage.getCookie(NAVEGG_ID) : null;
/**
* @returns {string | null}
*/
function readNavIdFromCookie() {
return storage.cookiesAreEnabled() ? (storage.findSimilarCookies('nav') ? storage.findSimilarCookies('nav')[0] : null) : null;
}

function readoldnaveggIDFromCookie() {
return storage.cookiesAreEnabled ? storage.getCookie(OLD_NAVEGG_ID) : null;
/**
* @returns {string | null}
*/
function readOldNaveggIdFromCookie() {
return storage.cookiesAreEnabled() ? storage.getCookie(OLD_NAVEGG_ID) : null;
}

function readnvgIDFromCookie() {
return storage.cookiesAreEnabled ? (storage.findSimilarCookies('nvg') ? storage.findSimilarCookies('nvg')[0] : null) : null;
/**
* @returns {string | null}
*/
function getOldCookie() {
const oldCookie = readOldNaveggIdFromCookie() || readNvgIdFromCookie() || readNavIdFromCookie();
return oldCookie;
}

function readnavIDFromCookie() {
return storage.cookiesAreEnabled ? (storage.findSimilarCookies('nav') ? storage.findSimilarCookies('nav')[0] : null) : null;
/**
* @returns {string | null}
*/
function getNaveggIdFromLocalStorage() {
return storage.localStorageIsEnabled() ? storage.getDataFromLocalStorage(NAVEGG_ID) : null;
}

/** @type {Submodule} */
Expand All @@ -95,23 +106,16 @@ export const naveggIdSubmodule = {
'naveggId': naveggIdVal.split('|')[0]
} : 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 naveggIdString = readnaveggIdFromLocalStorage() || readnaveggIDFromCookie() || getNaveggIdFromApi() || readoldnaveggIDFromCookie() || readnvgIDFromCookie() || readnavIDFromCookie();

if (typeof naveggIdString == 'string' && naveggIdString) {
try {
return { id: naveggIdString };
} catch (error) {
logError(error);
}
}
return undefined;
getId(config, consentData) {
const resp = getIdFromAPI()
return {callback: resp}
},
eids: {
'naveggId': {
Expand Down
8 changes: 8 additions & 0 deletions modules/naveggIdSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ pbjs.setConfig({
userSync: {
userIds: [{
name: 'naveggId',
storage: {
name : 'nvggid',
type : 'cookie&html5',
expires: 8
}
}]
}
});
Expand All @@ -20,3 +25,6 @@ The below parameters apply only to the naveggID integration.
| Param under usersync.userIds[] | Scope | Type | Description | Example |
| --- | --- | --- | --- | --- |
| name | Required | String | ID of the module - `"naveggId"` | `"naveggId"` |
| storage.name | Required | String | The name of the cookie or html5 local storage where the user ID will be stored. | `"nvggid"` |
| storage.type | Required | String | Must be "`cookie`", "`html5`" or "`cookie&html5`". This is where the results of the user ID will be stored. | `"cookie&html5"` |
| storage.expires | Required | Integer | How long (in days) the user ID information will be stored. | `8` |
172 changes: 141 additions & 31 deletions test/spec/modules/naveggIdSystem_spec.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,155 @@
import { naveggIdSubmodule, storage } from 'modules/naveggIdSystem.js';
import { naveggIdSubmodule, storage, getIdFromAPI } from 'modules/naveggIdSystem.js';
import { server } from 'test/mocks/xhr.js';
import * as ajaxLib from 'src/ajax.js';

describe('naveggId', function () {
let sandbox;
beforeEach(() => {
sandbox = sinon.sandbox.create();
sandbox.stub(storage, 'getDataFromLocalStorage');
const NAVEGGID_CONFIG_COOKIE_HTML5 = {
storage: {
name: 'nvggid',
type: 'cookie&html5',
expires: 8
}
}

const MOCK_RESPONSE = {
nvggid: 'test_nvggid'
}

const MOCK_RESPONSE_NULL = {
nvggid: null
}

function mockResponse(responseText, isSuccess = true) {
return function(url, callbacks) {
if (isSuccess) {
callbacks.success(responseText)
} else {
callbacks.error(new Error('Mock Error'))
}
}
}

function deleteAllCookies() {
document.cookie.split(';').forEach(cookie => {
const eqPos = cookie.indexOf('=');
const name = eqPos > -1 ? cookie.substring(0, eqPos) : cookie;
document.cookie = name + '=;expires=Thu, 01 Jan 1970 00:00:00 GMT';
});
}

function setLocalStorage() {
storage.setDataInLocalStorage('nvggid', 'localstorage_value');
}

describe('getId', function () {
let ajaxStub, ajaxBuilderStub;

beforeEach(function() {
ajaxStub = sinon.stub();
ajaxBuilderStub = sinon.stub(ajaxLib, 'ajaxBuilder').returns(ajaxStub);
});

afterEach(function() {
ajaxBuilderStub.restore();
deleteAllCookies();
storage.removeDataFromLocalStorage('nvggid');
});

it('should get the value from the existing localstorage', function() {
setLocalStorage();

const callback = sinon.spy();
const apiCallback = naveggIdSubmodule.getId(NAVEGGID_CONFIG_COOKIE_HTML5).callback;

ajaxStub.callsFake((url, successCallbacks, errorCallback, options) => {
if (successCallbacks && typeof successCallbacks === 'function') {
successCallbacks(JSON.stringify(MOCK_RESPONSE_NULL));
}
});
apiCallback(callback)

expect(callback.calledOnce).to.be.true;
expect(callback.calledWith('localstorage_value')).to.be.true;
});
afterEach(() => {
sandbox.restore();

it('should get the value from a nid cookie', function() {
storage.setCookie('nid', 'old_nid_cookie', storage.expires)

const callback = sinon.spy();
const apiCallback = naveggIdSubmodule.getId(NAVEGGID_CONFIG_COOKIE_HTML5).callback;

ajaxStub.callsFake((url, successCallbacks, errorCallback, options) => {
if (successCallbacks && typeof successCallbacks === 'function') {
successCallbacks(JSON.stringify(MOCK_RESPONSE_NULL));
}
});
apiCallback(callback)

expect(callback.calledOnce).to.be.true;
expect(callback.calledWith('old_nid_cookie')).to.be.true;
});

it('should NOT find navegg id', function () {
let id = naveggIdSubmodule.getId();
it('should get the value from a nav cookie', function() {
storage.setCookie('navId', 'old_nav_cookie', storage.expires)

const callback = sinon.spy();
const apiCallback = naveggIdSubmodule.getId(NAVEGGID_CONFIG_COOKIE_HTML5).callback;

expect(id).to.be.undefined;
ajaxStub.callsFake((url, successCallbacks, errorCallback, options) => {
if (successCallbacks && typeof successCallbacks === 'function') {
successCallbacks(JSON.stringify(MOCK_RESPONSE_NULL));
}
});
apiCallback(callback)

expect(callback.calledOnce).to.be.true;
expect(callback.calledWith('old_nav_cookie')).to.be.true;
});

it('getId() should return "test-nid" id from cookie OLD_NAVEGG_ID', function() {
sinon.stub(storage, 'getCookie').withArgs('nid').returns('test-nid');
let id = naveggIdSubmodule.getId();
expect(id).to.be.deep.equal({id: 'test-nid'})
})
it('should get the value from an old nvg cookie', function() {
storage.setCookie('nvgid', 'old_nvg_cookie', storage.expires)

const callback = sinon.spy();
const apiCallback = naveggIdSubmodule.getId(NAVEGGID_CONFIG_COOKIE_HTML5).callback;

ajaxStub.callsFake((url, successCallbacks, errorCallback, options) => {
if (successCallbacks && typeof successCallbacks === 'function') {
successCallbacks(JSON.stringify(MOCK_RESPONSE_NULL));
}
});
apiCallback(callback)

it('getId() should return "test-nvggid" id from local storage NAVEGG_ID', function() {
storage.getDataFromLocalStorage.callsFake(() => 'test-ninvggidd')
expect(callback.calledOnce).to.be.true;
expect(callback.calledWith('old_nvg_cookie')).to.be.true;
});

let id = naveggIdSubmodule.getId();
expect(id).to.be.deep.equal({id: 'test-ninvggidd'})
})
it('should return correct value from API response', function(done) {
const callback = sinon.spy();
const apiCallback = naveggIdSubmodule.getId(NAVEGGID_CONFIG_COOKIE_HTML5).callback;

it('getId() should return "test-nvggid" id from local storage NAV0', function() {
storage.getDataFromLocalStorage.callsFake(() => 'nvgid-nav0')
ajaxStub.callsFake((url, successCallbacks, errorCallback, options) => {
if (successCallbacks && typeof successCallbacks === 'function') {
successCallbacks(JSON.stringify(MOCK_RESPONSE));
}
});
apiCallback(callback)

let id = naveggIdSubmodule.getId();
expect(id).to.be.deep.equal({id: 'nvgid-nav0'})
})
expect(callback.calledOnce).to.be.true;
expect(callback.calledWith('test_nvggid')).to.be.true;
done();
});

it('getId() should return "test-nvggid" id from local storage NVG0', function() {
storage.getDataFromLocalStorage.callsFake(() => 'nvgid-nvg0')
it('should return no value from API response', function(done) {
const callback = sinon.spy();
const apiCallback = naveggIdSubmodule.getId(NAVEGGID_CONFIG_COOKIE_HTML5).callback;

let id = naveggIdSubmodule.getId();
expect(id).to.be.deep.equal({id: 'nvgid-nvg0'})
})
ajaxStub.callsFake((url, successCallbacks, errorCallback, options) => {
if (successCallbacks && typeof successCallbacks === 'function') {
successCallbacks(JSON.stringify(MOCK_RESPONSE_NULL));
}
});
apiCallback(callback)
expect(callback.calledOnce).to.be.true;
expect(callback.calledWith(undefined)).to.be.true;
done();
});
});