Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Unreleased

* Localstorage is not persisted across subdomains, reverting cookie data migration and adding a reverse migration path for users already on 2.6.0.

## 2.6.0 (November 2, 2015)

* Migrate cookie data to local storage to address issue where having cookies disabled causes SDK to generate a new deviceId for returning users.
Expand Down
42 changes: 40 additions & 2 deletions amplitude.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,11 @@ var LocalStorageKeys = {
LAST_IDENTIFY_ID: 'amplitude_lastIdentifyId',
LAST_SEQUENCE_NUMBER: 'amplitude_lastSequenceNumber',
LAST_EVENT_TIME: 'amplitude_lastEventTime',
SESSION_ID: 'amplitude_sessionId'
SESSION_ID: 'amplitude_sessionId',

DEVICE_ID: 'amplitude_deviceId',
USER_ID: 'amplitude_userId',
OPT_OUT: 'amplitude_optOut'
};

/*
Expand Down Expand Up @@ -216,6 +220,7 @@ Amplitude.prototype.init = function(apiKey, opt_userId, opt_config, callback) {
});
this.options.domain = Cookie.options().domain;

_migrateLocalStorageDataToCookie(this);
_loadCookieData(this);

this.options.deviceId = (opt_config && opt_config.deviceId !== undefined &&
Expand Down Expand Up @@ -334,6 +339,39 @@ Amplitude.prototype._sendEventsIfReady = function(callback) {
return false;
};

var _migrateLocalStorageDataToCookie = function(scope) {
var cookieData = Cookie.get(scope.options.cookieName);
if (cookieData && cookieData.deviceId) {
return; // migration not needed
}

var cookieDeviceId = (cookieData && cookieData.deviceId) || null;
var cookieUserId = (cookieData && cookieData.userId) || null;
var cookieOptOut = (cookieData && cookieData.optOut !== null && cookieData.optOut !== undefined) ?
cookieData.optOut : null;

var keySuffix = '_' + scope.options.apiKey.slice(0, 6);
var localStorageDeviceId = localStorage.getItem(LocalStorageKeys.DEVICE_ID + keySuffix);
if (localStorageDeviceId) {
localStorage.removeItem(LocalStorageKeys.DEVICE_ID + keySuffix);
}
var localStorageUserId = localStorage.getItem(LocalStorageKeys.USER_ID + keySuffix);
if (localStorageUserId) {
localStorage.removeItem(LocalStorageKeys.USER_ID + keySuffix);
}
var localStorageOptOut = localStorage.getItem(LocalStorageKeys.OPT_OUT + keySuffix);
if (localStorageOptOut !== null && localStorageOptOut !== undefined) {
localStorage.removeItem(LocalStorageKeys.OPT_OUT + keySuffix);
localStorageOptOut = String(localStorageOptOut) === 'true'; // convert to boolean
}

Cookie.set(scope.options.cookieName, {
deviceId: cookieDeviceId || localStorageDeviceId,
userId: cookieUserId || localStorageUserId,
optOut: (cookieOptOut !== undefined && cookieOptOut !== null) ? cookieOptOut : localStorageOptOut
});
};

var _loadCookieData = function(scope) {
var cookieData = Cookie.get(scope.options.cookieName);
if (cookieData) {
Expand All @@ -343,7 +381,7 @@ var _loadCookieData = function(scope) {
if (cookieData.userId) {
scope.options.userId = cookieData.userId;
}
if (cookieData.optOut !== undefined) {
if (cookieData.optOut !== null && cookieData.optOut !== undefined) {
scope.options.optOut = cookieData.optOut;
}
}
Expand Down
4 changes: 2 additions & 2 deletions amplitude.min.js

Large diffs are not rendered by default.

42 changes: 40 additions & 2 deletions src/amplitude.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ var LocalStorageKeys = {
LAST_IDENTIFY_ID: 'amplitude_lastIdentifyId',
LAST_SEQUENCE_NUMBER: 'amplitude_lastSequenceNumber',
LAST_EVENT_TIME: 'amplitude_lastEventTime',
SESSION_ID: 'amplitude_sessionId'
SESSION_ID: 'amplitude_sessionId',

DEVICE_ID: 'amplitude_deviceId',
USER_ID: 'amplitude_userId',
OPT_OUT: 'amplitude_optOut'
};

/*
Expand Down Expand Up @@ -110,6 +114,7 @@ Amplitude.prototype.init = function(apiKey, opt_userId, opt_config, callback) {
});
this.options.domain = Cookie.options().domain;

_migrateLocalStorageDataToCookie(this);
_loadCookieData(this);

this.options.deviceId = (opt_config && opt_config.deviceId !== undefined &&
Expand Down Expand Up @@ -228,6 +233,39 @@ Amplitude.prototype._sendEventsIfReady = function(callback) {
return false;
};

var _migrateLocalStorageDataToCookie = function(scope) {
var cookieData = Cookie.get(scope.options.cookieName);
if (cookieData && cookieData.deviceId) {
return; // migration not needed
}

var cookieDeviceId = (cookieData && cookieData.deviceId) || null;
var cookieUserId = (cookieData && cookieData.userId) || null;
var cookieOptOut = (cookieData && cookieData.optOut !== null && cookieData.optOut !== undefined) ?
cookieData.optOut : null;

var keySuffix = '_' + scope.options.apiKey.slice(0, 6);
var localStorageDeviceId = localStorage.getItem(LocalStorageKeys.DEVICE_ID + keySuffix);
if (localStorageDeviceId) {
localStorage.removeItem(LocalStorageKeys.DEVICE_ID + keySuffix);
}
var localStorageUserId = localStorage.getItem(LocalStorageKeys.USER_ID + keySuffix);
if (localStorageUserId) {
localStorage.removeItem(LocalStorageKeys.USER_ID + keySuffix);
}
var localStorageOptOut = localStorage.getItem(LocalStorageKeys.OPT_OUT + keySuffix);
if (localStorageOptOut !== null && localStorageOptOut !== undefined) {
localStorage.removeItem(LocalStorageKeys.OPT_OUT + keySuffix);
localStorageOptOut = String(localStorageOptOut) === 'true'; // convert to boolean
}

Cookie.set(scope.options.cookieName, {
deviceId: cookieDeviceId || localStorageDeviceId,
userId: cookieUserId || localStorageUserId,
optOut: (cookieOptOut !== undefined && cookieOptOut !== null) ? cookieOptOut : localStorageOptOut
});
};

var _loadCookieData = function(scope) {
var cookieData = Cookie.get(scope.options.cookieName);
if (cookieData) {
Expand All @@ -237,7 +275,7 @@ var _loadCookieData = function(scope) {
if (cookieData.userId) {
scope.options.userId = cookieData.userId;
}
if (cookieData.optOut !== undefined) {
if (cookieData.optOut !== null && cookieData.optOut !== undefined) {
scope.options.optOut = cookieData.optOut;
}
}
Expand Down
66 changes: 66 additions & 0 deletions test/amplitude.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,72 @@ describe('Amplitude', function() {
amplitude.init(apiKey, userId, null, callback);
assert.equal(counter, 1);
});

it ('should migrate deviceId, userId, optOut from localStorage to cookie', function() {
var deviceId = 'test_device_id';
var userId = 'test_user_id';

assert.isNull(cookie.get(amplitude.options.cookieName));
localStorage.setItem('amplitude_deviceId' + '_' + apiKey, deviceId);
localStorage.setItem('amplitude_userId' + '_' + apiKey, userId);
localStorage.setItem('amplitude_optOut' + '_' + apiKey, true);

amplitude.init(apiKey);
assert.equal(amplitude.options.deviceId, deviceId);
assert.equal(amplitude.options.userId, userId);
assert.isTrue(amplitude.options.optOut);

var cookieData = cookie.get(amplitude.options.cookieName);
assert.equal(cookieData.deviceId, deviceId);
assert.equal(cookieData.userId, userId);
assert.isTrue(cookieData.optOut);

assert.isNull(localStorage.getItem('amplitude_deviceId' + '_' + apiKey));
assert.isNull(localStorage.getItem('amplitude_userId' + '_' + apiKey));
assert.isNull(localStorage.getItem('amplitude_optOut' + '_' + apiKey));
});

it ('should migrate data from localStorage to cookie but preserve existing values', function() {
var deviceId = 'test_device_id2';
var userId = 'test_user_id2';

// use amplitude1 to set cookie values
amplitude.init(apiKey, userId, {deviceId: deviceId});
assert.equal(amplitude.options.deviceId, deviceId);
assert.equal(amplitude.options.userId, userId);
assert.isFalse(amplitude.options.optOut);

var cookieData = cookie.get(amplitude.options.cookieName);
assert.equal(cookieData.deviceId, deviceId);
assert.equal(cookieData.userId, userId);
assert.isFalse(cookieData.optOut);

// remove deviceId to make amplitude2 go through migration process
cookie.set(amplitude.options.cookieName, {
'userId': userId,
'optOut': false
});

// set local storage values and verify that they are ignored by the init migration
localStorage.setItem('amplitude_deviceId' + '_' + apiKey, deviceId);
localStorage.setItem('amplitude_userId' + '_' + apiKey, 'test_bad_user_id'); // ignored
localStorage.setItem('amplitude_optOut' + '_' + apiKey, true); // ignored

var amplitude2 = new Amplitude();
amplitude2.init(apiKey);
assert.equal(amplitude2.options.deviceId, deviceId);
assert.equal(amplitude2.options.userId, userId);
assert.isFalse(amplitude2.options.optOut);

cookieData = cookie.get(amplitude.options.cookieName);
assert.equal(cookieData.deviceId, deviceId);
assert.equal(cookieData.userId, userId);
assert.isFalse(cookieData.optOut);

assert.isNull(localStorage.getItem('amplitude_deviceId' + '_' + apiKey));
assert.isNull(localStorage.getItem('amplitude_userId' + '_' + apiKey));
assert.isNull(localStorage.getItem('amplitude_optOut' + '_' + apiKey));
});
});

describe('runQueuedFunctions', function() {
Expand Down