Skip to content

Commit

Permalink
Namespace local storage keys with api key (#132)
Browse files Browse the repository at this point in the history
* Namespace local storage keys with api key

Some weird sites(eg optimizely's visual editor) pollute local storage by
acting as a proxy to other sites. These changes should prevent different
domains from contaminating each other.
  • Loading branch information
blazzy authored Jan 18, 2018
1 parent 5985deb commit 2e0e81b
Show file tree
Hide file tree
Showing 9 changed files with 239 additions and 112 deletions.
94 changes: 69 additions & 25 deletions amplitude.js
Original file line number Diff line number Diff line change
Expand Up @@ -5673,7 +5673,7 @@ var DEFAULT_OPTIONS = {
*/
var AmplitudeClient = function AmplitudeClient(instanceName) {
this._instanceName = utils.isEmptyString(instanceName) ? constants.DEFAULT_INSTANCE : instanceName.toLowerCase();
this._storageSuffix = this._instanceName === constants.DEFAULT_INSTANCE ? '' : '_' + this._instanceName;
this._legacyStorageSuffix = this._instanceName === constants.DEFAULT_INSTANCE ? '' : '_' + this._instanceName;
this._unsentEvents = [];
this._unsentIdentifys = [];
this._ua = new uaParser(navigator.userAgent).getResult();
Expand Down Expand Up @@ -5716,6 +5716,7 @@ AmplitudeClient.prototype.init = function init(apiKey, opt_userId, opt_config, o

try {
this.options.apiKey = apiKey;
this._storageSuffix = '_' + apiKey + this._legacyStorageSuffix;
_parseConfig(this.options, opt_config);
this.cookieStorage.options({
expirationDays: this.options.cookieExpiration,
Expand Down Expand Up @@ -5864,6 +5865,29 @@ AmplitudeClient.prototype._apiKeySet = function _apiKeySet(methodName) {
*/
AmplitudeClient.prototype._loadSavedUnsentEvents = function _loadSavedUnsentEvents(unsentKey) {
var savedUnsentEventsString = this._getFromStorage(localStorage$1, unsentKey);
var events = this._parseSavedUnsentEventsString(savedUnsentEventsString, unsentKey);

var savedUnsentEventsStringLegacy = this._getFromStorageLegacy(localStorage$1, unsentKey);
var legacyEvents = this._parseSavedUnsentEventsString(savedUnsentEventsStringLegacy, unsentKey);

var unsentEvents = legacyEvents.concat(events);

// Migrate legacy events out of storage
this._removeFromLegacyStorage(localStorage$1, unsentKey);
this._setInStorage(localStorage$1, unsentKey, JSON.stringify(unsentEvents));

return unsentEvents;
};

AmplitudeClient.prototype._removeFromLegacyStorage = function _removeFromLegacyStorage(storage, key) {
storage.removeItem(key + this._legacyStorageSuffix);
};

/**
* Load saved events from localStorage. JSON deserializes event array. Handles case where string is corrupted.
* @private
*/
AmplitudeClient.prototype._parseSavedUnsentEventsString = function _parseSavedUnsentEventsString(savedUnsentEventsString, unsentKey) {
if (utils.isEmptyString(savedUnsentEventsString)) {
return []; // new app, does not have any saved events
}
Expand Down Expand Up @@ -5977,6 +6001,15 @@ AmplitudeClient.prototype._getFromStorage = function _getFromStorage(storage, ke
return storage.getItem(key + this._storageSuffix);
};

/**
* Helper function to fetch values from storage
* Storage argument allows for localStoraoge and sessionStoraoge
* @private
*/
AmplitudeClient.prototype._getFromStorageLegacy = function _getFromStorageLegacy(storage, key) {
return storage.getItem(key + this._legacyStorageSuffix);
};

/**
* Helper function to set values in storage
* Storage argument allows for localStoraoge and sessionStoraoge
Expand All @@ -5994,7 +6027,7 @@ AmplitudeClient.prototype._setInStorage = function _setInStorage(storage, key, v
*/
var _upgradeCookeData = function _upgradeCookeData(scope) {
// skip if migration already happened
var cookieData = scope.cookieStorage.get(scope.options.cookieName);
var cookieData = scope.cookieStorage.get(scope.options.cookieName + scope._storageSuffix);
if (type(cookieData) === 'object' && cookieData.deviceId && cookieData.sessionId && cookieData.lastEventTime) {
return;
}
Expand Down Expand Up @@ -6047,34 +6080,45 @@ var _upgradeCookeData = function _upgradeCookeData(scope) {
*/
var _loadCookieData = function _loadCookieData(scope) {
var cookieData = scope.cookieStorage.get(scope.options.cookieName + scope._storageSuffix);

if (type(cookieData) === 'object') {
if (cookieData.deviceId) {
scope.options.deviceId = cookieData.deviceId;
}
if (cookieData.userId) {
scope.options.userId = cookieData.userId;
}
if (cookieData.optOut !== null && cookieData.optOut !== undefined) {
scope.options.optOut = cookieData.optOut;
}
if (cookieData.sessionId) {
scope._sessionId = parseInt(cookieData.sessionId);
}
if (cookieData.lastEventTime) {
scope._lastEventTime = parseInt(cookieData.lastEventTime);
}
if (cookieData.eventId) {
scope._eventId = parseInt(cookieData.eventId);
}
if (cookieData.identifyId) {
scope._identifyId = parseInt(cookieData.identifyId);
}
if (cookieData.sequenceNumber) {
scope._sequenceNumber = parseInt(cookieData.sequenceNumber);
_loadCookieDataProps(scope, cookieData);
} else {
var legacyCookieData = scope.cookieStorage.get(scope.options.cookieName + scope._legacyStorageSuffix);
if (type(legacyCookieData) === 'object') {
scope.cookieStorage.remove(scope.options.cookieName + scope._legacyStorageSuffix);
_loadCookieDataProps(scope, legacyCookieData);
}
}
};

var _loadCookieDataProps = function _loadCookieDataProps(scope, cookieData) {
if (cookieData.deviceId) {
scope.options.deviceId = cookieData.deviceId;
}
if (cookieData.userId) {
scope.options.userId = cookieData.userId;
}
if (cookieData.optOut !== null && cookieData.optOut !== undefined) {
scope.options.optOut = cookieData.optOut;
}
if (cookieData.sessionId) {
scope._sessionId = parseInt(cookieData.sessionId);
}
if (cookieData.lastEventTime) {
scope._lastEventTime = parseInt(cookieData.lastEventTime);
}
if (cookieData.eventId) {
scope._eventId = parseInt(cookieData.eventId);
}
if (cookieData.identifyId) {
scope._identifyId = parseInt(cookieData.identifyId);
}
if (cookieData.sequenceNumber) {
scope._sequenceNumber = parseInt(cookieData.sequenceNumber);
}
};

/**
* Saves deviceId, userId, event meta data to amplitude cookie
* @private
Expand Down
2 changes: 1 addition & 1 deletion amplitude.min.js

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ module.exports = function(config) {
sauceLabs: {
testName: 'Amplitude JavaScript SDK',
},
preprocessors: {
'**/*.js': ['sourcemap']
},
frameworks: ['mocha', 'chai', 'sinon'],
files: ['amplitude-snippet.min.js', 'build/snippet-tests.js', 'build/tests.js'],
reporters: ['mocha', 'saucelabs'],
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"karma-mocha": "^1.3.0",
"karma-mocha-reporter": "^2.2.5",
"karma-sauce-launcher": "^1.2.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-sinon": "^1.0.5",
"mocha": "^4.0.1",
"rollup": "^0.49.3",
Expand Down
1 change: 1 addition & 0 deletions rollup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ import config from './rollup.config.js';

config.input = 'test/tests.js';
config.output.file = 'build/tests.js';
config.output.sourcemap = true;

export default config;
95 changes: 70 additions & 25 deletions src/amplitude-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import DEFAULT_OPTIONS from './options';
*/
var AmplitudeClient = function AmplitudeClient(instanceName) {
this._instanceName = utils.isEmptyString(instanceName) ? Constants.DEFAULT_INSTANCE : instanceName.toLowerCase();
this._storageSuffix = this._instanceName === Constants.DEFAULT_INSTANCE ? '' : '_' + this._instanceName;
this._legacyStorageSuffix = this._instanceName === Constants.DEFAULT_INSTANCE ? '' : '_' + this._instanceName;
this._unsentEvents = [];
this._unsentIdentifys = [];
this._ua = new UAParser(navigator.userAgent).getResult();
Expand Down Expand Up @@ -66,6 +66,7 @@ AmplitudeClient.prototype.init = function init(apiKey, opt_userId, opt_config, o

try {
this.options.apiKey = apiKey;
this._storageSuffix = '_' + apiKey + this._legacyStorageSuffix;
_parseConfig(this.options, opt_config);
this.cookieStorage.options({
expirationDays: this.options.cookieExpiration,
Expand Down Expand Up @@ -221,6 +222,30 @@ AmplitudeClient.prototype._apiKeySet = function _apiKeySet(methodName) {
*/
AmplitudeClient.prototype._loadSavedUnsentEvents = function _loadSavedUnsentEvents(unsentKey) {
var savedUnsentEventsString = this._getFromStorage(localStorage, unsentKey);
var events = this._parseSavedUnsentEventsString(savedUnsentEventsString, unsentKey);

var savedUnsentEventsStringLegacy = this._getFromStorageLegacy(localStorage, unsentKey);
var legacyEvents = this._parseSavedUnsentEventsString(savedUnsentEventsStringLegacy, unsentKey);

var unsentEvents = legacyEvents.concat(events);

// Migrate legacy events out of storage
this._removeFromLegacyStorage(localStorage, unsentKey);
this._setInStorage(localStorage, unsentKey, JSON.stringify(unsentEvents));

return unsentEvents;
};


AmplitudeClient.prototype._removeFromLegacyStorage = function _removeFromLegacyStorage(storage, key) {
storage.removeItem(key + this._legacyStorageSuffix);
};

/**
* Load saved events from localStorage. JSON deserializes event array. Handles case where string is corrupted.
* @private
*/
AmplitudeClient.prototype._parseSavedUnsentEventsString = function _parseSavedUnsentEventsString(savedUnsentEventsString, unsentKey) {
if (utils.isEmptyString(savedUnsentEventsString)) {
return []; // new app, does not have any saved events
}
Expand Down Expand Up @@ -333,6 +358,15 @@ AmplitudeClient.prototype._getFromStorage = function _getFromStorage(storage, ke
return storage.getItem(key + this._storageSuffix);
};

/**
* Helper function to fetch values from storage
* Storage argument allows for localStoraoge and sessionStoraoge
* @private
*/
AmplitudeClient.prototype._getFromStorageLegacy = function _getFromStorageLegacy(storage, key) {
return storage.getItem(key + this._legacyStorageSuffix);
};

/**
* Helper function to set values in storage
* Storage argument allows for localStoraoge and sessionStoraoge
Expand All @@ -350,7 +384,7 @@ AmplitudeClient.prototype._setInStorage = function _setInStorage(storage, key, v
*/
var _upgradeCookeData = function _upgradeCookeData(scope) {
// skip if migration already happened
var cookieData = scope.cookieStorage.get(scope.options.cookieName);
var cookieData = scope.cookieStorage.get(scope.options.cookieName + scope._storageSuffix);
if (type(cookieData) === 'object' && cookieData.deviceId && cookieData.sessionId && cookieData.lastEventTime) {
return;
}
Expand Down Expand Up @@ -403,34 +437,45 @@ var _upgradeCookeData = function _upgradeCookeData(scope) {
*/
var _loadCookieData = function _loadCookieData(scope) {
var cookieData = scope.cookieStorage.get(scope.options.cookieName + scope._storageSuffix);

if (type(cookieData) === 'object') {
if (cookieData.deviceId) {
scope.options.deviceId = cookieData.deviceId;
}
if (cookieData.userId) {
scope.options.userId = cookieData.userId;
}
if (cookieData.optOut !== null && cookieData.optOut !== undefined) {
scope.options.optOut = cookieData.optOut;
}
if (cookieData.sessionId) {
scope._sessionId = parseInt(cookieData.sessionId);
}
if (cookieData.lastEventTime) {
scope._lastEventTime = parseInt(cookieData.lastEventTime);
}
if (cookieData.eventId) {
scope._eventId = parseInt(cookieData.eventId);
}
if (cookieData.identifyId) {
scope._identifyId = parseInt(cookieData.identifyId);
}
if (cookieData.sequenceNumber) {
scope._sequenceNumber = parseInt(cookieData.sequenceNumber);
_loadCookieDataProps(scope, cookieData);
} else {
var legacyCookieData = scope.cookieStorage.get(scope.options.cookieName + scope._legacyStorageSuffix);
if (type(legacyCookieData) === 'object') {
scope.cookieStorage.remove(scope.options.cookieName + scope._legacyStorageSuffix);
_loadCookieDataProps(scope, legacyCookieData);
}
}
};

var _loadCookieDataProps = function _loadCookieDataProps(scope, cookieData) {
if (cookieData.deviceId) {
scope.options.deviceId = cookieData.deviceId;
}
if (cookieData.userId) {
scope.options.userId = cookieData.userId;
}
if (cookieData.optOut !== null && cookieData.optOut !== undefined) {
scope.options.optOut = cookieData.optOut;
}
if (cookieData.sessionId) {
scope._sessionId = parseInt(cookieData.sessionId);
}
if (cookieData.lastEventTime) {
scope._lastEventTime = parseInt(cookieData.lastEventTime);
}
if (cookieData.eventId) {
scope._eventId = parseInt(cookieData.eventId);
}
if (cookieData.identifyId) {
scope._identifyId = parseInt(cookieData.identifyId);
}
if (cookieData.sequenceNumber) {
scope._sequenceNumber = parseInt(cookieData.sequenceNumber);
}
};

/**
* Saves deviceId, userId, event meta data to amplitude cookie
* @private
Expand Down
Loading

0 comments on commit 2e0e81b

Please sign in to comment.