Skip to content

Instantly share code, notes, and snippets.

@davidsertillange-optimizely
Last active October 25, 2020 23:39
Show Gist options
  • Save davidsertillange-optimizely/4a2d9a04e9029f8fbb0ae8b36810ed31 to your computer and use it in GitHub Desktop.
Save davidsertillange-optimizely/4a2d9a04e9029f8fbb0ae8b36810ed31 to your computer and use it in GitHub Desktop.
Mirroring Web events in Full Stack
/*
How to use:
Step 1: line 20-26, enter account_id, project_id, Full Stack user_id, environmentKey, and updateDatafile
Step 2: make sure your Full Stack project has the events you want to mirror created.
The API names need to match between the Web and Full Stack events.
Example: on Web, i have a click event "Homepage clicks" with API name "123456790_homepage_clicks"
In the Full Stack project, a custom event with the same name "123456790_homepage_clicks" needs to be created for this script to work
Step 3: add the script to Project Javascript
Step 4: load your page and check the network tab for events going to /events with "client_name": "Optimizely/fswebintegration"
The script attaches to every Web event (pageviews, clicks or custom) and dispatches an additional
network request with the payload for Full Stack
It supports value and revenue metrics
It also mirror custom attributes from Web to Full Stack
*/
window.FSWebIntegrator = (function() {
return {
account_id: "", // Optimizely Account ID
project_id:"", // project ID where the full stack events need to be dispatched to
user_id:"", // add the Full Stack UserID
endpoint:"https://logx.optimizely.com/v1/events",
method:"POST",
environmentKey:"", // Full Stack SDK Key
updateDatafile:86400 // how often the datafile should be updated (in ms)
};
});
window.FSWebIntegrator.getCookie = function(cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for(var i = 0; i <ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return null;
};
// Send Full Stack by mirroring Web event
window.FSWebIntegrator.sendFullStackEvent = function(event) {
// Parse datafile and get event ID-name map
var datafile = JSON.parse(localStorage.getItem('OptlyDatafile') || '{}');
var eventMap;
if(datafile.hasOwnProperty('events')) {
eventMap = datafile.events.map(function(DatafileEvent) {
return {
id:DatafileEvent.id,
key:DatafileEvent.key
};
});
}
buildEventPayload(event, eventMap, window.FSWebIntegrator().user_id);
dispatchNetworkEvent();
function findEntityId(event_name, eventMap) {
return eventMap.filter(function(event) {
if(event_name === event.key) {
return true;
}
}).map(function(event) {
return event.id;
}).join();
}
function dispatchNetworkEvent() {
var Http = new XMLHttpRequest();
Http.open(FSWebIntegrator().method, FSWebIntegrator().endpoint);
Http.send(JSON.stringify(this.body));
Http.onreadystatechange = function(e) {
};
// todo: handle event failure
Http.onerror = function(e) {
console.log(e);
};
}
function buildEventPayload(event, eventMap, user_id) {
// We copy the web attributes
if(optimizely.get('visitor').hasOwnProperty('custom')) {
var attributes = Object.entries(optimizely.get('visitor').custom).map(function(entry) {
return {
entity_id: null, // entity_id set to null as web snippet doesn't expose attribute ID
key: entry[0],
type:"custom",
value: entry[1].value
}
})
}
// Support for bot filtering
var attributes = attributes || [];
attributes.push(
{
entity_id: "$opt_bot_filtering",
key: "$opt_bot_filtering",
type:"custom",
value: false
}
);
this.body = {
"account_id": FSWebIntegrator().account_id,
"project_id":FSWebIntegrator().project_id,
"revision":"100",
"visitors":
[
{
"visitor_id":FSWebIntegrator().user_id,
"attributes": attributes,
"snapshots": [
{
"decisions": [],
"events": [
{
"entity_id": findEntityId(event.data.name, eventMap),
"key": event.data.name,
"timestamp": Date.now(),
"uuid":Date.now().toString(),
"revenue":event.data.metrics.hasOwnProperty("revenue") ? event.data.metrics.revenue : 0,
"value":event.data.tags.hasOwnProperty("value") ? event.data.tags.value : 0
}
]
}
]
}
],
"anonymize_ip": true,
"client_name": "Optimizely/fswebintegration",
"client_version": "1.0.0",
"enrich_decisions": true
};
return this;
}
};
window.FSWebIntegrator.onActivated = function() {
// Full Stack integration check for datafile existence + last fetch
var lastFetch = localStorage.getItem('OptlyDatafileFetch') || 0;
if(Date.now() - lastFetch > FSWebIntegrator().updateDatafile) // if datafile is older defined interval
{
// Get datafile to map entity_id
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
// Typical action to be performed when the document is ready:
var datafile = JSON.parse(xhttp.responseText);
if(datafile.hasOwnProperty('events')) {
localStorage.setItem('OptlyDatafile', JSON.stringify(datafile));
localStorage.setItem('OptlyDatafileFetch', Date.now());
}
}
};
xhttp.open("GET", "https://cdn.optimizely.com/datafiles/"+FSWebIntegrator().environmentKey+".json", true);
xhttp.send();
}
// Future Web integration / we map the events so we can track without the Web snippet (future feature)
var optimizelyData = optimizely.get('data');
if(optimizelyData.hasOwnProperty('events') && optimizelyData.hasOwnProperty('pages')) {
localStorage.setItem('OptlyWebData', JSON.stringify(Object.assign(optimizelyData.events,optimizelyData.pages)));
}
}
// Initialize the integration
window["optimizely"] = window["optimizely"] || [];
window["optimizely"].push({
type: "addListener",
filter: {
type: "lifecycle",
name: "activated"
},
handler: window.FSWebIntegrator.onActivated
});
// For every webevent, dispatch it to FullStack
window["optimizely"] = window["optimizely"] || [];
window["optimizely"].push({
type: "addListener",
filter: {
type: "analytics",
name: "trackEvent"
},
handler: window.FSWebIntegrator.sendFullStackEvent
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment