Skip to content

Commit 5e9759b

Browse files
author
Tim Roes
authored
AnalyticsService event cleanup (#15142)
* AnalyticsService event cleanup * Remove dead generics * Fix broken parameters
1 parent ce2fd86 commit 5e9759b

File tree

24 files changed

+196
-243
lines changed

24 files changed

+196
-243
lines changed

airbyte-webapp/src/components/CreateConnectionContent/CreateConnectionContent.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ import { IDataItem } from "components/base/DropDown/components/Option";
66
import { JobItem } from "components/JobItem/JobItem";
77
import LoadingSchema from "components/LoadingSchema";
88

9+
import { Action, Namespace } from "core/analytics";
910
import { LogsRequestError } from "core/request/LogsRequestError";
11+
import { useAnalyticsService } from "hooks/services/Analytics";
1012
import { useCreateConnection, ValuesProps } from "hooks/services/useConnectionHook";
11-
import { TrackActionLegacyType, TrackActionType, TrackActionNamespace, useTrackAction } from "hooks/useTrackAction";
1213
import ConnectionForm from "views/Connection/ConnectionForm";
1314
import { ConnectionFormProps } from "views/Connection/ConnectionForm/ConnectionForm";
1415
import { FormikConnectionFormValues } from "views/Connection/ConnectionForm/formConfig";
@@ -40,10 +41,7 @@ const CreateConnectionContent: React.FC<CreateConnectionContentProps> = ({
4041
additionBottomControls,
4142
}) => {
4243
const { mutateAsync: createConnection } = useCreateConnection();
43-
const trackNewConnectionAction = useTrackAction(
44-
TrackActionNamespace.CONNECTION,
45-
TrackActionLegacyType.NEW_CONNECTION
46-
);
44+
const analyticsService = useAnalyticsService();
4745

4846
const { schema, isLoading, schemaErrorStatus, catalogId, onDiscoverSchema } = useDiscoverSchema(source.sourceId);
4947

@@ -90,7 +88,8 @@ const CreateConnectionContent: React.FC<CreateConnectionContentProps> = ({
9088
const enabledStreams = connection.syncCatalog.streams.filter((stream) => stream.config?.selected).length;
9189

9290
if (item) {
93-
trackNewConnectionAction("Select a frequency", TrackActionType.FREQUENCY, {
91+
analyticsService.track(Namespace.CONNECTION, Action.FREQUENCY, {
92+
actionDescription: "Frequency selected",
9493
frequency: item.label,
9594
connector_source_definition: source?.sourceName,
9695
connector_source_definition_id: source?.sourceDefinitionId,

airbyte-webapp/src/components/EntityTable/hooks.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { getFrequencyConfig } from "config/utils";
2+
import { Action, Namespace } from "core/analytics";
23
import { buildConnectionUpdate } from "core/domain/connection";
4+
import { useAnalyticsService } from "hooks/services/Analytics";
35
import { useSyncConnection, useUpdateConnection } from "hooks/services/useConnectionHook";
4-
import { TrackActionLegacyType, TrackActionType, TrackActionNamespace, useTrackAction } from "hooks/useTrackAction";
56

67
import { ConnectionStatus, WebBackendConnectionRead } from "../../core/request/AirbyteClient";
78

@@ -11,7 +12,7 @@ const useSyncActions = (): {
1112
} => {
1213
const { mutateAsync: updateConnection } = useUpdateConnection();
1314
const { mutateAsync: syncConnection } = useSyncConnection();
14-
const trackSourceAction = useTrackAction(TrackActionNamespace.CONNECTION, TrackActionLegacyType.SOURCE);
15+
const analyticsService = useAnalyticsService();
1516

1617
const changeStatus = async (connection: WebBackendConnectionRead) => {
1718
await updateConnection(
@@ -24,16 +25,13 @@ const useSyncActions = (): {
2425

2526
const enabledStreams = connection.syncCatalog.streams.filter((stream) => stream.config?.selected).length;
2627

27-
const trackableAction =
28-
connection.status === ConnectionStatus.active ? TrackActionType.DISABLE : TrackActionType.REENABLE;
28+
const trackableAction = connection.status === ConnectionStatus.active ? Action.DISABLE : Action.REENABLE;
2929

30-
const trackableActionString = `${trackableAction} connection`;
31-
32-
trackSourceAction(trackableActionString, trackableAction, {
30+
analyticsService.track(Namespace.CONNECTION, trackableAction, {
3331
frequency: frequency?.type,
3432
connector_source: connection.source?.sourceName,
3533
connector_source_definition_id: connection.source?.sourceDefinitionId,
36-
connector_destination: connection.destination?.name,
34+
connector_destination: connection.destination?.destinationName,
3735
connector_destination_definition_id: connection.destination?.destinationDefinitionId,
3836
available_streams: connection.syncCatalog.streams.length,
3937
enabled_streams: enabledStreams,
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { AnalyticsService } from "./AnalyticsService";
2+
import { Action, Namespace } from "./types";
3+
4+
describe("AnalyticsService", () => {
5+
beforeEach(() => {
6+
window.analytics = {
7+
track: jest.fn(),
8+
alias: jest.fn(),
9+
group: jest.fn(),
10+
identify: jest.fn(),
11+
page: jest.fn(),
12+
reset: jest.fn(),
13+
};
14+
});
15+
16+
it("should send events to segment", () => {
17+
const service = new AnalyticsService({});
18+
service.track(Namespace.CONNECTION, Action.CREATE, {});
19+
expect(window.analytics.track).toHaveBeenCalledWith("Airbyte.UI.Connection.Create", expect.anything());
20+
});
21+
22+
it("should send version and environment for prod", () => {
23+
const service = new AnalyticsService({}, "0.42.13");
24+
service.track(Namespace.CONNECTION, Action.CREATE, {});
25+
expect(window.analytics.track).toHaveBeenCalledWith(
26+
expect.anything(),
27+
expect.objectContaining({ environment: "prod", airbyte_version: "0.42.13" })
28+
);
29+
});
30+
31+
it("should send version and environment for dev", () => {
32+
const service = new AnalyticsService({}, "dev");
33+
service.track(Namespace.CONNECTION, Action.CREATE, {});
34+
expect(window.analytics.track).toHaveBeenCalledWith(
35+
expect.anything(),
36+
expect.objectContaining({ environment: "dev", airbyte_version: "dev" })
37+
);
38+
});
39+
40+
it("should pass parameters to segment event", () => {
41+
const service = new AnalyticsService({});
42+
service.track(Namespace.CONNECTION, Action.CREATE, { actionDescription: "Created new connection" });
43+
expect(window.analytics.track).toHaveBeenCalledWith(
44+
expect.anything(),
45+
expect.objectContaining({ actionDescription: "Created new connection" })
46+
);
47+
});
48+
49+
it("should pass context parameters to segment event", () => {
50+
const service = new AnalyticsService({ context: 42 });
51+
service.track(Namespace.CONNECTION, Action.CREATE, { actionDescription: "Created new connection" });
52+
expect(window.analytics.track).toHaveBeenCalledWith(
53+
expect.anything(),
54+
expect.objectContaining({ actionDescription: "Created new connection", context: 42 })
55+
);
56+
});
57+
});

airbyte-webapp/src/core/analytics/AnalyticsService.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { SegmentAnalytics } from "./types";
1+
import { Action, EventParams, Namespace, SegmentAnalytics } from "./types";
22

33
export class AnalyticsService {
44
constructor(private context: Record<string, unknown>, private version?: string) {}
@@ -11,13 +11,14 @@ export class AnalyticsService {
1111

1212
reset = (): void => this.getSegmentAnalytics()?.reset?.();
1313

14-
track = <P = Record<string, unknown>>(name: string, properties: P): void =>
15-
this.getSegmentAnalytics()?.track?.(name, {
16-
...properties,
14+
track = (namespace: Namespace, action: Action, params: EventParams & { actionDescription?: string }) => {
15+
this.getSegmentAnalytics()?.track(`Airbyte.UI.${namespace}.${action}`, {
16+
...params,
1717
...this.context,
1818
airbyte_version: this.version,
1919
environment: this.version === "dev" ? "dev" : "prod",
2020
});
21+
};
2122

2223
identify = (userId: string, traits: Record<string, unknown> = {}): void => {
2324
this.getSegmentAnalytics()?.identify?.(userId, traits);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { AnalyticsService } from "./AnalyticsService";
2+
export { Namespace, Action } from "./types";
Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
interface SegmentAnalytics {
1+
export interface SegmentAnalytics {
22
page: (name?: string) => void;
33
reset: () => void;
44
alias: (newId: string) => void;
@@ -7,4 +7,33 @@ interface SegmentAnalytics {
77
group: (organisationId: string, traits: Record<string, unknown>) => void;
88
}
99

10-
export type { SegmentAnalytics };
10+
export const enum Namespace {
11+
SOURCE = "Source",
12+
DESTINATION = "Destination",
13+
CONNECTION = "Connection",
14+
CONNECTOR = "Connector",
15+
ONBOARDING = "Onboarding",
16+
USER = "User",
17+
}
18+
19+
export const enum Action {
20+
CREATE = "Create",
21+
TEST = "Test",
22+
SELECT = "Select",
23+
SUCCESS = "TestSuccess",
24+
FAILURE = "TestFailure",
25+
FREQUENCY = "FrequencySet",
26+
SYNC = "FullRefreshSync",
27+
EDIT_SCHEMA = "EditSchema",
28+
DISABLE = "Disable",
29+
REENABLE = "Reenable",
30+
DELETE = "Delete",
31+
REQUEST = "Request",
32+
SKIP = "Skip",
33+
FEEDBACK = "Feedback",
34+
PREFERENCES = "Preferences",
35+
NO_MATCHING_CONNECTOR = "NoMatchingConnector",
36+
SELECTION_OPENED = "SelectionOpened",
37+
}
38+
39+
export type EventParams = Record<string, unknown>;

airbyte-webapp/src/hooks/services/useConnectionHook.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { QueryClient, useMutation, useQueryClient } from "react-query";
22

33
import { getFrequencyConfig } from "config/utils";
4+
import { Action, Namespace } from "core/analytics";
45
import { SyncSchema } from "core/domain/catalog";
56
import { WebBackendConnectionService } from "core/domain/connection";
67
import { ConnectionService } from "core/domain/connection/ConnectionService";
7-
import { TrackActionLegacyType, useTrackAction, TrackActionNamespace, TrackActionType } from "hooks/useTrackAction";
88
import { useInitService } from "services/useInitService";
99

1010
import { useConfig } from "../../config";
@@ -21,6 +21,7 @@ import {
2121
import { useSuspenseQuery } from "../../services/connector/useSuspenseQuery";
2222
import { SCOPE_WORKSPACE } from "../../services/Scope";
2323
import { useDefaultRequestMiddlewares } from "../../services/useDefaultRequestMiddlewares";
24+
import { useAnalyticsService } from "./Analytics";
2425
import { useCurrentWorkspace } from "./useWorkspace";
2526

2627
export const connectionsKeys = {
@@ -88,15 +89,16 @@ export const useConnectionLoad = (
8889

8990
export const useSyncConnection = () => {
9091
const service = useConnectionService();
91-
const trackSourceAction = useTrackAction(TrackActionNamespace.SOURCE, TrackActionLegacyType.SOURCE);
92+
const analyticsService = useAnalyticsService();
9293

9394
return useMutation((connection: WebBackendConnectionRead) => {
9495
const frequency = getFrequencyConfig(connection.schedule);
9596

96-
trackSourceAction("Full refresh sync", TrackActionType.SYNC, {
97+
analyticsService.track(Namespace.CONNECTION, Action.SYNC, {
98+
actionDescription: "Manual triggered sync",
9799
connector_source: connection.source?.sourceName,
98100
connector_source_definition_id: connection.source?.sourceDefinitionId,
99-
connector_destination: connection.destination?.name,
101+
connector_destination: connection.destination?.destinationName,
100102
connector_destination_definition_id: connection.destination?.destinationDefinitionId,
101103
frequency: frequency?.type,
102104
});
@@ -120,10 +122,7 @@ const useGetConnection = (connectionId: string, options?: { refetchInterval: num
120122
const useCreateConnection = () => {
121123
const service = useWebConnectionService();
122124
const queryClient = useQueryClient();
123-
const trackNewConnectionAction = useTrackAction(
124-
TrackActionNamespace.CONNECTION,
125-
TrackActionLegacyType.NEW_CONNECTION
126-
);
125+
const analyticsService = useAnalyticsService();
127126

128127
return useMutation(
129128
async ({
@@ -146,7 +145,8 @@ const useCreateConnection = () => {
146145

147146
const frequencyData = getFrequencyConfig(values.schedule);
148147

149-
trackNewConnectionAction("Set up connection", TrackActionType.CREATE, {
148+
analyticsService.track(Namespace.CONNECTION, Action.CREATE, {
149+
actionDescription: "New connection created",
150150
frequency: frequencyData?.type || "",
151151
connector_source_definition: source?.sourceName,
152152
connector_source_definition_id: sourceDefinition?.sourceDefinitionId,

airbyte-webapp/src/hooks/services/useDestinationHook.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { useMutation, useQueryClient } from "react-query";
22

3+
import { Action, Namespace } from "core/analytics";
34
import { ConnectionConfiguration } from "core/domain/connection";
45
import { DestinationService } from "core/domain/connector/DestinationService";
5-
import { TrackActionLegacyType, TrackActionType, TrackActionNamespace, useTrackAction } from "hooks/useTrackAction";
66
import { useInitService } from "services/useInitService";
77
import { isDefined } from "utils/common";
88

@@ -11,6 +11,7 @@ import { DestinationRead, WebBackendConnectionRead } from "../../core/request/Ai
1111
import { useSuspenseQuery } from "../../services/connector/useSuspenseQuery";
1212
import { SCOPE_WORKSPACE } from "../../services/Scope";
1313
import { useDefaultRequestMiddlewares } from "../../services/useDefaultRequestMiddlewares";
14+
import { useAnalyticsService } from "./Analytics";
1415
import { connectionsKeys, ListConnection } from "./useConnectionHook";
1516
import { useCurrentWorkspace } from "./useWorkspace";
1617

@@ -92,14 +93,15 @@ const useCreateDestination = () => {
9293
const useDeleteDestination = () => {
9394
const service = useDestinationService();
9495
const queryClient = useQueryClient();
95-
const trackDestinationAction = useTrackAction(TrackActionNamespace.DESTINATION, TrackActionLegacyType.SOURCE);
96+
const analyticsService = useAnalyticsService();
9697

9798
return useMutation(
9899
(payload: { destination: DestinationRead; connectionsWithDestination: WebBackendConnectionRead[] }) =>
99100
service.delete(payload.destination.destinationId),
100101
{
101102
onSuccess: (_data, ctx) => {
102-
trackDestinationAction("Delete destination", TrackActionType.DELETE, {
103+
analyticsService.track(Namespace.DESTINATION, Action.DELETE, {
104+
actionDescription: "Destination deleted",
103105
connector_destination: ctx.destination.destinationName,
104106
connector_destination_definition_id: ctx.destination.destinationDefinitionId,
105107
});

airbyte-webapp/src/hooks/services/useRequestConnector.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Action, Namespace } from "core/analytics";
12
import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService";
23

34
interface Values {
@@ -13,7 +14,8 @@ const useRequestConnector = (): {
1314
const analyticsService = useAnalyticsService();
1415

1516
const requestConnector = (values: Values) => {
16-
analyticsService.track("Request a Connector", {
17+
analyticsService.track(Namespace.CONNECTOR, Action.REQUEST, {
18+
actionDescription: "Request new connector",
1719
email: values.email,
1820
// This parameter has a legacy name from when it was only the webpage, but we wanted to keep the parameter
1921
// name the same after renaming the field to additional information

airbyte-webapp/src/hooks/services/useSourceHook.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@ import { useCallback, useEffect, useState } from "react";
22
import { useMutation, useQueryClient } from "react-query";
33

44
import { useConfig } from "config";
5+
import { Action, Namespace } from "core/analytics";
56
import { SyncSchema } from "core/domain/catalog";
67
import { ConnectionConfiguration } from "core/domain/connection";
78
import { SourceService } from "core/domain/connector/SourceService";
89
import { JobInfo } from "core/domain/job";
9-
import { TrackActionLegacyType, TrackActionType, TrackActionNamespace, useTrackAction } from "hooks/useTrackAction";
1010
import { useInitService } from "services/useInitService";
1111
import { isDefined } from "utils/common";
1212

1313
import { SourceRead, SynchronousJobRead, WebBackendConnectionRead } from "../../core/request/AirbyteClient";
1414
import { useSuspenseQuery } from "../../services/connector/useSuspenseQuery";
1515
import { SCOPE_WORKSPACE } from "../../services/Scope";
1616
import { useDefaultRequestMiddlewares } from "../../services/useDefaultRequestMiddlewares";
17+
import { useAnalyticsService } from "./Analytics";
1718
import { connectionsKeys, ListConnection } from "./useConnectionHook";
1819
import { useCurrentWorkspace } from "./useWorkspace";
1920

@@ -98,14 +99,15 @@ const useCreateSource = () => {
9899
const useDeleteSource = () => {
99100
const service = useSourceService();
100101
const queryClient = useQueryClient();
101-
const trackSourceAction = useTrackAction(TrackActionNamespace.SOURCE, TrackActionLegacyType.SOURCE);
102+
const analyticsService = useAnalyticsService();
102103

103104
return useMutation(
104105
(payload: { source: SourceRead; connectionsWithSource: WebBackendConnectionRead[] }) =>
105106
service.delete(payload.source.sourceId),
106107
{
107108
onSuccess: (_data, ctx) => {
108-
trackSourceAction("Delete source", TrackActionType.DELETE, {
109+
analyticsService.track(Namespace.SOURCE, Action.DELETE, {
110+
actionDescription: "Source deleted",
109111
connector_source: ctx.source.sourceName,
110112
connector_source_definition_id: ctx.source.sourceDefinitionId,
111113
});

0 commit comments

Comments
 (0)