Skip to content

Commit 521e6a5

Browse files
akosyakovjeanp413
andauthored
[dashboard] use HTTP endpoint for telemetry (#19223)
* [dashboard] use HTTP endpoint for telemetry * 💄 --------- Co-authored-by: Jean Pierre <[email protected]>
1 parent 62ba5a8 commit 521e6a5

26 files changed

+382
-247
lines changed

components/dashboard/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
"@types/jest": "^26.0.22",
7474
"@types/js-cookie": "^2.2.7",
7575
"@types/lodash.debounce": "^4.0.7",
76-
"@types/node": "^16.11.0",
76+
"@types/node": "^18.18.8",
7777
"@types/react": "^17.0.0",
7878
"@types/react-dom": "^17.0.3",
7979
"@types/react-portal": "^4.0.4",

components/dashboard/src/Analytics.tsx

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,67 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7-
import { getGitpodService } from "./service/service";
87
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
98
import Cookies from "js-cookie";
109
import { v4 } from "uuid";
1110
import { StartWorkspaceError } from "./start/StartPage";
11+
import { RemoteTrackMessage } from "@gitpod/gitpod-protocol/lib/analytics";
1212

1313
export type Event =
1414
| "invite_url_requested"
1515
| "organisation_authorised"
1616
| "dotfile_repo_changed"
1717
| "feedback_submitted"
1818
| "workspace_class_changed"
19-
| "privacy_policy_update_accepted";
19+
| "privacy_policy_update_accepted"
20+
| "modal_dismiss"
21+
| "ide_configuration_changed"
22+
| "status_rendered"
23+
| "error_rendered";
2024
type InternalEvent = Event | "path_changed" | "dashboard_clicked";
2125

2226
export type EventProperties =
2327
| TrackOrgAuthorised
2428
| TrackInviteUrlRequested
2529
| TrackDotfileRepo
2630
| TrackFeedback
27-
| TrackPolicyUpdateClick;
31+
| TrackPolicyUpdateClick
32+
| TrackModalDismiss
33+
| TrackIDEConfigurationChanged
34+
| TrackWorkspaceClassChanged
35+
| TrackStatusRendered
36+
| TrackErrorRendered;
2837
type InternalEventProperties = EventProperties | TrackDashboardClick | TrackPathChanged;
2938

39+
export interface TrackErrorRendered {
40+
sessionId: string;
41+
instanceId?: string;
42+
workspaceId: string;
43+
type: string;
44+
error: any;
45+
}
46+
47+
export interface TrackStatusRendered {
48+
sessionId: string;
49+
instanceId?: string;
50+
workspaceId: string;
51+
type: string;
52+
phase?: string;
53+
}
54+
55+
export interface TrackWorkspaceClassChanged {}
56+
export interface TrackIDEConfigurationChanged {
57+
location: string;
58+
name?: string;
59+
version?: string;
60+
}
61+
export interface TrackModalDismiss {
62+
manner: string;
63+
title?: string;
64+
specify?: string;
65+
path: string;
66+
}
67+
3068
export interface TrackOrgAuthorised {
3169
installation_id: string;
3270
setup_action: string | undefined;
@@ -75,18 +113,33 @@ interface Traits {
75113
}
76114

77115
//call this to track all events outside of button and anchor clicks
78-
export const trackEvent = (event: Event, properties: EventProperties) => {
116+
export function trackEvent(event: "invite_url_requested", properties: TrackInviteUrlRequested): void;
117+
export function trackEvent(event: "organisation_authorised", properties: TrackOrgAuthorised): void;
118+
export function trackEvent(event: "dotfile_repo_changed", properties: TrackDotfileRepo): void;
119+
export function trackEvent(event: "feedback_submitted", properties: TrackFeedback): void;
120+
export function trackEvent(event: "workspace_class_changed", properties: TrackWorkspaceClassChanged): void;
121+
export function trackEvent(event: "privacy_policy_update_accepted", properties: TrackPolicyUpdateClick): void;
122+
export function trackEvent(event: "modal_dismiss", properties: TrackModalDismiss): void;
123+
export function trackEvent(event: "ide_configuration_changed", properties: TrackIDEConfigurationChanged): void;
124+
export function trackEvent(event: "status_rendered", properties: TrackStatusRendered): void;
125+
export function trackEvent(event: "error_rendered", properties: TrackErrorRendered): void;
126+
export function trackEvent(event: Event, properties: EventProperties): void {
79127
trackEventInternal(event, properties);
80-
};
128+
}
81129

82130
const trackEventInternal = (event: InternalEvent, properties: InternalEventProperties) => {
83-
getGitpodService().server.trackEvent({
131+
sendTrackEvent({
84132
anonymousId: getAnonymousId(),
85133
event,
86134
properties,
87135
});
88136
};
89137

138+
// Please use trackEvent instead of this function
139+
export function sendTrackEvent(message: RemoteTrackMessage): void {
140+
sendAnalytics("trackEvent", message);
141+
}
142+
90143
export const trackButtonOrAnchor = (target: HTMLAnchorElement | HTMLButtonElement | HTMLDivElement) => {
91144
//read manually passed analytics props from 'data-analytics' attribute of event target
92145
let passedProps: TrackDashboardClick | undefined;
@@ -162,7 +215,7 @@ export const trackLocation = async (includePII: boolean) => {
162215
url: window.location.href,
163216
};
164217

165-
getGitpodService().server.trackLocation({
218+
sendAnalytics("trackLocation", {
166219
//if the user is authenticated, let server determine the id. else, pass anonymousId explicitly.
167220
includePII: includePII,
168221
anonymousId: getAnonymousId(),
@@ -171,12 +224,23 @@ export const trackLocation = async (includePII: boolean) => {
171224
};
172225

173226
export const identifyUser = async (traits: Traits) => {
174-
getGitpodService().server.identifyUser({
227+
sendAnalytics("identifyUser", {
175228
anonymousId: getAnonymousId(),
176229
traits: traits,
177230
});
178231
};
179232

233+
function sendAnalytics(operation: "trackEvent" | "trackLocation" | "identifyUser", message: any) {
234+
fetch("/_analytics/" + operation, {
235+
method: "POST",
236+
headers: {
237+
"Content-Type": "application/json",
238+
},
239+
body: JSON.stringify(message),
240+
credentials: "include",
241+
});
242+
}
243+
180244
const getCookieConsent = () => {
181245
return Cookies.get("gp-analytical") === "true";
182246
};

components/dashboard/src/components/Modal.tsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import { Heading2 } from "./typography/headings";
1212
import Alert, { AlertProps } from "./Alert";
1313
import "./modal.css";
1414
import classNames from "classnames";
15-
import { useTrackEvent } from "../data/tracking/track-event-mutation";
1615
import { Button } from "@podkit/buttons/Button";
16+
import { trackEvent } from "../Analytics";
1717

1818
type CloseModalManner = "esc" | "enter" | "x" | "click_outside";
1919

@@ -48,23 +48,18 @@ export const Modal: FC<Props> = ({
4848
onClose,
4949
onSubmit,
5050
}) => {
51-
const trackEvent = useTrackEvent();
52-
5351
const closeModal = useCallback(
5452
(manner: CloseModalManner) => {
5553
onClose();
5654

57-
trackEvent.mutate({
58-
event: "modal_dismiss",
59-
properties: {
60-
manner,
61-
title: title,
62-
specify: specify,
63-
path: window.location.pathname,
64-
},
55+
trackEvent("modal_dismiss", {
56+
manner,
57+
title: title,
58+
specify: specify,
59+
path: window.location.pathname,
6560
});
6661
},
67-
[onClose, specify, title, trackEvent],
62+
[onClose, specify, title],
6863
);
6964

7065
const handleClickOutside = useCallback(() => {

components/dashboard/src/data/tracking/track-event-mutation.ts

Lines changed: 0 additions & 17 deletions
This file was deleted.

components/dashboard/src/onboarding/UserOnboarding.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { StepPersonalize } from "./StepPersonalize";
1616
import { useUpdateCurrentUserMutation } from "../data/current-user/update-mutation";
1717
import Alert from "../components/Alert";
1818
import { useConfetti } from "../contexts/ConfettiContext";
19-
import { getGitpodService } from "../service/service";
19+
import { trackEvent } from "../Analytics";
2020

2121
// This param is optionally present to force an onboarding flow
2222
// Can be used if other conditions aren't true, i.e. if user has already onboarded, but we want to force the flow again
@@ -73,12 +73,10 @@ const UserOnboarding: FunctionComponent<Props> = ({ user }) => {
7373
const onboardedUser = await updateUser.mutateAsync(updates);
7474

7575
// TODO: move this into a mutation side effect once we have a specific mutation for updating the IDE (see above TODO)
76-
getGitpodService().server.trackEvent({
77-
event: "ide_configuration_changed",
78-
properties: {
79-
...(onboardedUser?.editorSettings ?? {}),
80-
location: "onboarding",
81-
},
76+
trackEvent("ide_configuration_changed", {
77+
name: onboardedUser.editorSettings?.name,
78+
version: onboardedUser.editorSettings?.version,
79+
location: "onboarding",
8280
});
8381

8482
dropConfetti();

components/dashboard/src/service/service.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { LotsOfRepliesResponse } from "@gitpod/public-api/lib/gitpod/experimenta
2525
import { User } from "@gitpod/public-api/lib/gitpod/v1/user_pb";
2626
import { watchWorkspaceStatus } from "../data/workspaces/listen-to-workspace-ws-messages";
2727
import { Workspace, WorkspaceSpec_WorkspaceType, WorkspaceStatus } from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";
28+
import { sendTrackEvent } from "../Analytics";
2829

2930
export const gitpodHostUrl = new GitpodHostUrl(window.location.toString());
3031

@@ -274,8 +275,7 @@ export class IDEFrontendService implements IDEFrontendDashboardService.IServer {
274275
workspaceId: this.workspaceID,
275276
type: this.latestInfo?.workspaceType,
276277
};
277-
// TODO:
278-
this.service.server.trackEvent(msg);
278+
sendTrackEvent(msg);
279279
}
280280

281281
private activeHeartbeat(): void {

components/dashboard/src/start/StartWorkspace.tsx

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
WorkspaceSpec_WorkspaceType,
3232
} from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";
3333
import { PartialMessage } from "@bufbuild/protobuf";
34+
import { trackEvent } from "../Analytics";
3435

3536
const sessionId = v4();
3637

@@ -172,28 +173,22 @@ export default class StartWorkspace extends React.Component<StartWorkspaceProps,
172173
const oldPhase = prevState.workspace?.status?.phase?.name;
173174
const type = this.state.workspace?.spec?.type === WorkspaceSpec_WorkspaceType.PREBUILD ? "prebuild" : "regular";
174175
if (newPhase !== oldPhase) {
175-
getGitpodService().server.trackEvent({
176-
event: "status_rendered",
177-
properties: {
178-
sessionId,
179-
instanceId: this.state.workspace?.status?.instanceId,
180-
workspaceId: this.props.workspaceId,
181-
type,
182-
phase: newPhase,
183-
},
176+
trackEvent("status_rendered", {
177+
sessionId,
178+
instanceId: this.state.workspace?.status?.instanceId,
179+
workspaceId: this.props.workspaceId,
180+
type,
181+
phase: newPhase ? WorkspacePhase_Phase[newPhase] : undefined,
184182
});
185183
}
186184

187185
if (!!this.state.error && this.state.error !== prevState.error) {
188-
getGitpodService().server.trackEvent({
189-
event: "error_rendered",
190-
properties: {
191-
sessionId,
192-
instanceId: this.state.workspace?.status?.instanceId,
193-
workspaceId: this.props.workspaceId,
194-
type,
195-
error: this.state.error,
196-
},
186+
trackEvent("error_rendered", {
187+
sessionId,
188+
instanceId: this.state.workspace?.status?.instanceId,
189+
workspaceId: this.props.workspaceId,
190+
type,
191+
error: this.state.error,
197192
});
198193
}
199194
}

components/gitpod-protocol/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"@types/jaeger-client": "^3.18.3",
2121
"@types/js-yaml": "^3.10.1",
2222
"@types/mocha": "^10.0.1",
23-
"@types/node": "^16.11.6",
23+
"@types/node": "^18.18.8",
2424
"@types/random-number-csprng": "^1.0.0",
2525
"@types/uuid": "^8.3.1",
2626
"@types/ws": "^5.1.2",
@@ -33,7 +33,6 @@
3333
"rimraf": "^2.6.2",
3434
"ts-node": "^10.4.0",
3535
"typescript": "~4.4.2",
36-
"typescript-json-schema": "^0.50.1",
3736
"typescript-parser": "^2.6.1"
3837
},
3938
"scripts": {

components/gitpod-protocol/src/gitpod-service.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -347,13 +347,6 @@ export interface ProviderRepository {
347347
installationUpdatedAt?: string;
348348
}
349349

350-
export interface ClientHeaderFields {
351-
ip?: string;
352-
userAgent?: string;
353-
dnt?: string;
354-
clientRegion?: string;
355-
}
356-
357350
const WORKSPACE_MAXIMUM_TIMEOUT_HOURS = 24;
358351

359352
export type WorkspaceTimeoutDuration = string;

components/gitpod-protocol/src/metrics.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ export type MetricsRequest = RequestInit & { url: string };
249249
export class MetricsReporter {
250250
private static readonly REPORT_INTERVAL = 10000;
251251

252-
private intervalHandler: NodeJS.Timer | undefined;
252+
private intervalHandler: NodeJS.Timeout | undefined;
253253

254254
private readonly metricsHost: string;
255255

components/gitpod-protocol/src/util/deferred.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export class Deferred<T> {
88
resolve: (value?: T) => void;
99
reject: (err?: any) => void;
1010
isResolved: boolean = false;
11-
timer: NodeJS.Timer;
11+
timer: NodeJS.Timeout;
1212

1313
constructor(timeout?: number) {
1414
if (timeout) {

components/proxy/conf/Caddyfile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,15 @@ https://{$GITPOD_DOMAIN} {
279279
gitpod.analytics
280280
}
281281

282+
@browser_analytics path /_analytics*
283+
handle @browser_analytics {
284+
import compression
285+
286+
reverse_proxy server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:3000 {
287+
import upstream_connection
288+
}
289+
}
290+
282291
@backend_wss {
283292
path /api/gitpod /api/v1
284293
}

components/public-api/typescript-common/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"@types/chai-subset": "^1.3.3",
3333
"@types/glob": "^8.1.0",
3434
"@types/mocha": "^10.0.1",
35-
"@types/node": "^16.11.6",
35+
"@types/node": "^18.18.8",
3636
"chai": "^4.3.4",
3737
"mocha": "^10.2.0",
3838
"typescript": "~4.4.2"

0 commit comments

Comments
 (0)