diff --git a/packages/analytics-browser/src/browser-client.ts b/packages/analytics-browser/src/browser-client.ts index c429eeb94..1f5d96cc2 100644 --- a/packages/analytics-browser/src/browser-client.ts +++ b/packages/analytics-browser/src/browser-client.ts @@ -10,6 +10,7 @@ import { isFormInteractionTrackingEnabled, setConnectorDeviceId, setConnectorUserId, + isNewSession, } from '@amplitude/analytics-client-common'; import { BrowserClient, @@ -250,14 +251,13 @@ export class AmplitudeBrowser extends AmplitudeCore implements BrowserClient { async process(event: Event) { const currentTime = Date.now(); - const lastEventTime = this.config.lastEventTime || Date.now(); - const timeSinceLastEvent = currentTime - lastEventTime; + const _isNewSession = isNewSession(this.config.sessionTimeout, this.config.lastEventTime); if ( event.event_type !== DEFAULT_SESSION_START_EVENT && event.event_type !== DEFAULT_SESSION_END_EVENT && (!event.session_id || event.session_id === this.getSessionId()) && - timeSinceLastEvent > this.config.sessionTimeout + _isNewSession ) { this.setSessionId(currentTime); } diff --git a/packages/analytics-client-common/src/index.ts b/packages/analytics-client-common/src/index.ts index ab94858ff..65e3e19d4 100644 --- a/packages/analytics-client-common/src/index.ts +++ b/packages/analytics-client-common/src/index.ts @@ -1,6 +1,7 @@ export { CampaignParser } from './attribution/campaign-parser'; export { CampaignTracker } from './attribution/campaign-tracker'; export { getQueryParams } from './query-params'; +export { isNewSession } from './session'; export { getCookieName, getOldCookieName } from './cookie-name'; export { CookieStorage } from './storage/cookie'; export { FetchTransport } from './transports/fetch'; diff --git a/packages/analytics-client-common/src/session.ts b/packages/analytics-client-common/src/session.ts new file mode 100644 index 000000000..bde539371 --- /dev/null +++ b/packages/analytics-client-common/src/session.ts @@ -0,0 +1,7 @@ +export const isNewSession = (sessionTimeout: number, lastEventTime?: number): boolean => { + const _currentTime = Date.now(); + const _lastEventTime = lastEventTime || Date.now(); + const timeSinceLastEvent = _currentTime - _lastEventTime; + + return timeSinceLastEvent > sessionTimeout; +}; diff --git a/packages/analytics-client-common/test/session.test.ts b/packages/analytics-client-common/test/session.test.ts new file mode 100644 index 000000000..189bf70a9 --- /dev/null +++ b/packages/analytics-client-common/test/session.test.ts @@ -0,0 +1,25 @@ +import { isNewSession } from '../src/session'; + +describe('session', () => { + const sessionTimeout: number = 30 * 60 * 1000; + + test('should be a new session for the first event', () => { + const _isNewSession = isNewSession(sessionTimeout, undefined); + + expect(_isNewSession).toBe(false); + }); + + test('should be a new session', () => { + const lastEventTime = Date.now() - sessionTimeout * 2; + const _isNewSession = isNewSession(sessionTimeout, lastEventTime); + + expect(_isNewSession).toBe(true); + }); + + test('should be in a same session', () => { + const lastEventTime = Date.now(); + const _isNewSession = isNewSession(sessionTimeout, lastEventTime); + + expect(_isNewSession).toBe(false); + }); +}); diff --git a/packages/plugin-web-attribution-browser/src/helpers.ts b/packages/plugin-web-attribution-browser/src/helpers.ts index aad22a4c9..9e95cffdf 100644 --- a/packages/plugin-web-attribution-browser/src/helpers.ts +++ b/packages/plugin-web-attribution-browser/src/helpers.ts @@ -17,13 +17,24 @@ const domainWithoutSubdomain = (domain: string) => { return parts.slice(parts.length - 2, parts.length).join('.'); }; -export const isNewCampaign = (current: Campaign, previous: Campaign | undefined, options: Options) => { +export const isNewCampaign = ( + current: Campaign, + previous: Campaign | undefined, + options: Options, + isNewSession = true, +) => { const { referrer, referring_domain, ...currentCampaign } = current; const { referrer: _previous_referrer, referring_domain: prevReferringDomain, ...previousCampaign } = previous || {}; if (isExcludedReferrer(options.excludeReferrers, current.referring_domain)) { return false; } + + //In the same session, no referrer should not override or unset any persisting query params + if (!isNewSession && !referrer && previous) { + return false; + } + const hasNewCampaign = JSON.stringify(currentCampaign) !== JSON.stringify(previousCampaign); const hasNewDomain = domainWithoutSubdomain(referring_domain || '') !== domainWithoutSubdomain(prevReferringDomain || ''); diff --git a/packages/plugin-web-attribution-browser/src/web-attribution.ts b/packages/plugin-web-attribution-browser/src/web-attribution.ts index e6a6a57c6..e71e5cfe5 100644 --- a/packages/plugin-web-attribution-browser/src/web-attribution.ts +++ b/packages/plugin-web-attribution-browser/src/web-attribution.ts @@ -2,6 +2,7 @@ import { CampaignParser } from '@amplitude/analytics-client-common'; import { BeforePlugin, BrowserClient, BrowserConfig, Campaign, Event, Storage } from '@amplitude/analytics-types'; import { createCampaignEvent, getDefaultExcludedReferrers, getStorageKey, isNewCampaign } from './helpers'; import { CreateWebAttributionPlugin, Options } from './typings/web-attribution'; +import { isNewSession } from '@amplitude/analytics-client-common'; export const webAttributionPlugin: CreateWebAttributionPlugin = function (options: Options = {}) { const plugin: BeforePlugin = { @@ -26,7 +27,9 @@ export const webAttributionPlugin: CreateWebAttributionPlugin = function (option storage.get(storageKey), ]); - if (isNewCampaign(currentCampaign, previousCampaign, pluginConfig)) { + const _isNewSession = isNewSession(config.sessionTimeout, config.lastEventTime); + + if (isNewCampaign(currentCampaign, previousCampaign, pluginConfig, _isNewSession)) { if (pluginConfig.resetSessionOnNewCampaign) { amplitude.setSessionId(Date.now()); config.loggerProvider.log('Created a new session for new campaign.'); diff --git a/packages/plugin-web-attribution-browser/test/helpers.test.ts b/packages/plugin-web-attribution-browser/test/helpers.test.ts index cec737591..084705bad 100644 --- a/packages/plugin-web-attribution-browser/test/helpers.test.ts +++ b/packages/plugin-web-attribution-browser/test/helpers.test.ts @@ -112,6 +112,19 @@ describe('isNewCampaign', () => { }), ).toBe(false); }); + + test('should return false for no referrer in the same session', () => { + const previousCampaign = { + ...BASE_CAMPAIGN, + utm_campaign: 'utm_campaign', + referring_domain: 'a.b.c.d', + }; + const currentCampaign = { + ...BASE_CAMPAIGN, + }; + + expect(isNewCampaign(currentCampaign, previousCampaign, {}, false)).toBe(false); + }); }); describe('isExcludedReferrer', () => {