From b64b8b0d0619c5f749e71dca45773d7c869631bb Mon Sep 17 00:00:00 2001 From: Kevin Pagtakhan Date: Wed, 19 Jan 2022 21:41:48 -0800 Subject: [PATCH] feat: accept custom session id paramter in config (#485) --- src/amplitude-client.js | 13 +++++++++++-- src/options.js | 2 ++ src/utils.js | 10 ++++++++++ test/amplitude-client.js | 21 +++++++++++++++++++++ test/utils.js | 20 ++++++++++++++++++++ 5 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/amplitude-client.js b/src/amplitude-client.js index cda4ffc7..893888fb 100644 --- a/src/amplitude-client.js +++ b/src/amplitude-client.js @@ -174,13 +174,19 @@ AmplitudeClient.prototype.init = function init(apiKey, opt_userId, opt_config, o var now = new Date().getTime(); const startNewSession = - !this._sessionId || !this._lastEventTime || now - this._lastEventTime > this.options.sessionTimeout; + !this._sessionId || + !this._lastEventTime || + now - this._lastEventTime > this.options.sessionTimeout || + this.options.sessionId; if (startNewSession) { if (this.options.unsetParamsReferrerOnNewSession) { this._unsetUTMParams(); } this._newSession = true; - this._sessionId = now; + this._sessionId = this.options.sessionId || now; + // reset this.options.sessionId to avoid re-usage + // use instance.getSessionId() to get session id + this.options.sessionId = undefined; // only capture UTM params and referrer if new session if (this.options.saveParamsReferrerOncePerSession) { @@ -387,6 +393,9 @@ var _parseConfig = function _parseConfig(options, config) { var expectedType = type(options[key]); if (key === 'transport' && !utils.validateTransport(inputValue)) { return; + } else if (key === 'sessionId' && inputValue !== null) { + options[key] = utils.validateSessionId(inputValue) ? inputValue : null; + return; } else if (!utils.validateInput(inputValue, key + ' option', expectedType)) { return; } diff --git a/src/options.js b/src/options.js index 124198cb..21826eb7 100644 --- a/src/options.js +++ b/src/options.js @@ -52,6 +52,7 @@ import { version as libraryVersion } from '../package.json'; * @property {string} [serverZone] - For server zone related configuration, used for server api endpoint and dynamic configuration. * @property {boolean} [useDynamicConfig] - Enable dynamic configuration to find best server url for user. * @property {boolean} [serverZoneBasedApi] - To update api endpoint with serverZone change or not. For data residency, recommend to enable it unless using own proxy server. + * @property {number} [sessionId=`null`] - The custom Session ID for the current session. *Note: This is not recommended unless you know what you are doing because the Session ID of a session is utilized for all session metrics in Amplitude. */ export default { apiEndpoint: Constants.EVENT_LOG_URL, @@ -121,4 +122,5 @@ export default { serverZone: AmplitudeServerZone.US, useDynamicConfig: false, serverZoneBasedApi: false, + sessionId: null, }; diff --git a/src/utils.js b/src/utils.js index a163576e..4cb26a37 100644 --- a/src/utils.js +++ b/src/utils.js @@ -278,6 +278,15 @@ const isWebWorkerEnvironment = () => { return typeof WorkerGlobalScope !== 'undefined'; }; +const validateSessionId = (sessionId) => { + if (validateInput(sessionId, 'sessionId', 'number') && new Date(sessionId).getTime() > 0) { + return true; + } + + log.error(`sessionId value must in milliseconds since epoch (Unix Timestamp)`); + return false; +}; + export default { setLogLevel, getLogLevel, @@ -293,4 +302,5 @@ export default { validateProperties, validateDeviceId, validateTransport, + validateSessionId, }; diff --git a/test/amplitude-client.js b/test/amplitude-client.js index c17aded2..368d5e14 100644 --- a/test/amplitude-client.js +++ b/test/amplitude-client.js @@ -905,6 +905,27 @@ describe('AmplitudeClient', function () { assert.deepEqual(amplitude.options.plan, plan); }); + + it('should set sessionId from config', () => { + const amplitude = new AmplitudeClient(); + amplitude.init(apiKey, null, { sessionId: 123 }); + assert.equal(amplitude.getSessionId(), 123); + assert.isUndefined(amplitude.options.sessionId); + }); + + it('should set default sessionId', () => { + const amplitude = new AmplitudeClient(); + amplitude.init(apiKey, null); + assert.isTrue(amplitude.getSessionId() > 0); + assert.isUndefined(amplitude.options.sessionId); + }); + + it('should not set invalid sessionId', () => { + const amplitude = new AmplitudeClient(); + amplitude.init(apiKey, null, { sessionId: 'asdf' }); + assert.isTrue(amplitude.getSessionId() > 0); + assert.isUndefined(amplitude.options.sessionId); + }); }); describe('runQueuedFunctions', function () { diff --git a/test/utils.js b/test/utils.js index 76278412..6f9c43e0 100644 --- a/test/utils.js +++ b/test/utils.js @@ -260,4 +260,24 @@ describe('utils', function () { assert.isFalse(utils.isWebWorkerEnvironment()); }); }); + + describe('validateSessionId', function () { + it('should return true', function () { + assert.isTrue(utils.validateSessionId(Date.now())); + }); + + it('should return false', function () { + assert.isFalse(utils.validateSessionId('asdf')); + assert.isFalse(utils.validateSessionId(0)); + assert.isFalse(utils.validateSessionId(NaN)); + assert.isFalse(utils.validateSessionId(null)); + assert.isFalse(utils.validateSessionId(undefined)); + assert.isFalse(utils.validateSessionId({})); + assert.isFalse(utils.validateSessionId([])); + assert.isFalse(utils.validateSessionId(new Map())); + assert.isFalse(utils.validateSessionId(new Set())); + assert.isFalse(utils.validateSessionId(true)); + assert.isFalse(utils.validateSessionId(false)); + }); + }); });