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.
Criteo real time user sync (prebid#3930)
* add criteo rtus submodule and user id changes * update appnexus adapter to include criteo user id * updated to submodules pattern
- Loading branch information
1 parent
53f64e1
commit 66bb9fc
Showing
6 changed files
with
233 additions
and
2 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
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,105 @@ | ||
/** | ||
* This module adds Criteo Real Time User Sync to the User ID module | ||
* The {@link module:modules/userId} module is required | ||
* @module modules/criteortusIdSystem | ||
* @requires module:modules/userId | ||
*/ | ||
|
||
import * as utils from '../src/utils' | ||
import { ajax } from '../src/ajax'; | ||
import { submodule } from '../src/hook'; | ||
|
||
const key = '__pbjs_criteo_rtus'; | ||
|
||
/** @type {Submodule} */ | ||
export const criteortusIdSubmodule = { | ||
/** | ||
* used to link submodule with config | ||
* @type {string} | ||
*/ | ||
name: 'criteortus', | ||
/** | ||
* decode the stored id value for passing to bid requests | ||
* @function | ||
* @returns {{criteortus:Object}} | ||
*/ | ||
decode() { | ||
let uid = utils.getCookie(key); | ||
try { | ||
uid = JSON.parse(uid); | ||
return { 'criteortus': uid }; | ||
} catch (error) { | ||
utils.logError('Error in parsing criteo rtus data', error); | ||
} | ||
}, | ||
/** | ||
* performs action to obtain id and return a value in the callback's response argument | ||
* @function | ||
* @param {SubmoduleParams} [configParams] | ||
* @returns {function(callback:function)} | ||
*/ | ||
getId(configParams) { | ||
if (!configParams || !utils.isPlainObject(configParams.clientIdentifier)) { | ||
utils.logError('User ID - Criteo rtus requires client identifier to be defined'); | ||
return; | ||
} | ||
|
||
let uid = utils.getCookie(key); | ||
if (uid) { | ||
return uid; | ||
} else { | ||
let userIds = {}; | ||
return function(callback) { | ||
let bidders = Object.keys(configParams.clientIdentifier); | ||
|
||
function afterAllResponses() { | ||
// criteo rtus user id expires in 1 hour | ||
const expiresStr = (new Date(Date.now() + (60 * 60 * 1000))).toUTCString(); | ||
utils.setCookie(key, JSON.stringify(userIds), expiresStr); | ||
callback(userIds); | ||
} | ||
|
||
const onResponse = utils.delayExecution(afterAllResponses, bidders.length); | ||
|
||
bidders.forEach((bidder) => { | ||
let url = `https://gum.criteo.com/sync?c=${configParams.clientIdentifier[bidder]}&r=3`; | ||
const getSuccessHandler = (bidder) => { | ||
return function onSuccess(response) { | ||
if (response) { | ||
try { | ||
response = JSON.parse(response); | ||
userIds[bidder] = response; | ||
onResponse(); | ||
} catch (error) { | ||
utils.logError(error); | ||
} | ||
} | ||
} | ||
} | ||
|
||
const getFailureHandler = (bidder) => { | ||
return function onFailure(error) { | ||
utils.logError(`Criteo RTUS server call failed for ${bidder}`, error); | ||
onResponse(); | ||
} | ||
} | ||
|
||
ajax( | ||
url, | ||
{ | ||
success: getSuccessHandler(bidder), | ||
error: getFailureHandler(bidder) | ||
}, | ||
undefined, | ||
Object.assign({ | ||
method: 'GET', | ||
withCredentials: true | ||
}) | ||
); | ||
}) | ||
} | ||
} | ||
} | ||
}; | ||
|
||
submodule('userId', criteortusIdSubmodule); |
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
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,88 @@ | ||
import { criteortusIdSubmodule } from 'modules/criteortusIdSystem'; | ||
import * as utils from 'src/utils'; | ||
|
||
describe('Criteo RTUS', function() { | ||
let xhr; | ||
let requests; | ||
let getCookieStub; | ||
let logErrorStub; | ||
|
||
beforeEach(function () { | ||
xhr = sinon.useFakeXMLHttpRequest(); | ||
requests = []; | ||
xhr.onCreate = request => requests.push(request); | ||
getCookieStub = sinon.stub(utils, 'getCookie'); | ||
logErrorStub = sinon.stub(utils, 'logError'); | ||
}); | ||
|
||
afterEach(function () { | ||
xhr.restore(); | ||
getCookieStub.restore(); | ||
logErrorStub.restore(); | ||
}); | ||
|
||
it('should log error when configParams are not passed', function() { | ||
criteortusIdSubmodule.getId(); | ||
expect(logErrorStub.calledOnce).to.be.true; | ||
}) | ||
|
||
it('should call criteo endpoint to get user id', function() { | ||
getCookieStub.returns(null); | ||
let configParams = { | ||
clientIdentifier: { | ||
'sampleBidder': 1 | ||
} | ||
} | ||
|
||
let response = { 'status': 'ok', 'userid': 'sample-userid' } | ||
let callBackSpy = sinon.spy(); | ||
let submoduleCallback = criteortusIdSubmodule.getId(configParams); | ||
submoduleCallback(callBackSpy); | ||
requests[0].respond( | ||
200, | ||
{ 'Content-Type': 'text/plain' }, | ||
JSON.stringify(response) | ||
); | ||
expect(callBackSpy.calledOnce).to.be.true; | ||
expect(callBackSpy.calledWith({'sampleBidder': response})).to.be.true; | ||
}) | ||
|
||
it('should get uid from cookie and not call endpoint', function() { | ||
let response = {'appnexus': {'status': 'ok', 'userid': 'sample-userid'}} | ||
getCookieStub.returns(JSON.stringify(response)); | ||
let configParams = { | ||
clientIdentifier: { | ||
'sampleBidder': 1 | ||
} | ||
} | ||
let uid = criteortusIdSubmodule.getId(configParams); | ||
expect(requests.length).to.equal(0); | ||
}) | ||
|
||
it('should call criteo endpoint for multiple bidders', function() { | ||
getCookieStub.returns(null); | ||
let configParams = { | ||
clientIdentifier: { | ||
'sampleBidder': 1, | ||
'sampleBidder2': 2 | ||
} | ||
} | ||
|
||
let response = { 'status': 'ok', 'userid': 'sample-userid' } | ||
let callBackSpy = sinon.spy(); | ||
let submoduleCallback = criteortusIdSubmodule.getId(configParams); | ||
submoduleCallback(callBackSpy); | ||
requests[0].respond( | ||
200, | ||
{ 'Content-Type': 'text/plain' }, | ||
JSON.stringify(response) | ||
); | ||
expect(callBackSpy.calledOnce).to.be.false; | ||
requests[1].respond( | ||
200, | ||
{ 'Content-Type': 'text/plain' }, | ||
JSON.stringify(response) | ||
); | ||
expect(callBackSpy.calledOnce).to.be.true; | ||
}) | ||
}); |