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 bf083a157..05c99cc89 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 ce122e418..09fac64ea 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 15c8a2879..02d20ed6f 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 88d1d6567..faaa3ea5b 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 59f99306a..5e19a8309 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 d0ba37512..9136b83fc 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 ac52204f4..60e4025a8 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 677932161..7a7de0807 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 8bec4cc7d..9eccbeb98 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 3143bbc5f..e702acefa 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 f074ad218..000000000
--- 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 656dcbb80..1ed65869a 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 2afa537a6..f627caf47 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 5d1771aad..1e166806b 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 7ce66bafd..2b4c8705d 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 06cd9aabe..f64d108fc 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 085f44378..3e8ca56d2 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 a870673fe..5e887dca0 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 eefb2509b..12cf5c316 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 54844671b..3615066a4 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 a78b636e2..2e2df06c4 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 c3ecf518a..4e30e84bd 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 000000000..b9be5ec20
--- /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 000000000..6f59f9847
--- /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 31fdd6379..4f26a8b0e 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 efe5ae6b4..d9455ed17 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'),
);
}
}