Skip to content

Commit

Permalink
[Backport 5.2] web/tracking: extract user and session tracking from E…
Browse files Browse the repository at this point in the history
…ventLogger (#57884) (#58029)

`EventLogger` is currently a very overloaded mechanism that handles all sorts of tracking mechanisms and cookie configurations on top of event logging.

This change extracts user tracking (anonymous user ID, cohorts, and device ID) and session tracking (URL stuff) from `EventLogger` into separate, isolated classes. Functionally everything should be the same, but streamlined significantly. Most importantly, this unblocks us so that we can use these mechanisms for introducing a client implementation for the [new telemetry framework](https://docs.sourcegraph.com/dev/background-information/telemetry) while preserving existing behaviour.

This only changes `client/web` - it seems this is copy-pasted into several places, we can work on centralizing them all later.

Part of #56920
Enables #57939

`sg start dotcom`, then:

```
select * from event_logs where source = 'WEB' order by timestamp desc limit 3;
```

(cherry picked from commit f79aa36)
  • Loading branch information
bobheadxi committed Nov 1, 2023
1 parent 4b2ef0c commit 9de3bed
Show file tree
Hide file tree
Showing 9 changed files with 310 additions and 277 deletions.
4 changes: 4 additions & 0 deletions client/web/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -1698,8 +1698,11 @@ ts_project(
"src/tour/data/index.tsx",
"src/tour/hooks.ts",
"src/tracking/TelemetricLink.tsx",
"src/tracking/cookies.ts",
"src/tracking/eventLogger.ts",
"src/tracking/services/serverAdminWrapper.tsx",
"src/tracking/sessionTracker.ts",
"src/tracking/userTracker.ts",
"src/tracking/util.ts",
"src/types/fzy.js/index.d.ts",
"src/user/area/UserArea.tsx",
Expand Down Expand Up @@ -2007,6 +2010,7 @@ ts_project(
"src/team/area/testContext.mock.ts",
"src/tour/components/Tour/Tour.test.tsx",
"src/tour/components/Tour/useTour.test.tsx",
"src/tracking/userTracker.test.ts",
"src/tracking/util.test.ts",
"src/user/index.test.ts",
"src/user/settings/auth/ExternalAccount.test.tsx",
Expand Down
9 changes: 4 additions & 5 deletions client/web/src/auth/SignUpForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { useCallback, useMemo, useState } from 'react'

import { mdiBitbucket, mdiGithub, mdiGitlab } from '@mdi/js'
import classNames from 'classnames'
import cookies from 'js-cookie'
import { type Observable, of } from 'rxjs'
import { fromFetch } from 'rxjs/fetch'
import { catchError, switchMap } from 'rxjs/operators'
Expand All @@ -17,7 +16,7 @@ import { Link, Icon, Label, Text, Button, AnchorLink, LoaderInput, ErrorAlert }

import { LoaderButton } from '../components/LoaderButton'
import type { AuthProvider, SourcegraphContext } from '../jscontext'
import { ANONYMOUS_USER_ID_KEY, eventLogger, FIRST_SOURCE_URL_KEY, LAST_SOURCE_URL_KEY } from '../tracking/eventLogger'
import { eventLogger } from '../tracking/eventLogger'
import { validatePassword, getPasswordRequirements } from '../util/security'

import { OrDivider } from './OrDivider'
Expand Down Expand Up @@ -110,9 +109,9 @@ export const SignUpForm: React.FunctionComponent<React.PropsWithChildren<SignUpF
email: emailState.value,
username: usernameState.value,
password: passwordState.value,
anonymousUserId: cookies.get(ANONYMOUS_USER_ID_KEY),
firstSourceUrl: cookies.get(FIRST_SOURCE_URL_KEY),
lastSourceUrl: cookies.get(LAST_SOURCE_URL_KEY),
anonymousUserId: eventLogger.user.anonymousUserID,
firstSourceUrl: eventLogger.session.getFirstSourceURL(),
lastSourceUrl: eventLogger.session.getLastSourceURL(),
}).catch(error => {
setError(asError(error))
setLoading(false)
Expand Down
50 changes: 50 additions & 0 deletions client/web/src/tracking/cookies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import cookies, { type CookieAttributes } from 'js-cookie'

/**
* Cookies is a simple interface over real cookies from 'js-cookie'.
*/
export interface Cookies {
/**
* Read cookie
*/
get(name: string): string | undefined
/**
* Create a cookie
*/
set(name: string, value: string, options?: CookieAttributes): string | undefined
}

/**
* Alias for 'js-cookie' default implementation, behind the Cookies interface.
*/
export function defaultCookies(): Cookies {
return cookies
}

export const userCookieSettings: CookieAttributes = {
// 365 days expiry, but renewed on activity.
expires: 365,
// Enforce HTTPS
secure: true,
// We only read the cookie with JS so we don't need to send it cross-site nor on initial page requests.
// However, we do need it on page redirects when users sign up via OAuth, hence using the Lax policy.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
sameSite: 'Lax',
// Specify the Domain attribute to ensure subdomains (about.sourcegraph.com) can receive this cookie.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#define_where_cookies_are_sent
domain: location.hostname,
}

export const deviceSessionCookieSettings: CookieAttributes = {
// ~30 minutes expiry, but renewed on activity.
expires: 0.0208,
// Enforce HTTPS
secure: true,
// We only read the cookie with JS so we don't need to send it cross-site nor on initial page requests.
// However, we do need it on page redirects when users sign up via OAuth, hence using the Lax policy.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
sameSite: 'Lax',
// Specify the Domain attribute to ensure subdomains (about.sourcegraph.com) can receive this cookie.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#define_where_cookies_are_sent
domain: location.hostname,
}

0 comments on commit 9de3bed

Please sign in to comment.