From 16bb1f22e43a85408292b7d57c9e6b1e9b6e19d3 Mon Sep 17 00:00:00 2001
From: Ana Sofia Marin Alexandre <61988046+anamarn@users.noreply.github.com>
Date: Thu, 26 Sep 2024 10:53:10 +0200
Subject: [PATCH] Connect EventTracker to TB endpoint (#7240)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
#7091
EventTrackers send information of events to the TinyBird instance:
In order to test:
1. Set ANALYTICS_ENABLED= true and TELEMETRY_ENABLED=true in
evironment-variables.ts
2. Set the TINYBIRD_TOKEN in environment variables (go to TiniyBird
Tokens)
3. Log in to twenty's TinyBird and go to datasources/analytics_events in
twenty_analytics workspace
4. Run twenty and navigate it
5. New events will be logged in the datasources, containing their
timestamp, sessionId and payload.
Example of payload when user is not logged in
```
{"hostName":"localhost",
"pathname":"/welcome",
"locale":"en-US",
"userAgent":"Mozilla/5.0",
"href":"http://localhost:3001/welcome",
"referrer":"",
"timeZone":"Europe/Barcelona"}
```
Example of payload when user is logged in
```
{"userId":"2020202",
"workspaceId":"202",
"workspaceDisplayName":"Apple",
"workspaceDomainName":"apple.dev",
"hostName":"localhost",
"pathname":"/objects/companies",
"locale":"en-US",
"userAgent":"Mozilla/5.0Chrome/128.0.0.0Safari/537.36",
"href":"http://localhost:3001/objects/companies",
"referrer":"",
"timeZone":"Europe/Paris"}
```
---------
Co-authored-by: FĂ©lix Malfait
---
.../effect-components/PageChangeEffect.tsx | 15 +++--
.../src/generated-metadata/graphql.ts | 6 --
.../twenty-front/src/generated/graphql.tsx | 26 +++------
.../analytics/graphql/queries/track.ts | 4 +-
.../hooks/__tests__/useEventTracker.test.tsx | 28 ++++++++--
.../analytics/hooks/useEventTracker.ts | 52 +++++++++++-------
.../hooks/__tests__/useApolloFactory.test.tsx | 6 +-
.../services/__tests__/apollo.factory.test.ts | 4 +-
.../auth/hooks/__test__/useAuth.test.tsx | 8 +--
.../src/modules/auth/hooks/useAuth.ts | 3 -
.../components/ClientConfigProviderEffect.tsx | 4 --
.../graphql/queries/getClientConfig.ts | 3 -
.../client-config/states/telemetryState.ts | 8 ---
.../src/testing/mock-data/config.ts | 4 --
.../listeners/telemetry.listener.ts | 39 +++++++------
.../workspace-query-runner.module.ts | 2 +
.../analytics/analytics.module.ts | 8 ++-
.../analytics/analytics.resolver.ts | 3 -
.../analytics/analytics.service.ts | 51 +++++++++++------
.../analytics/dtos/create-analytics.input.ts | 8 +--
.../client-config/client-config.entity.ts | 3 -
.../client-config/client-config.resolver.ts | 5 +-
.../engine/core-modules/core-engine.module.ts | 54 +++++++++---------
.../environment/environment-variables.ts | 23 +++++---
.../telemetry/telemetry.module.ts | 15 +++++
.../telemetry/telemetry.service.ts | 55 +++++++++++++++++++
.../user-workspace/user-workspace.service.ts | 16 +++---
.../services/messaging-telemetry.service.ts | 7 +--
28 files changed, 273 insertions(+), 187 deletions(-)
delete mode 100644 packages/twenty-front/src/modules/client-config/states/telemetryState.ts
create mode 100644 packages/twenty-server/src/engine/core-modules/telemetry/telemetry.module.ts
create mode 100644 packages/twenty-server/src/engine/core-modules/telemetry/telemetry.service.ts
diff --git a/packages/twenty-front/src/effect-components/PageChangeEffect.tsx b/packages/twenty-front/src/effect-components/PageChangeEffect.tsx
index bf083a157e..05c99cc89d 100644
--- a/packages/twenty-front/src/effect-components/PageChangeEffect.tsx
+++ b/packages/twenty-front/src/effect-components/PageChangeEffect.tsx
@@ -4,7 +4,10 @@ import { useRecoilValue } from 'recoil';
import { IconCheckbox } from 'twenty-ui';
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
-import { useEventTracker } from '@/analytics/hooks/useEventTracker';
+import {
+ setSessionId,
+ useEventTracker,
+} from '@/analytics/hooks/useEventTracker';
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
import { isCaptchaScriptLoadedState } from '@/captcha/states/isCaptchaScriptLoadedState';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
@@ -163,10 +166,14 @@ export const PageChangeEffect = () => {
useEffect(() => {
setTimeout(() => {
+ setSessionId();
eventTracker('pageview', {
- location: {
- pathname: location.pathname,
- },
+ pathname: location.pathname,
+ locale: navigator.language,
+ userAgent: window.navigator.userAgent,
+ href: window.location.href,
+ referrer: document.referrer,
+ timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
});
}, 500);
}, [eventTracker, location.pathname]);
diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts
index ce122e418c..09fac64ea5 100644
--- a/packages/twenty-front/src/generated-metadata/graphql.ts
+++ b/packages/twenty-front/src/generated-metadata/graphql.ts
@@ -165,7 +165,6 @@ export type ClientConfig = {
signInPrefilled: Scalars['Boolean']['output'];
signUpDisabled: Scalars['Boolean']['output'];
support: Support;
- telemetry: Telemetry;
};
export type CreateAppTokenInput = {
@@ -1171,11 +1170,6 @@ export type Support = {
supportFrontChatId?: Maybe;
};
-export type Telemetry = {
- __typename?: 'Telemetry';
- enabled: Scalars['Boolean']['output'];
-};
-
export type TimelineCalendarEvent = {
__typename?: 'TimelineCalendarEvent';
conferenceLink: LinksMetadata;
diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx
index 15c8a28790..02d20ed6fd 100644
--- a/packages/twenty-front/src/generated/graphql.tsx
+++ b/packages/twenty-front/src/generated/graphql.tsx
@@ -1,5 +1,5 @@
-import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
+import { gql } from '@apollo/client';
export type Maybe = T | null;
export type InputMaybe = Maybe;
export type Exact = { [K in keyof T]: T[K] };
@@ -158,7 +158,6 @@ export type ClientConfig = {
signInPrefilled: Scalars['Boolean'];
signUpDisabled: Scalars['Boolean'];
support: Support;
- telemetry: Telemetry;
};
export type CreateServerlessFunctionFromFileInput = {
@@ -524,6 +523,7 @@ export type MutationSignUpArgs = {
export type MutationTrackArgs = {
data: Scalars['JSON'];
+ sessionId: Scalars['String'];
type: Scalars['String'];
};
@@ -919,11 +919,6 @@ export type Support = {
supportFrontChatId?: Maybe;
};
-export type Telemetry = {
- __typename?: 'Telemetry';
- enabled: Scalars['Boolean'];
-};
-
export type TimelineCalendarEvent = {
__typename?: 'TimelineCalendarEvent';
conferenceLink: LinksMetadata;
@@ -1354,8 +1349,8 @@ export type GetTimelineThreadsFromPersonIdQueryVariables = Exact<{
export type GetTimelineThreadsFromPersonIdQuery = { __typename?: 'Query', getTimelineThreadsFromPersonId: { __typename?: 'TimelineThreadsWithTotal', totalNumberOfThreads: number, timelineThreads: Array<{ __typename?: 'TimelineThread', id: any, read: boolean, visibility: MessageChannelVisibility, lastMessageReceivedAt: string, lastMessageBody: string, subject: string, numberOfMessagesInThread: number, participantCount: number, firstParticipant: { __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }, lastTwoParticipants: Array<{ __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> } };
export type TrackMutationVariables = Exact<{
- type: Scalars['String'];
- data: Scalars['JSON'];
+ action: Scalars['String'];
+ payload: Scalars['JSON'];
}>;
@@ -1511,7 +1506,7 @@ export type UpdateBillingSubscriptionMutation = { __typename?: 'Mutation', updat
export type GetClientConfigQueryVariables = Exact<{ [key: string]: never; }>;
-export type GetClientConfigQuery = { __typename?: 'Query', clientConfig: { __typename?: 'ClientConfig', signInPrefilled: boolean, signUpDisabled: boolean, debugMode: boolean, chromeExtensionId?: string | null, authProviders: { __typename?: 'AuthProviders', google: boolean, password: boolean, microsoft: boolean }, billing: { __typename?: 'Billing', isBillingEnabled: boolean, billingUrl?: string | null, billingFreeTrialDurationInDays?: number | null }, telemetry: { __typename?: 'Telemetry', enabled: boolean }, support: { __typename?: 'Support', supportDriver: string, supportFrontChatId?: string | null }, sentry: { __typename?: 'Sentry', dsn?: string | null, environment?: string | null, release?: string | null }, captcha: { __typename?: 'Captcha', provider?: CaptchaDriverType | null, siteKey?: string | null }, api: { __typename?: 'ApiConfig', mutationMaximumAffectedRecords: number } } };
+export type GetClientConfigQuery = { __typename?: 'Query', clientConfig: { __typename?: 'ClientConfig', signInPrefilled: boolean, signUpDisabled: boolean, debugMode: boolean, chromeExtensionId?: string | null, authProviders: { __typename?: 'AuthProviders', google: boolean, password: boolean, microsoft: boolean }, billing: { __typename?: 'Billing', isBillingEnabled: boolean, billingUrl?: string | null, billingFreeTrialDurationInDays?: number | null }, support: { __typename?: 'Support', supportDriver: string, supportFrontChatId?: string | null }, sentry: { __typename?: 'Sentry', dsn?: string | null, environment?: string | null, release?: string | null }, captcha: { __typename?: 'Captcha', provider?: CaptchaDriverType | null, siteKey?: string | null }, api: { __typename?: 'ApiConfig', mutationMaximumAffectedRecords: number } } };
export type SkipSyncEmailOnboardingStepMutationVariables = Exact<{ [key: string]: never; }>;
@@ -1948,8 +1943,8 @@ export type GetTimelineThreadsFromPersonIdQueryHookResult = ReturnType;
export type GetTimelineThreadsFromPersonIdQueryResult = Apollo.QueryResult;
export const TrackDocument = gql`
- mutation Track($type: String!, $data: JSON!) {
- track(type: $type, data: $data) {
+ mutation Track($action: String!, $payload: JSON!) {
+ track(action: $action, payload: $payload) {
success
}
}
@@ -1969,8 +1964,8 @@ export type TrackMutationFn = Apollo.MutationFunction ({
@@ -43,7 +51,15 @@ const Wrapper = ({ children }: { children: ReactNode }) => (
describe('useEventTracker', () => {
it('should make the call to track the event', async () => {
const eventType = 'exampleType';
- const eventData = { location: { pathname: '/examplePath' } };
+ const eventData = {
+ sessionId: 'exampleId',
+ pathname: '',
+ userAgent: '',
+ timeZone: '',
+ locale: '',
+ href: '',
+ referrer: '',
+ };
const { result } = renderHook(() => useEventTracker(), {
wrapper: Wrapper,
});
diff --git a/packages/twenty-front/src/modules/analytics/hooks/useEventTracker.ts b/packages/twenty-front/src/modules/analytics/hooks/useEventTracker.ts
index 88d1d65674..faaa3ea5b6 100644
--- a/packages/twenty-front/src/modules/analytics/hooks/useEventTracker.ts
+++ b/packages/twenty-front/src/modules/analytics/hooks/useEventTracker.ts
@@ -1,32 +1,46 @@
import { useCallback } from 'react';
-import { useRecoilValue } from 'recoil';
-
-import { telemetryState } from '@/client-config/states/telemetryState';
import { useTrackMutation } from '~/generated/graphql';
-
-interface EventLocation {
- pathname: string;
-}
-
export interface EventData {
- location: EventLocation;
+ pathname: string;
+ userAgent: string;
+ timeZone: string;
+ locale: string;
+ href: string;
+ referrer: string;
}
+export const ANALYTICS_COOKIE_NAME = 'analyticsCookie';
+export const getSessionId = (): string => {
+ const cookie: { [key: string]: string } = {};
+ document.cookie.split(';').forEach((el) => {
+ const [key, value] = el.split('=');
+ cookie[key.trim()] = value;
+ });
+ return cookie[ANALYTICS_COOKIE_NAME];
+};
+
+export const setSessionId = (domain?: string): void => {
+ const sessionId = getSessionId() || crypto.randomUUID();
+ const baseCookie = `${ANALYTICS_COOKIE_NAME}=${sessionId}; Max-Age=1800; path=/; secure`;
+ const cookie = domain ? baseCookie + `; domain=${domain}` : baseCookie;
+
+ document.cookie = cookie;
+};
export const useEventTracker = () => {
- const telemetry = useRecoilValue(telemetryState);
const [createEventMutation] = useTrackMutation();
return useCallback(
- (eventType: string, eventData: EventData) => {
- if (telemetry.enabled) {
- createEventMutation({
- variables: {
- type: eventType,
- data: eventData,
+ (eventAction: string, eventPayload: EventData) => {
+ createEventMutation({
+ variables: {
+ action: eventAction,
+ payload: {
+ sessionId: getSessionId(),
+ ...eventPayload,
},
- });
- }
+ },
+ });
},
- [createEventMutation, telemetry],
+ [createEventMutation],
);
};
diff --git a/packages/twenty-front/src/modules/apollo/hooks/__tests__/useApolloFactory.test.tsx b/packages/twenty-front/src/modules/apollo/hooks/__tests__/useApolloFactory.test.tsx
index 59f99306a5..5e19a8309c 100644
--- a/packages/twenty-front/src/modules/apollo/hooks/__tests__/useApolloFactory.test.tsx
+++ b/packages/twenty-front/src/modules/apollo/hooks/__tests__/useApolloFactory.test.tsx
@@ -1,7 +1,7 @@
-import { MemoryRouter, useLocation } from 'react-router-dom';
import { ApolloError, gql } from '@apollo/client';
import { act, renderHook } from '@testing-library/react';
import fetchMock, { enableFetchMocks } from 'jest-fetch-mock';
+import { MemoryRouter, useLocation } from 'react-router-dom';
import { RecoilRoot } from 'recoil';
import { useApolloFactory } from '../useApolloFactory';
@@ -77,8 +77,8 @@ describe('useApolloFactory', () => {
await act(async () => {
await result.current.factory.mutate({
mutation: gql`
- mutation Track($type: String!, $data: JSON!) {
- track(type: $type, data: $data) {
+ mutation Track($type: String!, $sessionId: String!, $data: JSON!) {
+ track(type: $type, sessionId: $sessionId, data: $data) {
success
}
}
diff --git a/packages/twenty-front/src/modules/apollo/services/__tests__/apollo.factory.test.ts b/packages/twenty-front/src/modules/apollo/services/__tests__/apollo.factory.test.ts
index d0ba375124..9136b83fcd 100644
--- a/packages/twenty-front/src/modules/apollo/services/__tests__/apollo.factory.test.ts
+++ b/packages/twenty-front/src/modules/apollo/services/__tests__/apollo.factory.test.ts
@@ -41,8 +41,8 @@ const makeRequest = async () => {
await client.mutate({
mutation: gql`
- mutation Track($type: String!, $data: JSON!) {
- track(type: $type, data: $data) {
+ mutation Track($type: String!, $sessionId: String!, $data: JSON!) {
+ track(type: $type, sessionId: $sessionId, data: $data) {
success
}
}
diff --git a/packages/twenty-front/src/modules/auth/hooks/__test__/useAuth.test.tsx b/packages/twenty-front/src/modules/auth/hooks/__test__/useAuth.test.tsx
index ac52204f4c..60e4025a81 100644
--- a/packages/twenty-front/src/modules/auth/hooks/__test__/useAuth.test.tsx
+++ b/packages/twenty-front/src/modules/auth/hooks/__test__/useAuth.test.tsx
@@ -1,8 +1,8 @@
-import { ReactNode } from 'react';
import { useApolloClient } from '@apollo/client';
import { MockedProvider } from '@apollo/client/testing';
import { expect } from '@storybook/test';
import { act, renderHook } from '@testing-library/react';
+import { ReactNode } from 'react';
import { RecoilRoot, useRecoilValue } from 'recoil';
import { iconsState } from 'twenty-ui';
@@ -12,7 +12,6 @@ import { billingState } from '@/client-config/states/billingState';
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
import { isSignInPrefilledState } from '@/client-config/states/isSignInPrefilledState';
import { supportChatState } from '@/client-config/states/supportChatState';
-import { telemetryState } from '@/client-config/states/telemetryState';
import { email, mocks, password, results, token } from '../__mocks__/useAuth';
@@ -81,7 +80,6 @@ describe('useAuth', () => {
const billing = useRecoilValue(billingState);
const isSignInPrefilled = useRecoilValue(isSignInPrefilledState);
const supportChat = useRecoilValue(supportChatState);
- const telemetry = useRecoilValue(telemetryState);
const isDebugMode = useRecoilValue(isDebugModeState);
return {
...useAuth(),
@@ -92,7 +90,6 @@ describe('useAuth', () => {
billing,
isSignInPrefilled,
supportChat,
- telemetry,
isDebugMode,
},
};
@@ -126,9 +123,6 @@ describe('useAuth', () => {
supportDriver: 'none',
supportFrontChatId: null,
});
- expect(state.telemetry).toEqual({
- enabled: true,
- });
expect(state.isDebugMode).toBe(false);
});
diff --git a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts
index 677932161d..7a7de0807f 100644
--- a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts
+++ b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts
@@ -21,7 +21,6 @@ import { isClientConfigLoadedState } from '@/client-config/states/isClientConfig
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
import { isSignInPrefilledState } from '@/client-config/states/isSignInPrefilledState';
import { supportChatState } from '@/client-config/states/supportChatState';
-import { telemetryState } from '@/client-config/states/telemetryState';
import { ColorScheme } from '@/workspace-member/types/WorkspaceMember';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import {
@@ -224,7 +223,6 @@ export const useAuth = () => {
.getLoadable(isSignInPrefilledState)
.getValue();
const supportChat = snapshot.getLoadable(supportChatState).getValue();
- const telemetry = snapshot.getLoadable(telemetryState).getValue();
const isDebugMode = snapshot.getLoadable(isDebugModeState).getValue();
const captchaProvider = snapshot
.getLoadable(captchaProviderState)
@@ -242,7 +240,6 @@ export const useAuth = () => {
set(billingState, billing);
set(isSignInPrefilledState, isSignInPrefilled);
set(supportChatState, supportChat);
- set(telemetryState, telemetry);
set(isDebugModeState, isDebugMode);
set(captchaProviderState, captchaProvider);
set(isClientConfigLoadedState, isClientConfigLoaded);
diff --git a/packages/twenty-front/src/modules/client-config/components/ClientConfigProviderEffect.tsx b/packages/twenty-front/src/modules/client-config/components/ClientConfigProviderEffect.tsx
index 8bec4cc7db..9eccbeb98e 100644
--- a/packages/twenty-front/src/modules/client-config/components/ClientConfigProviderEffect.tsx
+++ b/packages/twenty-front/src/modules/client-config/components/ClientConfigProviderEffect.tsx
@@ -12,7 +12,6 @@ import { isSignInPrefilledState } from '@/client-config/states/isSignInPrefilled
import { isSignUpDisabledState } from '@/client-config/states/isSignUpDisabledState';
import { sentryConfigState } from '@/client-config/states/sentryConfigState';
import { supportChatState } from '@/client-config/states/supportChatState';
-import { telemetryState } from '@/client-config/states/telemetryState';
import { useGetClientConfigQuery } from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';
@@ -24,7 +23,6 @@ export const ClientConfigProviderEffect = () => {
const setIsSignUpDisabled = useSetRecoilState(isSignUpDisabledState);
const setBilling = useSetRecoilState(billingState);
- const setTelemetry = useSetRecoilState(telemetryState);
const setSupportChat = useSetRecoilState(supportChatState);
const setSentryConfig = useSetRecoilState(sentryConfigState);
@@ -56,7 +54,6 @@ export const ClientConfigProviderEffect = () => {
setIsSignUpDisabled(data?.clientConfig.signUpDisabled);
setBilling(data?.clientConfig.billing);
- setTelemetry(data?.clientConfig.telemetry);
setSupportChat(data?.clientConfig.support);
setSentryConfig({
@@ -79,7 +76,6 @@ export const ClientConfigProviderEffect = () => {
setIsDebugMode,
setIsSignInPrefilled,
setIsSignUpDisabled,
- setTelemetry,
setSupportChat,
setBilling,
setSentryConfig,
diff --git a/packages/twenty-front/src/modules/client-config/graphql/queries/getClientConfig.ts b/packages/twenty-front/src/modules/client-config/graphql/queries/getClientConfig.ts
index 3143bbc5f6..e702acefa4 100644
--- a/packages/twenty-front/src/modules/client-config/graphql/queries/getClientConfig.ts
+++ b/packages/twenty-front/src/modules/client-config/graphql/queries/getClientConfig.ts
@@ -16,9 +16,6 @@ export const GET_CLIENT_CONFIG = gql`
signInPrefilled
signUpDisabled
debugMode
- telemetry {
- enabled
- }
support {
supportDriver
supportFrontChatId
diff --git a/packages/twenty-front/src/modules/client-config/states/telemetryState.ts b/packages/twenty-front/src/modules/client-config/states/telemetryState.ts
deleted file mode 100644
index f074ad218d..0000000000
--- a/packages/twenty-front/src/modules/client-config/states/telemetryState.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { createState } from 'twenty-ui';
-
-import { Telemetry } from '~/generated/graphql';
-
-export const telemetryState = createState({
- key: 'telemetryState',
- defaultValue: { enabled: true },
-});
diff --git a/packages/twenty-front/src/testing/mock-data/config.ts b/packages/twenty-front/src/testing/mock-data/config.ts
index 656dcbb80b..1ed65869a7 100644
--- a/packages/twenty-front/src/testing/mock-data/config.ts
+++ b/packages/twenty-front/src/testing/mock-data/config.ts
@@ -13,10 +13,6 @@ export const mockedClientConfig: ClientConfig = {
microsoft: false,
__typename: 'AuthProviders',
},
- telemetry: {
- enabled: false,
- __typename: 'Telemetry',
- },
support: {
supportDriver: 'front',
supportFrontChatId: null,
diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/listeners/telemetry.listener.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/listeners/telemetry.listener.ts
index 2afa537a68..f627caf47a 100644
--- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/listeners/telemetry.listener.ts
+++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/listeners/telemetry.listener.ts
@@ -2,15 +2,15 @@ import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { AnalyticsService } from 'src/engine/core-modules/analytics/analytics.service';
-import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event';
+import { TelemetryService } from 'src/engine/core-modules/telemetry/telemetry.service';
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/workspace-event.type';
@Injectable()
export class TelemetryListener {
constructor(
private readonly analyticsService: AnalyticsService,
- private readonly environmentService: EnvironmentService,
+ private readonly telemetryService: TelemetryService,
) {}
@OnEvent('*.created')
@@ -21,16 +21,11 @@ export class TelemetryListener {
payload.events.map((eventPayload) =>
this.analyticsService.create(
{
- type: 'track',
- data: {
- eventName: payload.name,
- },
+ action: payload.name,
+ payload: {},
},
eventPayload.userId,
payload.workspaceId,
- '', // voluntarily not retrieving this
- '', // to avoid slowing down
- this.environmentService.get('SERVER_URL'),
),
),
);
@@ -41,21 +36,29 @@ export class TelemetryListener {
payload: WorkspaceEventBatch>,
) {
await Promise.all(
- payload.events.map((eventPayload) =>
+ payload.events.map(async (eventPayload) => {
this.analyticsService.create(
{
- type: 'track',
- data: {
- eventName: 'user.signup',
+ action: 'user.signup',
+ payload: {},
+ },
+ eventPayload.userId,
+ payload.workspaceId,
+ );
+
+ this.telemetryService.create(
+ {
+ action: 'user.signup',
+ payload: {
+ payload,
+ userId: undefined,
+ workspaceId: undefined,
},
},
eventPayload.userId,
payload.workspaceId,
- '',
- '',
- this.environmentService.get('SERVER_URL'),
- ),
- ),
+ );
+ }),
);
}
}
diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module.ts
index 5d1771aad8..1e166806be 100644
--- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module.ts
+++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module.ts
@@ -12,6 +12,7 @@ import { DuplicateModule } from 'src/engine/core-modules/duplicate/duplicate.mod
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { FileModule } from 'src/engine/core-modules/file/file.module';
+import { TelemetryModule } from 'src/engine/core-modules/telemetry/telemetry.module';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
@@ -29,6 +30,7 @@ import { EntityEventsToDbListener } from './listeners/entity-events-to-db.listen
ObjectMetadataRepositoryModule.forFeature([WorkspaceMemberWorkspaceEntity]),
TypeOrmModule.forFeature([FeatureFlagEntity], 'core'),
AnalyticsModule,
+ TelemetryModule,
DuplicateModule,
FileModule,
FeatureFlagModule,
diff --git a/packages/twenty-server/src/engine/core-modules/analytics/analytics.module.ts b/packages/twenty-server/src/engine/core-modules/analytics/analytics.module.ts
index 7ce66bafdd..2b4c8705d6 100644
--- a/packages/twenty-server/src/engine/core-modules/analytics/analytics.module.ts
+++ b/packages/twenty-server/src/engine/core-modules/analytics/analytics.module.ts
@@ -1,14 +1,16 @@
-import { Module } from '@nestjs/common';
import { HttpModule } from '@nestjs/axios';
+import { Module } from '@nestjs/common';
-import { AnalyticsService } from './analytics.service';
import { AnalyticsResolver } from './analytics.resolver';
+import { AnalyticsService } from './analytics.service';
+
+const TINYBIRD_BASE_URL = 'https://api.eu-central-1.aws.tinybird.co/v0';
@Module({
providers: [AnalyticsResolver, AnalyticsService],
imports: [
HttpModule.register({
- baseURL: 'https://t.twenty.com/api/v1/s2s',
+ baseURL: TINYBIRD_BASE_URL,
}),
],
exports: [AnalyticsService],
diff --git a/packages/twenty-server/src/engine/core-modules/analytics/analytics.resolver.ts b/packages/twenty-server/src/engine/core-modules/analytics/analytics.resolver.ts
index 06cd9aabed..f64d108fcd 100644
--- a/packages/twenty-server/src/engine/core-modules/analytics/analytics.resolver.ts
+++ b/packages/twenty-server/src/engine/core-modules/analytics/analytics.resolver.ts
@@ -31,9 +31,6 @@ export class AnalyticsResolver {
createAnalyticsInput,
user?.id,
workspace?.id,
- workspace?.displayName,
- workspace?.domainName,
- this.environmentService.get('SERVER_URL') ?? request.hostname,
);
}
}
diff --git a/packages/twenty-server/src/engine/core-modules/analytics/analytics.service.ts b/packages/twenty-server/src/engine/core-modules/analytics/analytics.service.ts
index 085f443783..3e8ca56d22 100644
--- a/packages/twenty-server/src/engine/core-modules/analytics/analytics.service.ts
+++ b/packages/twenty-server/src/engine/core-modules/analytics/analytics.service.ts
@@ -1,16 +1,19 @@
-import { Injectable, Logger } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
+import { Injectable, Logger } from '@nestjs/common';
+
+import { AxiosRequestConfig } from 'axios';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
type CreateEventInput = {
- type: string;
- data: object;
+ action: string;
+ payload: object;
};
@Injectable()
export class AnalyticsService {
private readonly logger = new Logger(AnalyticsService.name);
+ private readonly datasource = 'event';
constructor(
private readonly environmentService: EnvironmentService,
@@ -21,30 +24,42 @@ export class AnalyticsService {
createEventInput: CreateEventInput,
userId: string | null | undefined,
workspaceId: string | null | undefined,
- workspaceDisplayName: string | undefined,
- workspaceDomainName: string | undefined,
- hostName: string | undefined,
) {
- if (!this.environmentService.get('TELEMETRY_ENABLED')) {
+ if (this.environmentService.get('ANALYTICS_ENABLED')) {
return { success: true };
}
const data = {
- type: createEventInput.type,
- data: {
- hostname: hostName,
- userUUID: userId,
- workspaceUUID: workspaceId,
- workspaceDisplayName: workspaceDisplayName,
- workspaceDomainName: workspaceDomainName,
- ...createEventInput.data,
+ action: createEventInput.action,
+ timestamp: new Date().toISOString(),
+ version: '1',
+ payload: {
+ userId: userId,
+ workspaceId: workspaceId,
+ ...createEventInput.payload,
+ },
+ };
+
+ const config: AxiosRequestConfig = {
+ headers: {
+ Authorization:
+ 'Bearer ' + this.environmentService.get('TINYBIRD_TOKEN'),
},
};
try {
- await this.httpService.axiosRef.post('/v1', data);
- } catch {
- this.logger.error('Failed to send analytics event');
+ await this.httpService.axiosRef.post(
+ `/events?name=${this.datasource}`,
+ data,
+ config,
+ );
+ } catch (error) {
+ this.logger.error('Error occurred:', error);
+ if (error.response) {
+ this.logger.error(
+ `Error response body: ${JSON.stringify(error.response.data)}`,
+ );
+ }
return { success: false };
}
diff --git a/packages/twenty-server/src/engine/core-modules/analytics/dtos/create-analytics.input.ts b/packages/twenty-server/src/engine/core-modules/analytics/dtos/create-analytics.input.ts
index a870673fe1..5e887dca05 100644
--- a/packages/twenty-server/src/engine/core-modules/analytics/dtos/create-analytics.input.ts
+++ b/packages/twenty-server/src/engine/core-modules/analytics/dtos/create-analytics.input.ts
@@ -1,16 +1,16 @@
import { ArgsType, Field } from '@nestjs/graphql';
+import { IsNotEmpty, IsObject, IsString } from 'class-validator';
import graphqlTypeJson from 'graphql-type-json';
-import { IsNotEmpty, IsString, IsObject } from 'class-validator';
@ArgsType()
export class CreateAnalyticsInput {
@Field({ description: 'Type of the event' })
@IsNotEmpty()
@IsString()
- type: string;
+ action: string;
- @Field(() => graphqlTypeJson, { description: 'Event data in JSON format' })
+ @Field(() => graphqlTypeJson, { description: 'Event payload in JSON format' })
@IsObject()
- data: JSON;
+ payload: JSON;
}
diff --git a/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts b/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts
index eefb2509be..12cf5c3164 100644
--- a/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts
+++ b/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts
@@ -76,9 +76,6 @@ export class ClientConfig {
@Field(() => AuthProviders, { nullable: false })
authProviders: AuthProviders;
- @Field(() => Telemetry, { nullable: false })
- telemetry: Telemetry;
-
@Field(() => Billing, { nullable: false })
billing: Billing;
diff --git a/packages/twenty-server/src/engine/core-modules/client-config/client-config.resolver.ts b/packages/twenty-server/src/engine/core-modules/client-config/client-config.resolver.ts
index 54844671bb..3615066a43 100644
--- a/packages/twenty-server/src/engine/core-modules/client-config/client-config.resolver.ts
+++ b/packages/twenty-server/src/engine/core-modules/client-config/client-config.resolver.ts
@@ -1,4 +1,4 @@
-import { Resolver, Query } from '@nestjs/graphql';
+import { Query, Resolver } from '@nestjs/graphql';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
@@ -17,9 +17,6 @@ export class ClientConfigResolver {
password: this.environmentService.get('AUTH_PASSWORD_ENABLED'),
microsoft: this.environmentService.get('AUTH_MICROSOFT_ENABLED'),
},
- telemetry: {
- enabled: this.environmentService.get('TELEMETRY_ENABLED'),
- },
billing: {
isBillingEnabled: this.environmentService.get('IS_BILLING_ENABLED'),
billingUrl: this.environmentService.get('BILLING_PLAN_REQUIRED_LINK'),
diff --git a/packages/twenty-server/src/engine/core-modules/core-engine.module.ts b/packages/twenty-server/src/engine/core-modules/core-engine.module.ts
index a78b636e2a..2e2df06c4d 100644
--- a/packages/twenty-server/src/engine/core-modules/core-engine.module.ts
+++ b/packages/twenty-server/src/engine/core-modules/core-engine.module.ts
@@ -7,43 +7,44 @@ import { AISQLQueryModule } from 'src/engine/core-modules/ai-sql-query/ai-sql-qu
import { AppTokenModule } from 'src/engine/core-modules/app-token/app-token.module';
import { AuthModule } from 'src/engine/core-modules/auth/auth.module';
import { BillingModule } from 'src/engine/core-modules/billing/billing.module';
+import { CacheStorageModule } from 'src/engine/core-modules/cache-storage/cache-storage.module';
import { TimelineCalendarEventModule } from 'src/engine/core-modules/calendar/timeline-calendar-event.module';
-import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
-import { HealthModule } from 'src/engine/core-modules/health/health.module';
-import { TimelineMessagingModule } from 'src/engine/core-modules/messaging/timeline-messaging.module';
-import { OpenApiModule } from 'src/engine/core-modules/open-api/open-api.module';
-import { PostgresCredentialsModule } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.module';
-import { UserModule } from 'src/engine/core-modules/user/user.module';
-import { WorkflowTriggerApiModule } from 'src/engine/core-modules/workflow/workflow-trigger-api.module';
-import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.module';
-import { WorkspaceEventEmitterModule } from 'src/engine/workspace-event-emitter/workspace-event-emitter.module';
-import { EnvironmentModule } from 'src/engine/core-modules/environment/environment.module';
-import { FileStorageModule } from 'src/engine/core-modules/file-storage/file-storage.module';
-import { fileStorageModuleFactory } from 'src/engine/core-modules/file-storage/file-storage.module-factory';
-import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
-import { LoggerModule } from 'src/engine/core-modules/logger/logger.module';
-import { loggerModuleFactory } from 'src/engine/core-modules/logger/logger.module-factory';
-import { MessageQueueModule } from 'src/engine/core-modules/message-queue/message-queue.module';
-import { messageQueueModuleFactory } from 'src/engine/core-modules/message-queue/message-queue.module-factory';
-import { ExceptionHandlerModule } from 'src/engine/core-modules/exception-handler/exception-handler.module';
-import { exceptionHandlerModuleFactory } from 'src/engine/core-modules/exception-handler/exception-handler.module-factory';
-import { EmailModule } from 'src/engine/core-modules/email/email.module';
-import { emailModuleFactory } from 'src/engine/core-modules/email/email.module-factory';
import { CaptchaModule } from 'src/engine/core-modules/captcha/captcha.module';
import { captchaModuleFactory } from 'src/engine/core-modules/captcha/captcha.module-factory';
-import { CacheStorageModule } from 'src/engine/core-modules/cache-storage/cache-storage.module';
+import { EmailModule } from 'src/engine/core-modules/email/email.module';
+import { emailModuleFactory } from 'src/engine/core-modules/email/email.module-factory';
+import { EnvironmentModule } from 'src/engine/core-modules/environment/environment.module';
+import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
+import { ExceptionHandlerModule } from 'src/engine/core-modules/exception-handler/exception-handler.module';
+import { exceptionHandlerModuleFactory } from 'src/engine/core-modules/exception-handler/exception-handler.module-factory';
+import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
+import { FileStorageModule } from 'src/engine/core-modules/file-storage/file-storage.module';
+import { fileStorageModuleFactory } from 'src/engine/core-modules/file-storage/file-storage.module-factory';
+import { FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service';
+import { HealthModule } from 'src/engine/core-modules/health/health.module';
import { LLMChatModelModule } from 'src/engine/core-modules/llm-chat-model/llm-chat-model.module';
import { llmChatModelModuleFactory } from 'src/engine/core-modules/llm-chat-model/llm-chat-model.module-factory';
import { LLMTracingModule } from 'src/engine/core-modules/llm-tracing/llm-tracing.module';
import { llmTracingModuleFactory } from 'src/engine/core-modules/llm-tracing/llm-tracing.module-factory';
-import { ServerlessModule } from 'src/engine/core-modules/serverless/serverless.module';
+import { LoggerModule } from 'src/engine/core-modules/logger/logger.module';
+import { loggerModuleFactory } from 'src/engine/core-modules/logger/logger.module-factory';
+import { MessageQueueModule } from 'src/engine/core-modules/message-queue/message-queue.module';
+import { messageQueueModuleFactory } from 'src/engine/core-modules/message-queue/message-queue.module-factory';
+import { TimelineMessagingModule } from 'src/engine/core-modules/messaging/timeline-messaging.module';
+import { OpenApiModule } from 'src/engine/core-modules/open-api/open-api.module';
+import { PostgresCredentialsModule } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.module';
import { serverlessModuleFactory } from 'src/engine/core-modules/serverless/serverless-module.factory';
-import { FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service';
+import { ServerlessModule } from 'src/engine/core-modules/serverless/serverless.module';
+import { TelemetryModule } from 'src/engine/core-modules/telemetry/telemetry.module';
+import { UserModule } from 'src/engine/core-modules/user/user.module';
+import { WorkflowTriggerApiModule } from 'src/engine/core-modules/workflow/workflow-trigger-api.module';
import { WorkspaceInvitationModule } from 'src/engine/core-modules/workspace-invitation/workspace-invitation.module';
+import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.module';
+import { WorkspaceEventEmitterModule } from 'src/engine/workspace-event-emitter/workspace-event-emitter.module';
-import { FileModule } from './file/file.module';
-import { ClientConfigModule } from './client-config/client-config.module';
import { AnalyticsModule } from './analytics/analytics.module';
+import { ClientConfigModule } from './client-config/client-config.module';
+import { FileModule } from './file/file.module';
@Module({
imports: [
@@ -66,6 +67,7 @@ import { AnalyticsModule } from './analytics/analytics.module';
WorkflowTriggerApiModule,
WorkspaceEventEmitterModule,
ActorModule,
+ TelemetryModule,
EnvironmentModule.forRoot({}),
FileStorageModule.forRootAsync({
useFactory: fileStorageModuleFactory,
diff --git a/packages/twenty-server/src/engine/core-modules/environment/environment-variables.ts b/packages/twenty-server/src/engine/core-modules/environment/environment-variables.ts
index c3ecf518a0..4e30e84bd8 100644
--- a/packages/twenty-server/src/engine/core-modules/environment/environment-variables.ts
+++ b/packages/twenty-server/src/engine/core-modules/environment/environment-variables.ts
@@ -16,20 +16,20 @@ import {
} from 'class-validator';
import { EmailDriver } from 'src/engine/core-modules/email/interfaces/email.interface';
+import { AwsRegion } from 'src/engine/core-modules/environment/interfaces/aws-region.interface';
import { NodeEnvironment } from 'src/engine/core-modules/environment/interfaces/node-environment.interface';
+import { SupportDriver } from 'src/engine/core-modules/environment/interfaces/support.interface';
import { LLMChatModelDriver } from 'src/engine/core-modules/llm-chat-model/interfaces/llm-chat-model.interface';
import { LLMTracingDriver } from 'src/engine/core-modules/llm-tracing/interfaces/llm-tracing.interface';
-import { AwsRegion } from 'src/engine/core-modules/environment/interfaces/aws-region.interface';
-import { SupportDriver } from 'src/engine/core-modules/environment/interfaces/support.interface';
-import { IsDuration } from 'src/engine/core-modules/environment/decorators/is-duration.decorator';
-import { IsAWSRegion } from 'src/engine/core-modules/environment/decorators/is-aws-region.decorator';
-import { CastToPositiveNumber } from 'src/engine/core-modules/environment/decorators/cast-to-positive-number.decorator';
-import { CastToLogLevelArray } from 'src/engine/core-modules/environment/decorators/cast-to-log-level-array.decorator';
-import { CastToBoolean } from 'src/engine/core-modules/environment/decorators/cast-to-boolean.decorator';
import { CacheStorageType } from 'src/engine/core-modules/cache-storage/types/cache-storage-type.enum';
import { CaptchaDriverType } from 'src/engine/core-modules/captcha/interfaces';
+import { CastToBoolean } from 'src/engine/core-modules/environment/decorators/cast-to-boolean.decorator';
+import { CastToLogLevelArray } from 'src/engine/core-modules/environment/decorators/cast-to-log-level-array.decorator';
+import { CastToPositiveNumber } from 'src/engine/core-modules/environment/decorators/cast-to-positive-number.decorator';
import { CastToStringArray } from 'src/engine/core-modules/environment/decorators/cast-to-string-array.decorator';
+import { IsAWSRegion } from 'src/engine/core-modules/environment/decorators/is-aws-region.decorator';
+import { IsDuration } from 'src/engine/core-modules/environment/decorators/is-duration.decorator';
import { IsStrictlyLowerThan } from 'src/engine/core-modules/environment/decorators/is-strictly-lower-than.decorator';
import { ExceptionHandlerDriver } from 'src/engine/core-modules/exception-handler/interfaces';
import { StorageDriverType } from 'src/engine/core-modules/file-storage/interfaces';
@@ -88,6 +88,15 @@ export class EnvironmentVariables {
@IsBoolean()
TELEMETRY_ENABLED = true;
+ @CastToBoolean()
+ @IsOptional()
+ @IsBoolean()
+ ANALYTICS_ENABLED = false;
+
+ @IsString()
+ @ValidateIf((env) => env.ANALYTICS_ENABLED)
+ TINYBIRD_TOKEN: string;
+
@CastToPositiveNumber()
@IsNumber()
@IsOptional()
diff --git a/packages/twenty-server/src/engine/core-modules/telemetry/telemetry.module.ts b/packages/twenty-server/src/engine/core-modules/telemetry/telemetry.module.ts
new file mode 100644
index 0000000000..b9be5ec208
--- /dev/null
+++ b/packages/twenty-server/src/engine/core-modules/telemetry/telemetry.module.ts
@@ -0,0 +1,15 @@
+import { HttpModule } from '@nestjs/axios';
+import { Module } from '@nestjs/common';
+
+import { TelemetryService } from './telemetry.service';
+
+@Module({
+ providers: [TelemetryService],
+ imports: [
+ HttpModule.register({
+ baseURL: 'https://t.twenty.com/api/v2',
+ }),
+ ],
+ exports: [TelemetryService],
+})
+export class TelemetryModule {}
diff --git a/packages/twenty-server/src/engine/core-modules/telemetry/telemetry.service.ts b/packages/twenty-server/src/engine/core-modules/telemetry/telemetry.service.ts
new file mode 100644
index 0000000000..6f59f98478
--- /dev/null
+++ b/packages/twenty-server/src/engine/core-modules/telemetry/telemetry.service.ts
@@ -0,0 +1,55 @@
+import { HttpService } from '@nestjs/axios';
+import { Injectable, Logger } from '@nestjs/common';
+
+import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
+
+type CreateEventInput = {
+ action: string;
+ payload: object;
+};
+
+@Injectable()
+export class TelemetryService {
+ private readonly logger = new Logger(TelemetryService.name);
+
+ constructor(
+ private readonly environmentService: EnvironmentService,
+ private readonly httpService: HttpService,
+ ) {}
+
+ async create(
+ createEventInput: CreateEventInput,
+ userId: string | null | undefined,
+ workspaceId: string | null | undefined,
+ ) {
+ if (!this.environmentService.get('TELEMETRY_ENABLED')) {
+ return { success: true };
+ }
+
+ const data = {
+ action: createEventInput.action,
+ timestamp: new Date().toISOString(),
+ version: '1',
+ payload: {
+ userId: userId,
+ workspaceId: workspaceId,
+ ...createEventInput.payload,
+ },
+ };
+
+ try {
+ await this.httpService.axiosRef.post(`/selfHostingEvent`, data);
+ } catch (error) {
+ this.logger.error('Error occurred:', error);
+ if (error.response) {
+ this.logger.error(
+ `Error response body: ${JSON.stringify(error.response.data)}`,
+ );
+ }
+
+ return { success: false };
+ }
+
+ return { success: true };
+ }
+}
diff --git a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts
index 31fdd6379a..4f26a8b0e0 100644
--- a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts
+++ b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts
@@ -5,19 +5,19 @@ import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
import { Repository } from 'typeorm';
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
-import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
-import { User } from 'src/engine/core-modules/user/user.entity';
-import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
-import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event';
-import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
-import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
-import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
-import { assert } from 'src/utils/assert';
import {
AppToken,
AppTokenType,
} from 'src/engine/core-modules/app-token/app-token.entity';
+import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event';
+import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
+import { User } from 'src/engine/core-modules/user/user.entity';
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
+import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
+import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
+import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
+import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
+import { assert } from 'src/utils/assert';
export class UserWorkspaceService extends TypeOrmQueryService {
constructor(
diff --git a/packages/twenty-server/src/modules/messaging/monitoring/services/messaging-telemetry.service.ts b/packages/twenty-server/src/modules/messaging/monitoring/services/messaging-telemetry.service.ts
index efe5ae6b44..d9455ed17b 100644
--- a/packages/twenty-server/src/modules/messaging/monitoring/services/messaging-telemetry.service.ts
+++ b/packages/twenty-server/src/modules/messaging/monitoring/services/messaging-telemetry.service.ts
@@ -29,8 +29,8 @@ export class MessagingTelemetryService {
}: MessagingTelemetryTrackInput): Promise {
await this.analyticsService.create(
{
- type: 'track',
- data: {
+ action: 'monitoring',
+ payload: {
eventName: `messaging.${eventName}`,
workspaceId,
userId,
@@ -41,9 +41,6 @@ export class MessagingTelemetryService {
},
userId,
workspaceId,
- '', // voluntarely not retrieving this
- '', // to avoid slowing down
- this.environmentService.get('SERVER_URL'),
);
}
}