Skip to content

Commit 9aec943

Browse files
committed
adding migration path from localstorage to cookie data
1 parent 7f64a97 commit 9aec943

File tree

5 files changed

+140
-4
lines changed

5 files changed

+140
-4
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
## Unreleased
22

3+
* Localstorage is not persisted across subdomains, reverting cookie data migration and adding a reverse migration path for users already on 2.6.0.
4+
5+
## 2.6.0 (November 2, 2015)
6+
7+
* Migrate cookie data to local storage to address issue where having cookies disabled causes SDK to generate a new deviceId for returning users.
8+
39
## 2.5.0 (September 30, 2015)
410

511
* Add support for user properties operations (set, setOnce, add, unset).

amplitude.js

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ Amplitude.prototype.init = function(apiKey, opt_userId, opt_config, callback) {
216216
});
217217
this.options.domain = Cookie.options().domain;
218218

219+
_migrateLocalStorageDataToCookie(this);
219220
_loadCookieData(this);
220221

221222
this.options.deviceId = (opt_config && opt_config.deviceId !== undefined &&
@@ -334,6 +335,40 @@ Amplitude.prototype._sendEventsIfReady = function(callback) {
334335
return false;
335336
};
336337

338+
var _migrateLocalStorageDataToCookie = function(scope) {
339+
var cookieData = Cookie.get(scope.options.cookieName);
340+
if (cookieData && cookieData.deviceId && cookieData.userId &&
341+
cookieData.optOut !== null && cookieData.optOut !== undefined) {
342+
return; // migration not needed
343+
}
344+
345+
var cookieDeviceId = (cookieData && cookieData.deviceId) || null;
346+
var cookieUserId = (cookieData && cookieData.userId) || null;
347+
var cookieOptOut = (cookieData && cookieData.optOut !== null && cookieData.optOut !== undefined) ?
348+
cookieData.optOut : null;
349+
350+
var keySuffix = '_' + scope.options.apiKey.slice(0, 6);
351+
var localStorageDeviceId = localStorage.getItem('amplitude_deviceId' + keySuffix);
352+
if (localStorageDeviceId) {
353+
localStorage.removeItem('amplitude_deviceId' + keySuffix);
354+
}
355+
var localStorageUserId = localStorage.getItem('amplitude_userId' + keySuffix);
356+
if (localStorageUserId) {
357+
localStorage.removeItem('amplitude_userId' + keySuffix);
358+
}
359+
var localStorageOptOut = localStorage.getItem('amplitude_optOut' + keySuffix);
360+
if (localStorageOptOut !== null && localStorageOptOut !== undefined) {
361+
localStorage.removeItem('amplitude_optOut' + keySuffix);
362+
localStorageOptOut = String(localStorageOptOut) === 'true'; // convert to boolean
363+
}
364+
365+
Cookie.set(scope.options.cookieName, {
366+
deviceId: cookieDeviceId || localStorageDeviceId,
367+
userId: cookieUserId || localStorageUserId,
368+
optOut: (cookieOptOut !== undefined && cookieOptOut !== null) ? cookieOptOut : localStorageOptOut
369+
});
370+
};
371+
337372
var _loadCookieData = function(scope) {
338373
var cookieData = Cookie.get(scope.options.cookieName);
339374
if (cookieData) {
@@ -343,7 +378,7 @@ var _loadCookieData = function(scope) {
343378
if (cookieData.userId) {
344379
scope.options.userId = cookieData.userId;
345380
}
346-
if (cookieData.optOut !== undefined) {
381+
if (cookieData.optOut !== null && cookieData.optOut !== undefined) {
347382
scope.options.optOut = cookieData.optOut;
348383
}
349384
}

amplitude.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/amplitude.js

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ Amplitude.prototype.init = function(apiKey, opt_userId, opt_config, callback) {
110110
});
111111
this.options.domain = Cookie.options().domain;
112112

113+
_migrateLocalStorageDataToCookie(this);
113114
_loadCookieData(this);
114115

115116
this.options.deviceId = (opt_config && opt_config.deviceId !== undefined &&
@@ -228,6 +229,40 @@ Amplitude.prototype._sendEventsIfReady = function(callback) {
228229
return false;
229230
};
230231

232+
var _migrateLocalStorageDataToCookie = function(scope) {
233+
var cookieData = Cookie.get(scope.options.cookieName);
234+
if (cookieData && cookieData.deviceId && cookieData.userId &&
235+
cookieData.optOut !== null && cookieData.optOut !== undefined) {
236+
return; // migration not needed
237+
}
238+
239+
var cookieDeviceId = (cookieData && cookieData.deviceId) || null;
240+
var cookieUserId = (cookieData && cookieData.userId) || null;
241+
var cookieOptOut = (cookieData && cookieData.optOut !== null && cookieData.optOut !== undefined) ?
242+
cookieData.optOut : null;
243+
244+
var keySuffix = '_' + scope.options.apiKey.slice(0, 6);
245+
var localStorageDeviceId = localStorage.getItem('amplitude_deviceId' + keySuffix);
246+
if (localStorageDeviceId) {
247+
localStorage.removeItem('amplitude_deviceId' + keySuffix);
248+
}
249+
var localStorageUserId = localStorage.getItem('amplitude_userId' + keySuffix);
250+
if (localStorageUserId) {
251+
localStorage.removeItem('amplitude_userId' + keySuffix);
252+
}
253+
var localStorageOptOut = localStorage.getItem('amplitude_optOut' + keySuffix);
254+
if (localStorageOptOut !== null && localStorageOptOut !== undefined) {
255+
localStorage.removeItem('amplitude_optOut' + keySuffix);
256+
localStorageOptOut = String(localStorageOptOut) === 'true'; // convert to boolean
257+
}
258+
259+
Cookie.set(scope.options.cookieName, {
260+
deviceId: cookieDeviceId || localStorageDeviceId,
261+
userId: cookieUserId || localStorageUserId,
262+
optOut: (cookieOptOut !== undefined && cookieOptOut !== null) ? cookieOptOut : localStorageOptOut
263+
});
264+
};
265+
231266
var _loadCookieData = function(scope) {
232267
var cookieData = Cookie.get(scope.options.cookieName);
233268
if (cookieData) {
@@ -237,7 +272,7 @@ var _loadCookieData = function(scope) {
237272
if (cookieData.userId) {
238273
scope.options.userId = cookieData.userId;
239274
}
240-
if (cookieData.optOut !== undefined) {
275+
if (cookieData.optOut !== null && cookieData.optOut !== undefined) {
241276
scope.options.optOut = cookieData.optOut;
242277
}
243278
}

test/amplitude.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,66 @@ describe('Amplitude', function() {
7474
amplitude.init(apiKey, userId, null, callback);
7575
assert.equal(counter, 1);
7676
});
77+
78+
it ('should migrate deviceId, userId, optOut from localStorage to cookie', function() {
79+
var deviceId = 'test_device_id';
80+
var userId = 'test_user_id';
81+
82+
assert.isNull(cookie.get(amplitude.options.cookieName));
83+
localStorage.setItem('amplitude_deviceId' + '_' + apiKey, deviceId);
84+
localStorage.setItem('amplitude_userId' + '_' + apiKey, userId);
85+
localStorage.setItem('amplitude_optOut' + '_' + apiKey, true);
86+
87+
amplitude.init(apiKey);
88+
assert.equal(amplitude.options.deviceId, deviceId);
89+
assert.equal(amplitude.options.userId, userId);
90+
assert.isTrue(amplitude.options.optOut);
91+
92+
var cookieData = cookie.get(amplitude.options.cookieName);
93+
assert.equal(cookieData.deviceId, deviceId);
94+
assert.equal(cookieData.userId, userId);
95+
assert.isTrue(cookieData.optOut);
96+
97+
assert.isNull(localStorage.getItem('amplitude_deviceId' + '_' + apiKey));
98+
assert.isNull(localStorage.getItem('amplitude_userId' + '_' + apiKey));
99+
assert.isNull(localStorage.getItem('amplitude_optOut' + '_' + apiKey));
100+
});
101+
102+
it ('should migrate data from localStorage to cookie but preserve existing values', function() {
103+
var deviceId = 'test_device_id2';
104+
var userId = 'test_user_id2';
105+
106+
// use amplitude1 to set cookie values
107+
amplitude.init(apiKey, null, {deviceId: deviceId});
108+
assert.equal(amplitude.options.deviceId, deviceId);
109+
assert.isNull(amplitude.options.userId);
110+
assert.isFalse(amplitude.options.optOut);
111+
112+
var cookieData = cookie.get(amplitude.options.cookieName);
113+
assert.equal(cookieData.deviceId, deviceId);
114+
assert.isNull(cookieData.userId);
115+
assert.isFalse(cookieData.optOut);
116+
117+
// set local storage values and verify that they are ignored by the init migration
118+
localStorage.setItem('amplitude_deviceId' + '_' + apiKey, 'bad_test_device_id'); // ignored
119+
localStorage.setItem('amplitude_userId' + '_' + apiKey, userId); // since userId null, use localStorage value
120+
localStorage.setItem('amplitude_optOut' + '_' + apiKey, true); // ignored
121+
122+
var amplitude2 = new Amplitude();
123+
amplitude2.init(apiKey);
124+
assert.equal(amplitude2.options.deviceId, deviceId);
125+
assert.equal(amplitude2.options.userId, userId);
126+
assert.isFalse(amplitude2.options.optOut);
127+
128+
cookieData = cookie.get(amplitude.options.cookieName);
129+
assert.equal(cookieData.deviceId, deviceId);
130+
assert.equal(cookieData.userId, userId);
131+
assert.isFalse(cookieData.optOut);
132+
133+
assert.isNull(localStorage.getItem('amplitude_deviceId' + '_' + apiKey));
134+
assert.isNull(localStorage.getItem('amplitude_userId' + '_' + apiKey));
135+
assert.isNull(localStorage.getItem('amplitude_optOut' + '_' + apiKey));
136+
});
77137
});
78138

79139
describe('runQueuedFunctions', function() {

0 commit comments

Comments
 (0)