forked from prebid/Prebid.js
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
GrowthCode ID Module: initial module release (prebid#9011)
* Initial check-in ofthe GrowthCode Adaptor * Growthcode ID System * Working on test module * Tests for the growthCode Id System * Clean up tests for GrowthCode * Fixed the default values for shareID * Remove Test HTML Page * Remove file * Updated MD file to switch client ID to partner ID
- Loading branch information
1 parent
5596c14
commit a1b2b48
Showing
4 changed files
with
303 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
/** | ||
* This module adds GrowthCodeId to the User ID module | ||
* The {@link module:modules/userId} module is required | ||
* @module modules/growthCodeIdSystem | ||
* @requires module:modules/userId | ||
*/ | ||
|
||
import {logError, logInfo, tryAppendQueryString} from '../src/utils.js'; | ||
import {ajax} from '../src/ajax.js'; | ||
import { submodule } from '../src/hook.js' | ||
import { getStorageManager } from '../src/storageManager.js'; | ||
|
||
const GCID_EXPIRY = 45; | ||
const MODULE_NAME = 'growthCodeId'; | ||
const GC_DATA_KEY = '_gc_data'; | ||
const ENDPOINT_URL = 'https://p2.gcprivacy.com/v1/pb?' | ||
|
||
export const storage = getStorageManager({ gvlid: undefined, moduleName: MODULE_NAME }); | ||
|
||
/** | ||
* Read GrowthCode data from cookie or local storage | ||
* @param key | ||
* @return {string} | ||
*/ | ||
export function readData(key) { | ||
try { | ||
if (storage.hasLocalStorage()) { | ||
return storage.getDataFromLocalStorage(key); | ||
} | ||
if (storage.cookiesAreEnabled()) { | ||
return storage.getCookie(key); | ||
} | ||
} catch (error) { | ||
logError(error); | ||
} | ||
} | ||
|
||
/** | ||
* Store GrowthCode data in either cookie or local storage | ||
* expiration date: 45 days | ||
* @param key | ||
* @param {string} value | ||
*/ | ||
function storeData(key, value) { | ||
try { | ||
logInfo(MODULE_NAME + ': storing data: key=' + key + ' value=' + value); | ||
|
||
if (value) { | ||
if (storage.hasLocalStorage()) { | ||
storage.setDataInLocalStorage(key, value); | ||
} | ||
const expiresStr = (new Date(Date.now() + (GCID_EXPIRY * (60 * 60 * 24 * 1000)))).toUTCString(); | ||
if (storage.cookiesAreEnabled()) { | ||
storage.setCookie(key, value, expiresStr, 'LAX'); | ||
} | ||
} | ||
} catch (error) { | ||
logError(error); | ||
} | ||
} | ||
|
||
/** | ||
* Parse json if possible, else return null | ||
* @param data | ||
* @param {object|null} | ||
*/ | ||
function tryParse(data) { | ||
try { | ||
return JSON.parse(data); | ||
} catch (err) { | ||
logError(err); | ||
return null; | ||
} | ||
} | ||
|
||
/** @type {Submodule} */ | ||
export const growthCodeIdSubmodule = { | ||
/** | ||
* used to link submodule with config | ||
* @type {string} | ||
*/ | ||
name: MODULE_NAME, | ||
/** | ||
* decode the stored id value for passing to bid requests | ||
* @function | ||
* @param {{string}} value | ||
* @returns {{growthCodeId: {string}}|undefined} | ||
*/ | ||
decode(value) { | ||
return value && value !== '' ? { 'growthCodeId': value } : undefined; | ||
}, | ||
/** | ||
* performs action to obtain id and return a value in the callback's response argument | ||
* @function | ||
* @param {SubmoduleConfig} [config] | ||
* @returns {IdResponse|undefined} | ||
*/ | ||
getId(config, consentData) { | ||
const configParams = (config && config.params) || {}; | ||
if (!configParams || typeof configParams.pid !== 'string') { | ||
logError('User ID - GrowthCodeID submodule requires a valid Partner ID to be defined'); | ||
return; | ||
} | ||
|
||
const gdpr = (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) ? 1 : 0; | ||
const consentString = gdpr ? consentData.consentString : ''; | ||
if (gdpr && !consentString) { | ||
logInfo('Consent string is required to call GrowthCode id.'); | ||
return; | ||
} | ||
|
||
let publisherId = configParams.publisher_id ? configParams.publisher_id : '_sharedID'; | ||
|
||
let sharedId; | ||
if (configParams.publisher_id_storage === 'html5') { | ||
sharedId = storage.getDataFromLocalStorage(publisherId, null) ? (storage.getDataFromLocalStorage(publisherId, null)) : null; | ||
} else { | ||
sharedId = storage.getCookie(publisherId, null) ? (storage.getCookie(publisherId, null)) : null; | ||
} | ||
if (!sharedId) { | ||
logError('User ID - Publisher ID is not correctly setup.'); | ||
} | ||
|
||
const resp = function(callback) { | ||
let gcData = tryParse(readData(GC_DATA_KEY)); | ||
if (gcData) { | ||
callback(gcData); | ||
} else { | ||
let segment = window.location.pathname.substr(1).replace(/\/+$/, ''); | ||
if (segment === '') { | ||
segment = 'home'; | ||
} | ||
|
||
let url = configParams.url ? configParams.url : ENDPOINT_URL; | ||
url = tryAppendQueryString(url, 'pid', configParams.pid); | ||
url = tryAppendQueryString(url, 'uid', sharedId); | ||
url = tryAppendQueryString(url, 'u', window.location.href); | ||
url = tryAppendQueryString(url, 'h', window.location.hostname); | ||
url = tryAppendQueryString(url, 's', segment); | ||
url = tryAppendQueryString(url, 'r', document.referrer); | ||
|
||
ajax(url, { | ||
success: response => { | ||
let respJson = tryParse(response); | ||
// If response is a valid json and should save is true | ||
if (respJson) { | ||
storeData(GC_DATA_KEY, JSON.stringify(respJson)) | ||
callback(respJson); | ||
} else { | ||
callback(); | ||
} | ||
}, | ||
error: error => { | ||
logError(MODULE_NAME + ': ID fetch encountered an error', error); | ||
callback(); | ||
} | ||
}, undefined, {method: 'GET', withCredentials: true}) | ||
} | ||
}; | ||
return { callback: resp }; | ||
} | ||
}; | ||
|
||
submodule('userId', growthCodeIdSubmodule); |
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,37 @@ | ||
## GrowthCode User ID Submodule | ||
|
||
GrowthCode provides Id Enrichment for requests. | ||
|
||
## Building Prebid with GrowthCode Support | ||
|
||
First, make sure to add the GrowthCode submodule to your Prebid.js package with: | ||
|
||
``` | ||
gulp build --modules=growthCodeIdSystem,userId | ||
``` | ||
|
||
The following configuration parameters are available: | ||
|
||
```javascript | ||
pbjs.setConfig({ | ||
userSync: { | ||
userIds: [{ | ||
name: 'growthCodeId', | ||
params: { | ||
pid: 'TEST01', // Set your Partner ID here for production (obtained from Growthcode) | ||
publisher_id: '_sharedID', | ||
publisher_id_storage: 'html5' | ||
} | ||
}] | ||
} | ||
}); | ||
``` | ||
|
||
| Param under userSync.userIds[] | Scope | Type | Description | Example | | ||
|--------------------------------|----------|--------| --- |-----------------| | ||
| name | Required | String | The name of this module. | `"growthCodeId"` | | ||
| params | Required | Object | Details of module params. | | | ||
| params.pid | Required | String | This is the Parter ID value obtained from GrowthCode | `"TEST01"` | | ||
| params.url | Optional | String | Custom URL for server | | | ||
| params.publisher_id | Optional | String | Name if the variable that holds your publisher ID | `"_sharedID"` | | ||
| params.publisher_id_storage | Optional | String | Publisher ID storage (cookie, html5) | `"html5"` | |
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,83 @@ | ||
import { growthCodeIdSubmodule } from 'modules/growthCodeIdSystem.js'; | ||
import * as utils from 'src/utils.js'; | ||
import { server } from 'test/mocks/xhr.js'; | ||
import { uspDataHandler } from 'src/adapterManager.js'; | ||
import {expect} from 'chai'; | ||
import {getStorageManager} from '../../../src/storageManager.js'; | ||
|
||
const GCID_EXPIRY = 45; | ||
const MODULE_NAME = 'growthCodeId'; | ||
const SHAREDID = 'fe9c5c89-7d56-4666-976d-e07e73b3b664'; | ||
|
||
export const storage = getStorageManager({ gvlid: undefined, moduleName: MODULE_NAME }); | ||
|
||
const getIdParams = {params: { | ||
pid: 'TEST01', | ||
publisher_id: '_sharedid', | ||
publisher_id_storage: 'html5', | ||
}}; | ||
|
||
describe('growthCodeIdSystem', () => { | ||
let logErrorStub; | ||
|
||
beforeEach(function () { | ||
logErrorStub = sinon.stub(utils, 'logError'); | ||
storage.setDataInLocalStorage('_sharedid', SHAREDID); | ||
const expiresStr = (new Date(Date.now() + (GCID_EXPIRY * (60 * 60 * 24 * 1000)))).toUTCString(); | ||
if (storage.cookiesAreEnabled()) { | ||
storage.setCookie('_sharedid', SHAREDID, expiresStr, 'LAX'); | ||
} | ||
}); | ||
|
||
afterEach(function () { | ||
logErrorStub.restore(); | ||
}); | ||
|
||
describe('name', () => { | ||
it('should expose the name of the submodule', () => { | ||
expect(growthCodeIdSubmodule.name).to.equal('growthCodeId'); | ||
}); | ||
}); | ||
|
||
it('should NOT call the growthcode id endpoint if gdpr applies but consent string is missing', function () { | ||
let submoduleCallback = growthCodeIdSubmodule.getId(getIdParams, { gdprApplies: true }, undefined); | ||
expect(submoduleCallback).to.be.undefined; | ||
}); | ||
|
||
it('should log an error if pid configParam was not passed when getId', function () { | ||
growthCodeIdSubmodule.getId(); | ||
expect(logErrorStub.callCount).to.be.equal(1); | ||
}); | ||
|
||
it('should log an error if sharedId (LocalStore) is not setup correctly', function () { | ||
growthCodeIdSubmodule.getId({params: { | ||
pid: 'TEST01', | ||
publisher_id: '_sharedid_bad', | ||
publisher_id_storage: 'html5', | ||
}}); | ||
expect(logErrorStub.callCount).to.be.equal(1); | ||
}); | ||
|
||
it('should log an error if sharedId (LocalStore) is not setup correctly', function () { | ||
growthCodeIdSubmodule.getId({params: { | ||
pid: 'TEST01', | ||
publisher_id: '_sharedid_bad', | ||
publisher_id_storage: 'cookie', | ||
}}); | ||
expect(logErrorStub.callCount).to.be.equal(1); | ||
}); | ||
|
||
it('should call the growthcode id endpoint', function () { | ||
let callBackSpy = sinon.spy(); | ||
let submoduleCallback = growthCodeIdSubmodule.getId(getIdParams).callback; | ||
submoduleCallback(callBackSpy); | ||
let request = server.requests[0]; | ||
expect(request.url.substr(0, 85)).to.be.eq('https://p2.gcprivacy.com/v1/pb?pid=TEST01&uid=' + SHAREDID + '&u='); | ||
request.respond( | ||
200, | ||
{}, | ||
JSON.stringify({}) | ||
); | ||
expect(callBackSpy.calledOnce).to.be.true; | ||
}); | ||
}) |