forked from prebid/Prebid.js
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
UtiqMtpIdSystem: create the Utiq MTP ID submodule (prebid#11372)
* Creating new user ID module utiqMtpIdSystem to provide option for publishers using Utiq to send the martechPass. This is complementary to the UtiqIDSystem module * fixing documentation to reflect correct name utiqMtp instead of utiq-mtp * fixing inline doc and creating unit test for utiqMtpIdSystem * Adding extra documentation to notify publishers to contact Utiq in order to integrate the Utiq script on their site --------- Co-authored-by: root <[email protected]> Co-authored-by: jdelhommeau <[email protected]>
- Loading branch information
1 parent
cbbd524
commit b557f04
Showing
4 changed files
with
349 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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: | ||
|
||
``` | ||
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"` | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
}); | ||
}); | ||
}); | ||
}); |