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

UtiqMtpIdSystem: create the Utiq MTP ID submodule #11372

Merged
merged 5 commits into from
Apr 25, 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
1 change: 1 addition & 0 deletions modules/.submodules.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"teadsIdSystem",
"tncIdSystem",
"utiqSystem",
"utiqMtpIdSystem",
"uid2IdSystem",
"euidIdSystem",
"unifiedIdSystem",
Expand Down
138 changes: 138 additions & 0 deletions modules/utiqMtpIdSystem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/**
* This module adds Utiq MTP provided by Utiq SA/NV to the User ID module
* The {@link module:modules/userId} module is required
* @module modules/utiqMtpIdSystem
* @requires module:modules/userId
*/
import { logInfo } from '../src/utils.js';
import { submodule } from '../src/hook.js';
import { getStorageManager } from '../src/storageManager.js';
import { MODULE_TYPE_UID } from '../src/activities/modules.js';

const MODULE_NAME = 'utiqMtpId';
const LOG_PREFIX = 'Utiq MTP module';

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

/**
* Get the "mtid" from html5 local storage to make it available to the UserId module.
* @param config
* @returns {{utiqMtp: (*|string)}}
*/
function getUtiqFromStorage() {
let utiqPass;
let utiqPassStorage = JSON.parse(
storage.getDataFromLocalStorage('utiqPass')
);
logInfo(
`${LOG_PREFIX}: Local storage utiqPass: ${JSON.stringify(
utiqPassStorage
)}`
);

if (
utiqPassStorage &&
utiqPassStorage.connectId &&
Array.isArray(utiqPassStorage.connectId.idGraph) &&
utiqPassStorage.connectId.idGraph.length > 0
) {
utiqPass = utiqPassStorage.connectId.idGraph[0];
}
logInfo(
`${LOG_PREFIX}: Graph of utiqPass: ${JSON.stringify(
utiqPass
)}`
);

return {
utiqMtp:
utiqPass && utiqPass.mtid
? utiqPass.mtid
: null,
};
}

/** @type {Submodule} */
export const utiqMtpIdSubmodule = {
/**
* Used to link submodule with config
* @type {string}
*/
name: MODULE_NAME,
/**
* Decodes the stored id value for passing to bid requests.
* @function
* @returns {{utiqMtp: string} | null}
*/
decode(bidId) {
logInfo(`${LOG_PREFIX}: Decoded ID value ${JSON.stringify(bidId)}`);
return bidId.utiqMtp ? bidId : null;
},
/**
* Get the id from helper function and initiate a new user sync.
* @param config
* @returns {{callback: result}|{id: {utiqMtp: string}}}
*/
getId: function (config) {
const data = getUtiqFromStorage();
if (data.utiqMtp) {
logInfo(`${LOG_PREFIX}: Local storage ID value ${JSON.stringify(data)}`);
return { id: { utiqMtp: data.utiqMtp } };
} else {
if (!config) {
config = {};
}
if (!config.params) {
config.params = {};
}
if (
typeof config.params.maxDelayTime === 'undefined' ||
config.params.maxDelayTime === null
) {
config.params.maxDelayTime = 1000;
}
// Current delay and delay step in milliseconds
let currentDelay = 0;
const delayStep = 50;
const result = (callback) => {
const data = getUtiqFromStorage();
if (!data.utiqMtp) {
if (currentDelay > config.params.maxDelayTime) {
logInfo(
`${LOG_PREFIX}: No utiq value set after ${config.params.maxDelayTime} max allowed delay time`
);
callback(null);
} else {
currentDelay += delayStep;
setTimeout(() => {
result(callback);
}, delayStep);
}
} else {
const dataToReturn = { utiqMtp: data.utiqMtp };
logInfo(
`${LOG_PREFIX}: Returning ID value data of ${JSON.stringify(
dataToReturn
)}`
);
callback(dataToReturn);
}
};
return { callback: result };
}
},
eids: {
'utiqMtp': {
source: 'utiq-mtp.com',
atype: 1,
getValue: function (data) {
return data;
},
},
}
};

submodule('userId', utiqMtpIdSubmodule);
22 changes: 22 additions & 0 deletions modules/utiqMtpIdSystem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## Utiq User ID Submodule

Utiq MTP ID Module.

### Utiq installation ###

In order to use utiq in your prebid setup, you must first integrate utiq solution on your website as per https://docs.utiq.com/
If you are interested in using Utiq on your website, please contact Utiq on https://utiq.com/contact/

### Prebid integration ###

First, make sure to add the utiq MTP submodule to your Prebid.js package with:
smenzer marked this conversation as resolved.
Show resolved Hide resolved

```
gulp build --modules=userId,adfBidAdapter,ixBidAdapter,prebidServerBidAdapter,utiqMtpIdSystem
```

## Parameter Descriptions

| Params under userSync.userIds[] | Type | Description | Example |
| ------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------ | -------------------------------- |
| name | String | The name of the module | `"utiqMtpId"` |
188 changes: 188 additions & 0 deletions test/spec/modules/utiqMtpIdSystem_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import { expect } from 'chai';
import { utiqMtpIdSubmodule } from 'modules/utiqMtpIdSystem.js';
import { storage } from 'modules/utiqMtpIdSystem.js';

describe('utiqMtpIdSystem', () => {
const utiqPassKey = 'utiqPass';

const getStorageData = (idGraph) => {
if (!idGraph) {
idGraph = {id: 501, domain: ''};
}
return {
'connectId': {
'idGraph': [idGraph],
}
}
};

it('should have the correct module name declared', () => {
expect(utiqMtpIdSubmodule.name).to.equal('utiqMtpId');
});

describe('utiqMtpId getId()', () => {
afterEach(() => {
storage.removeDataFromLocalStorage(utiqPassKey);
});

it('it should return object with key callback', () => {
expect(utiqMtpIdSubmodule.getId()).to.have.property('callback');
});

it('should return object with key callback with value type - function', () => {
storage.setDataInLocalStorage(utiqPassKey, JSON.stringify(getStorageData()));
expect(utiqMtpIdSubmodule.getId()).to.have.property('callback');
expect(typeof utiqMtpIdSubmodule.getId().callback).to.be.equal('function');
});

it('tests if localstorage & JSON works properly ', () => {
const idGraph = {
'domain': 'domainValue',
'mtid': 'mtidValue',
};
storage.setDataInLocalStorage(utiqPassKey, JSON.stringify(getStorageData(idGraph)));
expect(JSON.parse(storage.getDataFromLocalStorage(utiqPassKey))).to.have.property('connectId');
});

it('returns {id: {utiq: data.utiq}} if we have the right data stored in the localstorage ', () => {
const idGraph = {
'domain': 'test.domain',
'mtid': 'mtidValue',
};
storage.setDataInLocalStorage(utiqPassKey, JSON.stringify(getStorageData(idGraph)));
const response = utiqMtpIdSubmodule.getId();
expect(response).to.have.property('id');
expect(response.id).to.have.property('utiqMtp');
expect(response.id.utiqMtp).to.be.equal('mtidValue');
});

it('returns {utiqMtp: data.utiqMtp} if we have the right data stored in the localstorage right after the callback is called', (done) => {
const idGraph = {
'domain': 'test.domain',
'mtid': 'mtidValue',
};
const response = utiqMtpIdSubmodule.getId();
expect(response).to.have.property('callback');
expect(response.callback.toString()).contain('result(callback)');

if (typeof response.callback === 'function') {
storage.setDataInLocalStorage(utiqPassKey, JSON.stringify(getStorageData(idGraph)));
response.callback(function (result) {
expect(result).to.not.be.null;
expect(result).to.have.property('utiqMtp');
expect(result.utiqMtp).to.be.equal('mtidValue');
done()
})
}
});

it('returns {utiqMtp: data.utiqMtp} if we have the right data stored in the localstorage right after 500ms delay', (done) => {
const idGraph = {
'domain': 'test.domain',
'mtid': 'mtidValue',
};

const response = utiqMtpIdSubmodule.getId();
expect(response).to.have.property('callback');
expect(response.callback.toString()).contain('result(callback)');

if (typeof response.callback === 'function') {
setTimeout(() => {
storage.setDataInLocalStorage(utiqPassKey, JSON.stringify(getStorageData(idGraph)));
}, 500);
response.callback(function (result) {
expect(result).to.not.be.null;
expect(result).to.have.property('utiqMtp');
expect(result.utiqMtp).to.be.equal('mtidValue');
done()
})
}
});

it('returns null if we have the data stored in the localstorage after 500ms delay and the max (waiting) delay is only 200ms ', (done) => {
const idGraph = {
'domain': 'test.domain',
'mtid': 'mtidValue',
};

const response = utiqMtpIdSubmodule.getId({params: {maxDelayTime: 200}});
expect(response).to.have.property('callback');
expect(response.callback.toString()).contain('result(callback)');

if (typeof response.callback === 'function') {
setTimeout(() => {
storage.setDataInLocalStorage(utiqPassKey, JSON.stringify(getStorageData(idGraph)));
}, 500);
response.callback(function (result) {
expect(result).to.be.null;
done()
})
}
});
});

describe('utiq decode()', () => {
const VALID_API_RESPONSES = [
{
expected: '32a97f612',
payload: {
utiqMtp: '32a97f612'
}
},
{
expected: '32a97f61',
payload: {
utiqMtp: '32a97f61',
}
},
];
VALID_API_RESPONSES.forEach(responseData => {
it('should return a newly constructed object with the utiqMtp for a payload with {utiqMtp: value}', () => {
expect(utiqMtpIdSubmodule.decode(responseData.payload)).to.deep.equal(
{utiqMtp: responseData.expected}
);
});
});

[{}, '', {foo: 'bar'}].forEach((response) => {
it(`should return null for an invalid response "${JSON.stringify(response)}"`, () => {
expect(utiqMtpIdSubmodule.decode(response)).to.be.null;
});
});
});

describe('utiq messageHandler', () => {
afterEach(() => {
storage.removeDataFromLocalStorage(utiqPassKey);
});

const domains = [
'domain1',
'domain2',
'domain3',
];

domains.forEach(domain => {
it(`correctly sets utiq value for domain name ${domain}`, (done) => {
const idGraph = {
'domain': domain,
'mtid': 'mtidValue',
};

storage.setDataInLocalStorage(utiqPassKey, JSON.stringify(getStorageData(idGraph)));

const eventData = {
data: `{\"msgType\":\"MNOSELECTOR\",\"body\":{\"url\":\"https://${domain}/some/path\"}}`
};

window.dispatchEvent(new MessageEvent('message', eventData));

const response = utiqMtpIdSubmodule.getId();
expect(response).to.have.property('id');
expect(response.id).to.have.property('utiqMtp');
expect(response.id.utiqMtp).to.be.equal('mtidValue');
done();
});
});
});
});