Skip to content

Commit

Permalink
HadronId System : not use localStorage for writing on it (prebid#12378)
Browse files Browse the repository at this point in the history
* don't use localStorage for storing and minimize its use for reading

* don't use localStorage for storing and minimize its use for reading

* restoring value of hadronId in storage (doc)

* making tests pass

* making test pass
  • Loading branch information
jlaso authored Nov 5, 2024
1 parent 3f3fe13 commit e39d671
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 108 deletions.
35 changes: 20 additions & 15 deletions modules/hadronIdSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import { gdprDataHandler, uspDataHandler, gppDataHandler } from '../src/adapterM
* @typedef {import('../modules/userId/index.js').IdResponse} IdResponse
*/

const LOG_PREFIX = '[hadronIdSystem]';
const HADRONID_LOCAL_NAME = 'auHadronId';
const MODULE_NAME = 'hadronId';
export const MODULE_NAME = 'hadronId';
const LOG_PREFIX = `[${MODULE_NAME}System]`;
export const LS_TAM_KEY = 'auHadronId';
const AU_GVLID = 561;
const DEFAULT_HADRON_URL_ENDPOINT = 'https://id.hadron.ad.gt/api/v1/pbhid';

Expand Down Expand Up @@ -68,11 +68,9 @@ export const hadronIdSubmodule = {
* @returns {Object}
*/
decode(value) {
const hadronId = storage.getDataFromLocalStorage(HADRONID_LOCAL_NAME);
if (isStr(hadronId)) {
return {hadronId: hadronId};
return {
hadronId: isStr(value) ? value : value.hasOwnProperty('id') ? value.id[MODULE_NAME] : value[MODULE_NAME]
}
return (value && typeof value['hadronId'] === 'string') ? {'hadronId': value['hadronId']} : undefined;
},
/**
* performs action to obtain id and return a value in the callback's response argument
Expand All @@ -81,14 +79,19 @@ export const hadronIdSubmodule = {
* @returns {IdResponse|undefined}
*/
getId(config) {
logInfo(LOG_PREFIX, `getId is called`, config);
if (!isPlainObject(config.params)) {
config.params = {};
}
const partnerId = config.params.partnerId | 0;
let hadronId = storage.getDataFromLocalStorage(HADRONID_LOCAL_NAME);
if (isStr(hadronId)) {
return {id: {hadronId}};
let hadronId = '';
// at this point hadronId was not found by prebid, let check if it is in the webpage by other ways
hadronId = storage.getDataFromLocalStorage(LS_TAM_KEY);
if (isStr(hadronId) && hadronId.length > 0) {
logInfo(LOG_PREFIX, `${LS_TAM_KEY} found in localStorage = ${hadronId}`)
// return {callback: function(cb) { cb(hadronId) }};
return {id: hadronId}
}
const partnerId = config.params.partnerId | 0;
const resp = function (callback) {
let responseObj = {};
const callbacks = {
Expand All @@ -98,11 +101,13 @@ export const hadronIdSubmodule = {
responseObj = JSON.parse(response);
} catch (error) {
logError(error);
callback();
}
logInfo(LOG_PREFIX, `Response from backend is ${response}`, responseObj);
hadronId = responseObj['hadronId'];
storage.setDataInLocalStorage(HADRONID_LOCAL_NAME, hadronId);
responseObj = {id: {hadronId}};
if (isPlainObject(responseObj) && responseObj.hasOwnProperty(MODULE_NAME)) {
hadronId = responseObj[MODULE_NAME];
}
responseObj = hadronId; // {id: {hadronId: hadronId}};
}
callback(responseObj);
},
Expand Down Expand Up @@ -137,7 +142,7 @@ export const hadronIdSubmodule = {
url += `${gppConsent.applicableSections ? '&gpp_sid=' + encodeURIComponent(gppConsent.applicableSections) : ''}`;
}

logInfo(LOG_PREFIX, `hadronId not found in storage, calling home (${url})`);
logInfo(LOG_PREFIX, `${MODULE_NAME} not found, calling home (${url})`);

ajax(url, callbacks, undefined, {method: 'GET'});
};
Expand Down
21 changes: 10 additions & 11 deletions modules/hadronIdSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pbjs.setConfig({
userIds: [{
name: 'hadronId',
params: {
partnerId: 1234 // change it to the Partner ID you'll get from Audigent
partnerId: 1234 // change it to the Partner ID you got from Audigent
},
storage: {
name: 'hadronId',
Expand All @@ -25,14 +25,13 @@ pbjs.setConfig({
## Parameter Descriptions for the `usersync` Configuration Section
The below parameters apply only to the HadronID User ID Module integration.

| Param under usersync.userIds[] | Scope | Type | Description | Example |
|--------------------------------|----------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------|
| name | Required | String | ID value for the HadronID module - `"hadronId"` | `"hadronId"` |
| storage | Required | Object | The publisher must specify the local storage in which to store the results of the call to get the user ID. This can be either cookie or HTML5 storage. | |
| storage.type | Required | String | This is where the results of the user ID will be stored. The recommended method is `localStorage` by specifying `html5`. | `"html5"` |
| storage.name | Required | String | The name of the cookie or html5 local storage where the user ID will be stored. | `"hadronid"` |
| storage.expires | Optional | Integer | How long (in days) the user ID information will be stored. | `365` |
| value | Optional | Object | Used only if the page has a separate mechanism for storing the Hadron ID. The value is an object containing the values to be sent to the adapters. In this scenario, no URL is called and nothing is added to local storage | `{"hadronId": "eb33b0cb-8d35-4722-b9c0-1a31d4064888"}` |
| Param under usersync.userIds[] | Scope | Type | Description | Example |
|--------------------------------|----------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|
| name | Required | String | ID value for the HadronID module - `"hadronId"` | `"hadronId"` |
| storage | Required | Object | The publisher must specify the local storage in which to store the results of the call to get the user ID. This can be either cookie or HTML5 storage. | |
| storage.type | Required | String | This is where the the user ID will be stored. The recommended method is `localStorage` by specifying `html5`. | `"html5"` |
| storage.name | Required | String | The name of the cookie or html5 local storage where the user ID will be stored. The recommended value is `hadronId`. | `"auHadronId"` |
| storage.expires | Optional | Integer | How long (in days) the user ID information will be stored. The recommended value is 14 days. | `14` |
| value | Optional | Object | Used only if the page has a separate mechanism for storing the Hadron ID. The value is an object containing the values to be sent to the adapters. In this scenario, no URL is called and nothing is added to local storage | `{"hadronId": "0aRSTUAackg79ijgd8e8j6kah9ed9j6hdfgb6cl00volopxo00npzjmmb"}` |
| params | Optional | Object | Used to store params for the id system |
| params.partnerId | Required | Number | This is the Audigent Partner ID obtained from Audigent. | `1234` |
|
| params.partnerId | Required | Number | This is the Audigent Partner ID obtained from Audigent. | `1234` |
45 changes: 30 additions & 15 deletions modules/hadronRtdProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ import {config} from '../src/config.js';
import {getGlobal} from '../src/prebidGlobal.js';
import {getStorageManager} from '../src/storageManager.js';
import {submodule} from '../src/hook.js';
import {isFn, isStr, isArray, deepEqual, isPlainObject, logError, logInfo} from '../src/utils.js';
import {isFn, isStr, isArray, isEmpty, deepEqual, isPlainObject, logError, logInfo} from '../src/utils.js';
import {loadExternalScript} from '../src/adloader.js';
import {MODULE_TYPE_RTD} from '../src/activities/modules.js';

/**
* @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule
*/

const LOG_PREFIX = 'User ID - HadronRtdProvider submodule: ';
const LOG_PREFIX = '[HadronRtdProvider] ';
const MODULE_NAME = 'realTimeData';
const SUBMODULE_NAME = 'hadron';
const AU_GVLID = 561;
const HADRON_ID_DEFAULT_URL = 'https://id.hadron.ad.gt/api/v1/hadronid?_it=prebid';
const HADRON_SEGMENT_URL = 'https://id.hadron.ad.gt/api/v1/rtd';
export const HADRONID_LOCAL_NAME = 'auHadronId';
export const RTD_LOCAL_NAME = 'auHadronRtd';
const HADRON_SEGMENT_URL = 'https://prebid-rtd.audigent.workers.dev'; // https://id.hadron.ad.gt/api/v1/rtd';
const LS_TAM_KEY = 'auHadronId';
const RTD_LOCAL_NAME = 'auHadronRtd';
export const storage = getStorageManager({moduleType: MODULE_TYPE_RTD, moduleName: SUBMODULE_NAME});

/**
Expand Down Expand Up @@ -166,14 +166,29 @@ export function getRealTimeData(bidConfig, onDone, rtdConfig, userConsent) {

const userIds = {};

let hadronId = storage.getDataFromLocalStorage(HADRONID_LOCAL_NAME);
if (isStr(hadronId)) {
if (typeof getGlobal().refreshUserIds === 'function') {
(getGlobal()).refreshUserIds({submoduleNames: 'hadronId'});
const allUserIds = getGlobal().getUserIds();
if (allUserIds.hasOwnProperty('hadronId')) {
userIds['hadronId'] = allUserIds.hadronId;
logInfo(LOG_PREFIX, 'hadronId user module found', allUserIds.hadronId);
} else {
let hadronId = storage.getDataFromLocalStorage(LS_TAM_KEY);
if (isStr(hadronId) && hadronId.length > 0) {
userIds['hadronId'] = hadronId;
logInfo(LOG_PREFIX, 'hadronId TAM found', hadronId);
}
userIds.hadronId = hadronId;
}
if (!isEmpty(userIds)) {
// if (typeof getGlobal().refreshUserIds === 'function') {
// (getGlobal()).refreshUserIds({submoduleNames: 'hadronId'});
// }
// userIds.hadronId = hadronId;
getRealTimeDataAsync(bidConfig, onDone, rtdConfig, userConsent, userIds);
} else {
// the hadronId was not found, reasons can be:
// 1) prebid wasn't compiled with hadronIdSystem
// 2) prebid wasn't configured to use hadronId user module
// 3) all previous and no other hadronId snippet configured in the page
// then need to load hadron.js from the CDN
window.pubHadronCb = (hadronId) => {
userIds.hadronId = hadronId;
getRealTimeDataAsync(bidConfig, onDone, rtdConfig, userConsent, userIds);
Expand All @@ -184,8 +199,8 @@ export function getRealTimeData(bidConfig, onDone, rtdConfig, userConsent) {
paramOrDefault(hadronIdUrl, HADRON_ID_DEFAULT_URL, userIds),
`partner_id=${partnerId}&_it=prebid`
);
loadExternalScript(scriptUrl, MODULE_TYPE_RTD, 'hadron', () => {
logInfo(LOG_PREFIX, 'hadronIdTag loaded', scriptUrl);
loadExternalScript(scriptUrl, SUBMODULE_NAME, () => {
logInfo(LOG_PREFIX, 'hadronId JS snippet loaded', scriptUrl);
})
}
}
Expand All @@ -198,7 +213,7 @@ export function getRealTimeData(bidConfig, onDone, rtdConfig, userConsent) {
* @param {Object} userConsent
* @param {Object} userIds
*/
export function getRealTimeDataAsync(bidConfig, onDone, rtdConfig, userConsent, userIds) {
function getRealTimeDataAsync(bidConfig, onDone, rtdConfig, userConsent, userIds) {
let reqParams = {};

if (isPlainObject(rtdConfig)) {
Expand All @@ -223,7 +238,7 @@ export function getRealTimeDataAsync(bidConfig, onDone, rtdConfig, userConsent,
onDone();
}
} catch (err) {
logError('unable to parse audigent segment data');
logError(LOG_PREFIX, 'unable to parse audigent segment data');
onDone();
}
} else if (req.status === 204) {
Expand All @@ -233,7 +248,7 @@ export function getRealTimeDataAsync(bidConfig, onDone, rtdConfig, userConsent,
},
error: function () {
onDone();
logError('unable to get audigent segment data');
logError(LOG_PREFIX, 'unable to get audigent segment data');
}
},
JSON.stringify({'userIds': userIds, 'config': reqParams}),
Expand Down
37 changes: 11 additions & 26 deletions test/spec/modules/hadronIdSystem_spec.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,51 @@
import { hadronIdSubmodule, storage } from 'modules/hadronIdSystem.js';
import { server } from 'test/mocks/xhr.js';
import * as utils from 'src/utils.js';
import {hadronIdSubmodule, storage, LS_TAM_KEY} from 'modules/hadronIdSystem.js';
import {server} from 'test/mocks/xhr.js';
import {attachIdSystem} from '../../../modules/userId/index.js';
import {createEidsArray} from '../../../modules/userId/eids.js';
import {expect} from 'chai/index.mjs';

describe('HadronIdSystem', function () {
describe('getId', function() {
const HADRON_TEST = 'tstCachedHadronId1';
describe('getId', function () {
let getDataFromLocalStorageStub;

beforeEach(function() {
beforeEach(function () {
getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage');
});

afterEach(function () {
getDataFromLocalStorageStub.restore();
});

it('gets a hadronId', function() {
it('gets a cached hadronid', function () {
const config = {
params: {}
};
const callbackSpy = sinon.spy();
const callback = hadronIdSubmodule.getId(config).callback;
callback(callbackSpy);
const request = server.requests[0];
expect(request.url).to.match(/^https:\/\/id\.hadron\.ad\.gt\/api\/v1\/pbhid/);
request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ hadronId: 'testHadronId1' }));
expect(callbackSpy.lastCall.lastArg).to.deep.equal({ id: { hadronId: 'testHadronId1' } });
});

it('gets a cached hadronid', function() {
const config = {
params: {}
};
getDataFromLocalStorageStub.withArgs('auHadronId').returns('tstCachedHadronId1');

getDataFromLocalStorageStub.withArgs(LS_TAM_KEY).returns(HADRON_TEST);
const result = hadronIdSubmodule.getId(config);
expect(result).to.deep.equal({ id: { hadronId: 'tstCachedHadronId1' } });
expect(result).to.deep.equal({id: HADRON_TEST});
});

it('allows configurable id url', function() {
it('allows configurable id url', function () {
const config = {
params: {
url: 'https://hadronid.publync.com'
}
};
getDataFromLocalStorageStub.withArgs(LS_TAM_KEY).returns(null);
const callbackSpy = sinon.spy();
const callback = hadronIdSubmodule.getId(config).callback;
callback(callbackSpy);
const request = server.requests[0];
expect(request.url).to.match(/^https:\/\/hadronid\.publync\.com\//);
request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ hadronId: 'testHadronId1' }));
expect(callbackSpy.lastCall.lastArg).to.deep.equal({ id: { hadronId: 'testHadronId1' } });
});
});

describe('eids', () => {
before(() => {
attachIdSystem(hadronIdSubmodule);
});
it('hadronId', function() {
it('hadronId', function () {
const userId = {
hadronId: 'some-random-id-value'
};
Expand Down
Loading

0 comments on commit e39d671

Please sign in to comment.