@segment/analytics.js-integration-fullstory
Advanced tools
Comparing version 2.1.4 to 2.2.0
117
lib/index.js
@@ -10,3 +10,2 @@ 'use strict'; | ||
var integration = require('@segment/analytics.js-integration'); | ||
var is = require('is'); | ||
@@ -19,11 +18,17 @@ /** | ||
var FullStory = module.exports = integration('FullStory') | ||
var FullStory = (module.exports = integration('FullStory') | ||
.option('org', '') | ||
.option('debug', false) | ||
.tag('<script src="https://www.fullstory.com/s/fs.js"></script>'); | ||
.tag('<script src="https://www.fullstory.com/s/fs.js"></script>')); | ||
/** | ||
* The ApiSource string. | ||
* | ||
* @type {string} | ||
*/ | ||
var apiSource = 'segment'; | ||
/** | ||
* Initialize. | ||
*/ | ||
FullStory.prototype.initialize = function() { | ||
@@ -38,6 +43,6 @@ window._fs_debug = this.options.debug; | ||
if (e in m) {if(m.console && m.console.log) { m.console.log('FullStory namespace conflict. Please set window["_fs_namespace"].');} return;} | ||
g=m[e]=function(a,b){g.q?g.q.push([a,b]):g._api(a,b);};g.q=[]; | ||
g.identify=function(i,v){g(l,{uid:i});if(v)g(l,v)};g.setUserVars=function(v){g(l,v)}; | ||
y="rec";g.shutdown=function(i,v){g(y,!1)};g.restart=function(i,v){g(y,!0)}; | ||
y="consent";g[y]=function(a){g(y,!arguments.length||a)}; | ||
g=m[e]=function(a,b,s){g.q?g.q.push([a,b,s]):g._api(a,b,s);};g.q=[]; | ||
g.identify=function(i,v,s){g(l,{uid:i},s);if(v)g(l,v,s)};g.setUserVars=function(v,s){g(l,v,s)};g.event=function(i,v,s){g('event',{n:i,p:v},s)}; | ||
g.shutdown=function(){g("rec",!1)};g.restart=function(){g("rec",!0)}; | ||
g.consent=function(a){g("consent",!arguments.length||a)}; | ||
g.identifyAccount=function(i,v){o='account';v=v||{};v.acctId=i;g(o,v)}; | ||
@@ -56,3 +61,2 @@ g.clearUserCookie=function(){}; | ||
*/ | ||
FullStory.prototype.loaded = function() { | ||
@@ -70,18 +74,22 @@ return !!window.FS; | ||
*/ | ||
FullStory.prototype.identify = function(identify) { | ||
var traits = identify.traits({ name: 'displayName' }); | ||
var newTraits = foldl(function(results, value, key) { | ||
if (value !== null && typeof value === 'object' && value.constructor !== Date) { | ||
var newTraits = foldl( | ||
function(results, value, key) { | ||
if (key !== 'id') { | ||
results[ | ||
key === 'displayName' || key === 'email' ? key : camelCaseField(key) | ||
] = value; | ||
} | ||
return results; | ||
} | ||
if (key !== 'id') results[key === 'displayName' || key === 'email' ? key : convert(key, value)] = value; | ||
return results; | ||
}, {}, traits); | ||
}, | ||
{}, | ||
traits | ||
); | ||
if (identify.userId()) { | ||
window.FS.identify(String(identify.userId()), newTraits); | ||
window.FS.identify(String(identify.userId()), newTraits, apiSource); | ||
} else { | ||
newTraits.segmentAnonymousId_str = String(identify.anonymousId()); | ||
window.FS.setUserVars(newTraits); | ||
window.FS.setUserVars(newTraits, apiSource); | ||
} | ||
@@ -91,46 +99,41 @@ }; | ||
/** | ||
* Convert to FullStory format. | ||
* | ||
* @param {string} trait | ||
* @param {*} value | ||
*/ | ||
* Track. Passes the events directly to FullStory via FS.event API. | ||
* | ||
* @param {Track} track | ||
*/ | ||
FullStory.prototype.track = function(track) { | ||
window.FS.event(track.event(), track.properties(), apiSource); | ||
}; | ||
function convert(key, value) { | ||
// Handle already-tagged keys without changing it. This means both that a | ||
// user passing avg_spend_real *gets* avg_spend_real not avg_spend_real_real, | ||
// AND that they get avg_spend_real even if the value happens to be a perfect | ||
// integer. (Or a string, although FullStory will flag that as an error.) | ||
var parts = key.split('_'); | ||
/** | ||
* Camel cases `.`, `-`, `_`, and white space within fieldNames. Leaves type suffix alone. | ||
* | ||
* NOTE: Does not fix otherwise malformed fieldNames. | ||
* FullStory will scrub characters from keys that do not conform to /^[a-zA-Z][a-zA-Z0-9_]*$/. | ||
* | ||
* @param {string} fieldName | ||
*/ | ||
function camelCaseField(fieldName) { | ||
// Do not camel case across type suffixes. | ||
var parts = fieldName.split('_'); | ||
if (parts.length > 1) { | ||
// If we have an underbar, we have at least 2 parts; check the last as a tag | ||
var tag = parts.pop(); | ||
if (tag === 'str' || tag === 'int' || tag === 'date' || tag === 'real' || tag === 'bool') { | ||
return camel(parts.join('_')) + '_' + tag; | ||
var typeSuffix = parts.pop(); | ||
switch (typeSuffix) { | ||
case 'str': | ||
case 'int': | ||
case 'date': | ||
case 'real': | ||
case 'bool': | ||
case 'strs': | ||
case 'ints': | ||
case 'dates': | ||
case 'reals': | ||
case 'bools': | ||
return camel(parts.join('_')) + '_' + typeSuffix; | ||
default: // passthrough | ||
} | ||
} | ||
// No tag found, try to infer one from the value. | ||
key = camel(key); | ||
if (is.string(value)) return key + '_str'; | ||
if (isInt(value)) return key + '_int'; | ||
if (isFloat(value)) return key + '_real'; | ||
if (is.date(value)) return key + '_date'; | ||
if (is.bool(value)) return key + '_bool'; | ||
return key; // Bad FullStory type, but don't mess with the key so error messages name it | ||
// No type suffix found. Camel case the whole field name. | ||
return camel(fieldName); | ||
} | ||
/** | ||
* Check if n is a float. | ||
*/ | ||
function isFloat(n) { | ||
return Number(n) === n && n % 1 !== 0; | ||
} | ||
/** | ||
* Check if n is an integer. | ||
*/ | ||
function isInt(n) { | ||
return Number(n) === n && n % 1 === 0; | ||
} |
{ | ||
"name": "@segment/analytics.js-integration-fullstory", | ||
"description": "The Fullstory analytics.js integration.", | ||
"version": "2.1.4", | ||
"version": "2.2.0", | ||
"keywords": [ | ||
@@ -15,12 +15,12 @@ "analytics.js", | ||
}, | ||
"author": "Segment \u003cfriends@segment.com\u003e", | ||
"license": "SEE LICENSE IN LICENSE", | ||
"homepage": "https://github.com/segmentio/analytics.js-integrations/blob/master/integrations/fullstory#readme", | ||
"bugs": { | ||
"url": "https://github.com/segmentio/analytics.js-integrations/issues" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/segment-integrations/analytics.js-integration-fullstory.git" | ||
"url": "git+https://github.com/segmentio/analytics.js-integrations.git" | ||
}, | ||
"author": "Segment <friends@segment.com>", | ||
"license": "SEE LICENSE IN LICENSE", | ||
"bugs": { | ||
"url": "https://github.com/segment-integrations/analytics.js-integration-fullstory/issues" | ||
}, | ||
"homepage": "https://github.com/segment-integrations/analytics.js-integration-fullstory#readme", | ||
"dependencies": { | ||
@@ -36,8 +36,4 @@ "@ndhoule/foldl": "^2.0.1", | ||
"@segment/clear-env": "^2.0.0", | ||
"@segment/eslint-config": "^3.1.1", | ||
"browserify": "^13.0.0", | ||
"browserify-istanbul": "^2.0.0", | ||
"eslint": "^2.9.0", | ||
"eslint-plugin-mocha": "^2.2.0", | ||
"eslint-plugin-require-path-exists": "^1.1.5", | ||
"istanbul": "^0.4.3", | ||
@@ -58,2 +54,2 @@ "karma": "1.3.0", | ||
} | ||
} | ||
} |
@@ -81,3 +81,3 @@ 'use strict'; | ||
analytics.identify(1); | ||
analytics.called(window.FS.identify, '1'); | ||
analytics.called(window.FS.identify, '1', {}, 'segment'); | ||
analytics.didNotCall(window.FS.setUserVars); | ||
@@ -91,8 +91,8 @@ var traits = window.FS.identify.args[0][0]; | ||
analytics.identify('id'); | ||
analytics.called(window.FS.identify, 'id'); | ||
analytics.called(window.FS.identify, 'id', {}, 'segment'); | ||
}); | ||
it('should camel case custom props', function() { | ||
analytics.identify('id', { name: 'Abc123', email: 'example@pizza.com', 'First Name': 'Steven' }); | ||
analytics.called(window.FS.identify, 'id', { displayName: 'Abc123', email: 'example@pizza.com', firstName_str: 'Steven' }); | ||
analytics.identify('id', { name: 'Abc123', email: 'example@pizza.com', 'First name': 'Steven', lastName: 'Brown' }); | ||
analytics.called(window.FS.identify, 'id', { displayName: 'Abc123', email: 'example@pizza.com', firstName: 'Steven', lastName: 'Brown' }, 'segment'); | ||
}); | ||
@@ -102,39 +102,19 @@ | ||
analytics.identify('id', { name: 'Test', email: 'test@test.com' }); | ||
analytics.called(window.FS.identify, 'id', { displayName: 'Test', email: 'test@test.com' }); | ||
analytics.called(window.FS.identify, 'id', { displayName: 'Test', email: 'test@test.com' }, 'segment'); | ||
}); | ||
it('should map integers properly', function() { | ||
// JavaScript only has Number, so 3.0 === 3 and is an "int" | ||
analytics.identify('id', { name: 'Test', revenue: 7, number: 3.0 }); | ||
analytics.called(window.FS.identify, 'id', { displayName: 'Test', revenue_int: 7, number_int: 3 }); | ||
}); | ||
it('should map floats properly', function() { | ||
analytics.identify('id1', { name: 'Example', amtAbandonedInCart: 3.84 }); | ||
analytics.called(window.FS.identify, 'id1', { displayName: 'Example', amtAbandonedInCart_real: 3.84 }); | ||
}); | ||
it('should map dates properly', function() { | ||
analytics.identify('id2', { name: 'Test123', signupDate: new Date('2014-03-11T13:19:23Z') }); | ||
analytics.called(window.FS.identify, 'id2', { displayName: 'Test123', signupDate_date: new Date('2014-03-11T13:19:23Z') }); | ||
}); | ||
it('should map booleans properly', function() { | ||
analytics.identify('id3', { name: 'Steven', registered: true }); | ||
analytics.called(window.FS.identify, 'id3', { displayName: 'Steven', registered_bool: true }); | ||
}); | ||
it('should skip arrays entirely', function() { | ||
analytics.identify('id3', { ok: 'string', teams: ['eng', 'redsox'] }); | ||
analytics.called(window.FS.identify, 'id3', { ok_str: 'string' }); | ||
}); | ||
it('should skip user objects entirely', function() { | ||
analytics.identify('id3', { ok: 7, account: { level: 'premier', avg_annual: 30000 } }); | ||
analytics.called(window.FS.identify, 'id3', { ok_int: 7 }); | ||
}); | ||
it('should respect existing type tags', function() { | ||
analytics.identify('id3', { my_real: 17, my_int_str: 17, my_str_int: 'foo', my_int_date: 3, | ||
my_int_bool: 4, mystr_real: 'plugh' }); | ||
analytics.identify('id3', { | ||
my_real: 17, | ||
my_int_str: 17, | ||
my_str_int: 'foo', | ||
my_int_date: 3, | ||
my_int_bool: 4, | ||
mystr_real: 'plugh', | ||
my_reals: [17], | ||
my_int_strs: [17], | ||
my_str_ints: ['foo'], | ||
my_int_dates: [3], | ||
my_int_bools: [4], | ||
mystr_reals: ['plugh'] }); | ||
analytics.called(window.FS.identify, 'id3', { | ||
@@ -146,6 +126,23 @@ my_real: 17, // didn't become my_real_real (double tag) | ||
myInt_bool: 4, | ||
mystr_real: 'plugh' }); | ||
mystr_real: 'plugh', | ||
my_reals: [17], | ||
myInt_strs: [17], | ||
myStr_ints: ['foo'], | ||
myInt_dates: [3], | ||
myInt_bools: [4], | ||
mystr_reals: ['plugh'] }, 'segment'); | ||
}); | ||
}); | ||
describe('#track', function() { | ||
beforeEach(function() { | ||
analytics.stub(window.FS, 'event'); | ||
}); | ||
it('should send track event name and properties', function() { | ||
analytics.track('foo', { some_field: 'field_value' }); | ||
analytics.called(window.FS.event, 'foo', { some_field: 'field_value' }, 'segment'); | ||
}); | ||
}); | ||
}); | ||
}); |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
19
0
12537
6
245
2